From ae14fa2feafd64cc94fa792cdcc83d109fb44294 Mon Sep 17 00:00:00 2001 From: matheus23 Date: Mon, 15 Dec 2025 12:53:31 +0000 Subject: [PATCH] deploy: 81ae3852168ed5756ca2aaf140807618749ba9f8 --- pr/234/docs/.lock | 0 pr/234/docs/bench/all.html | 1 + pr/234/docs/bench/enum.CipherSuite.html | 28 + .../fn.configure_tracing_subscriber.html | 1 + pr/234/docs/bench/fn.connect_client.html | 6 + pr/234/docs/bench/fn.drain_stream.html | 4 + pr/234/docs/bench/fn.rt.html | 1 + pr/234/docs/bench/fn.send_data_on_stream.html | 4 + pr/234/docs/bench/fn.server_endpoint.html | 7 + pr/234/docs/bench/fn.transport_config.html | 1 + pr/234/docs/bench/index.html | 1 + pr/234/docs/bench/sidebar-items.js | 1 + .../docs/bench/stats/fn.throughput_bps.html | 1 + pr/234/docs/bench/stats/index.html | 1 + pr/234/docs/bench/stats/sidebar-items.js | 1 + pr/234/docs/bench/stats/struct.Stats.html | 22 + .../docs/bench/stats/struct.StreamStats.html | 20 + .../bench/stats/struct.TransferResult.html | 21 + pr/234/docs/bench/struct.Opt.html | 61 + pr/234/docs/bulk/all.html | 1 + pr/234/docs/bulk/fn.client.html | 5 + pr/234/docs/bulk/fn.handle_client_stream.html | 5 + pr/234/docs/bulk/fn.main.html | 1 + pr/234/docs/bulk/fn.server.html | 1 + pr/234/docs/bulk/index.html | 1 + pr/234/docs/bulk/sidebar-items.js | 1 + pr/234/docs/bulk/struct.ClientStats.html | 20 + pr/234/docs/certificate/all.html | 1 + .../docs/certificate/fn.configure_client.html | 1 + .../fn.generate_self_signed_cert.html | 1 + pr/234/docs/certificate/fn.main.html | 1 + .../certificate/fn.read_certs_from_file.html | 1 + pr/234/docs/certificate/index.html | 1 + pr/234/docs/certificate/sidebar-items.js | 1 + .../struct.SkipServerVerification.html | 37 + pr/234/docs/crates.js | 2 + pr/234/docs/data_transfer/all.html | 1 + pr/234/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/234/docs/data_transfer/index.html | 1 + pr/234/docs/data_transfer/sidebar-items.js | 1 + pr/234/docs/help.html | 1 + pr/234/docs/iroh_quinn/all.html | 1 + pr/234/docs/iroh_quinn/congestion/index.html | 2 + .../iroh_quinn/congestion/sidebar-items.js | 1 + .../iroh_quinn/congestion/struct.Bbr.html | 45 + .../congestion/struct.BbrConfig.html | 26 + .../congestion/struct.ControllerMetrics.html | 25 + .../iroh_quinn/congestion/struct.Cubic.html | 41 + .../congestion/struct.CubicConfig.html | 26 + .../iroh_quinn/congestion/struct.NewReno.html | 45 + .../congestion/struct.NewRenoConfig.html | 27 + .../congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/enum.SendDatagramError.html | 11 + .../connection/struct.AcceptBi.html | 11 + .../connection/struct.AcceptUni.html | 11 + .../connection/struct.Connecting.html | 11 + .../connection/struct.Connection.html | 11 + .../connection/struct.OnClosed.html | 11 + .../iroh_quinn/connection/struct.OpenBi.html | 11 + .../iroh_quinn/connection/struct.OpenUni.html | 11 + .../connection/struct.ReadDatagram.html | 11 + .../connection/struct.SendDatagram.html | 11 + .../struct.WeakConnectionHandle.html | 11 + .../connection/struct.ZeroRttAccepted.html | 11 + pr/234/docs/iroh_quinn/crypto/index.html | 8 + .../iroh_quinn/crypto/rustls/enum.Error.html | 94 + .../docs/iroh_quinn/crypto/rustls/index.html | 2 + .../iroh_quinn/crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 27 + .../rustls/struct.NoInitialCipherSuite.html | 24 + .../rustls/struct.QuicClientConfig.html | 45 + .../rustls/struct.QuicServerConfig.html | 49 + .../crypto/rustls/struct.TlsSession.html | 31 + .../docs/iroh_quinn/crypto/sidebar-items.js | 1 + .../iroh_quinn/crypto/struct.CryptoError.html | 18 + .../struct.ExportKeyingMaterialError.html | 24 + .../iroh_quinn/crypto/struct.KeyPair.html | 29 + .../docs/iroh_quinn/crypto/struct.Keys.html | 23 + .../crypto/struct.UnsupportedVersion.html | 18 + .../docs/iroh_quinn/crypto/trait.AeadKey.html | 32 + .../iroh_quinn/crypto/trait.ClientConfig.html | 16 + .../crypto/trait.HandshakeTokenKey.html | 6 + .../iroh_quinn/crypto/trait.HeaderKey.html | 10 + .../docs/iroh_quinn/crypto/trait.HmacKey.html | 10 + .../iroh_quinn/crypto/trait.PacketKey.html | 51 + .../iroh_quinn/crypto/trait.ServerConfig.html | 38 + .../docs/iroh_quinn/crypto/trait.Session.html | 68 + .../iroh_quinn/endpoint/struct.Accept.html | 11 + .../iroh_quinn/endpoint/struct.Endpoint.html | 11 + .../endpoint/struct.EndpointStats.html | 11 + pr/234/docs/iroh_quinn/enum.ConfigError.html | 29 + pr/234/docs/iroh_quinn/enum.ConnectError.html | 44 + .../docs/iroh_quinn/enum.ConnectionError.html | 47 + pr/234/docs/iroh_quinn/enum.Dir.html | 47 + pr/234/docs/iroh_quinn/enum.EcnCodepoint.html | 34 + pr/234/docs/iroh_quinn/enum.ReadError.html | 42 + .../docs/iroh_quinn/enum.ReadExactError.html | 31 + .../docs/iroh_quinn/enum.ReadToEndError.html | 31 + pr/234/docs/iroh_quinn/enum.ResetError.html | 33 + .../iroh_quinn/enum.SendDatagramError.html | 37 + pr/234/docs/iroh_quinn/enum.Side.html | 48 + pr/234/docs/iroh_quinn/enum.StoppedError.html | 33 + pr/234/docs/iroh_quinn/enum.WriteError.html | 38 + .../docs/iroh_quinn/fn.default_runtime.html | 5 + .../iroh_quinn/incoming/struct.Incoming.html | 11 + .../incoming/struct.IncomingFuture.html | 11 + .../incoming/struct.RetryError.html | 11 + pr/234/docs/iroh_quinn/index.html | 34 + .../docs/iroh_quinn/path/struct.OpenPath.html | 11 + pr/234/docs/iroh_quinn/path/struct.Path.html | 11 + .../recv_stream/enum.ReadError.html | 11 + .../recv_stream/enum.ReadExactError.html | 11 + .../recv_stream/enum.ReadToEndError.html | 11 + .../recv_stream/enum.ResetError.html | 11 + .../recv_stream/struct.RecvStream.html | 11 + .../runtime/fn.default_runtime.html | 11 + .../runtime/smol/struct.SmolRuntime.html | 11 + .../runtime/tokio/struct.TokioRuntime.html | 11 + .../iroh_quinn/runtime/trait.AsyncTimer.html | 11 + .../runtime/trait.AsyncUdpSocket.html | 11 + .../iroh_quinn/runtime/trait.Runtime.html | 11 + .../iroh_quinn/runtime/trait.UdpSender.html | 11 + .../send_stream/enum.StoppedError.html | 11 + .../send_stream/enum.WriteError.html | 11 + .../send_stream/struct.SendStream.html | 11 + pr/234/docs/iroh_quinn/sidebar-items.js | 1 + pr/234/docs/iroh_quinn/struct.Accept.html | 43 + pr/234/docs/iroh_quinn/struct.AcceptBi.html | 47 + pr/234/docs/iroh_quinn/struct.AcceptUni.html | 47 + .../iroh_quinn/struct.AckFrequencyConfig.html | 58 + .../iroh_quinn/struct.ApplicationClose.html | 31 + .../docs/iroh_quinn/struct.BloomTokenLog.html | 41 + pr/234/docs/iroh_quinn/struct.Chunk.html | 28 + .../docs/iroh_quinn/struct.ClientConfig.html | 43 + .../docs/iroh_quinn/struct.ClosedStream.html | 26 + pr/234/docs/iroh_quinn/struct.Connecting.html | 94 + pr/234/docs/iroh_quinn/struct.Connection.html | 209 + .../iroh_quinn/struct.ConnectionClose.html | 33 + .../docs/iroh_quinn/struct.ConnectionId.html | 2462 ++++++ .../iroh_quinn/struct.ConnectionStats.html | 29 + pr/234/docs/iroh_quinn/struct.Endpoint.html | 96 + .../iroh_quinn/struct.EndpointConfig.html | 60 + .../docs/iroh_quinn/struct.EndpointStats.html | 29 + pr/234/docs/iroh_quinn/struct.FrameStats.html | 58 + pr/234/docs/iroh_quinn/struct.FrameType.html | 28 + .../docs/iroh_quinn/struct.IdleTimeout.html | 45 + pr/234/docs/iroh_quinn/struct.Incoming.html | 39 + .../iroh_quinn/struct.IncomingFuture.html | 46 + .../iroh_quinn/struct.MtuDiscoveryConfig.html | 81 + .../docs/iroh_quinn/struct.NoneTokenLog.html | 24 + .../iroh_quinn/struct.NoneTokenStore.html | 18 + pr/234/docs/iroh_quinn/struct.OnClosed.html | 43 + pr/234/docs/iroh_quinn/struct.OpenBi.html | 47 + pr/234/docs/iroh_quinn/struct.OpenPath.html | 50 + pr/234/docs/iroh_quinn/struct.OpenUni.html | 47 + pr/234/docs/iroh_quinn/struct.Path.html | 40 + pr/234/docs/iroh_quinn/struct.PathId.html | 46 + pr/234/docs/iroh_quinn/struct.PathStats.html | 47 + pr/234/docs/iroh_quinn/struct.QlogConfig.html | 27 + .../iroh_quinn/struct.QlogFileFactory.html | 32 + .../docs/iroh_quinn/struct.ReadDatagram.html | 47 + pr/234/docs/iroh_quinn/struct.RecvStream.html | 205 + pr/234/docs/iroh_quinn/struct.RetryError.html | 20 + .../docs/iroh_quinn/struct.SendDatagram.html | 47 + pr/234/docs/iroh_quinn/struct.SendStream.html | 215 + .../docs/iroh_quinn/struct.ServerConfig.html | 107 + .../docs/iroh_quinn/struct.SmolRuntime.html | 18 + .../docs/iroh_quinn/struct.StdSystemTime.html | 19 + pr/234/docs/iroh_quinn/struct.StreamId.html | 48 + .../iroh_quinn/struct.TokenMemoryCache.html | 24 + .../iroh_quinn/struct.TokenReuseError.html | 18 + .../docs/iroh_quinn/struct.TokioRuntime.html | 18 + pr/234/docs/iroh_quinn/struct.Transmit.html | 30 + .../iroh_quinn/struct.TransportConfig.html | 245 + .../iroh_quinn/struct.TransportErrorCode.html | 50 + pr/234/docs/iroh_quinn/struct.UdpStats.html | 29 + .../struct.ValidationTokenConfig.html | 67 + pr/234/docs/iroh_quinn/struct.VarInt.html | 58 + .../struct.VarIntBoundsExceeded.html | 26 + .../struct.WeakConnectionHandle.html | 24 + pr/234/docs/iroh_quinn/struct.Written.html | 31 + .../iroh_quinn/struct.ZeroRttAccepted.html | 44 + pr/234/docs/iroh_quinn/trait.AsyncTimer.html | 11 + .../docs/iroh_quinn/trait.AsyncUdpSocket.html | 38 + .../trait.ConnectionIdGenerator.html | 21 + pr/234/docs/iroh_quinn/trait.QlogFactory.html | 23 + pr/234/docs/iroh_quinn/trait.Runtime.html | 19 + pr/234/docs/iroh_quinn/trait.TimeSource.html | 7 + pr/234/docs/iroh_quinn/trait.TokenLog.html | 45 + pr/234/docs/iroh_quinn/trait.TokenStore.html | 13 + pr/234/docs/iroh_quinn/trait.UdpSender.html | 30 + pr/234/docs/iroh_quinn_proto/all.html | 1 + .../bloom_token_log/struct.BloomTokenLog.html | 11 + .../struct.HashedConnectionIdGenerator.html | 11 + .../cid_generator/struct.InvalidCid.html | 11 + .../struct.RandomConnectionIdGenerator.html | 11 + .../trait.ConnectionIdGenerator.html | 11 + .../docs/iroh_quinn_proto/coding/index.html | 2 + .../iroh_quinn_proto/coding/sidebar-items.js | 1 + .../coding/struct.UnexpectedEnd.html | 26 + .../iroh_quinn_proto/coding/trait.Codec.html | 8 + .../iroh_quinn_proto/coding/type.Result.html | 7 + .../config/enum.ConfigError.html | 11 + .../config/qlog/struct.QlogConfig.html | 11 + .../config/qlog/struct.QlogFileFactory.html | 11 + .../config/qlog/trait.QlogFactory.html | 11 + .../config/struct.ClientConfig.html | 11 + .../config/struct.EndpointConfig.html | 11 + .../config/struct.ServerConfig.html | 11 + .../config/struct.StdSystemTime.html | 11 + .../config/struct.ValidationTokenConfig.html | 11 + .../config/trait.TimeSource.html | 11 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 11 + .../transport/struct.AckFrequencyConfig.html | 11 + .../config/transport/struct.IdleTimeout.html | 11 + .../transport/struct.MtuDiscoveryConfig.html | 11 + .../transport/struct.TransportConfig.html | 11 + .../congestion/bbr/struct.Bbr.html | 11 + .../congestion/bbr/struct.BbrConfig.html | 11 + .../congestion/cubic/struct.Cubic.html | 11 + .../congestion/cubic/struct.CubicConfig.html | 11 + .../iroh_quinn_proto/congestion/index.html | 2 + .../congestion/new_reno/struct.NewReno.html | 11 + .../new_reno/struct.NewRenoConfig.html | 11 + .../congestion/sidebar-items.js | 1 + .../congestion/struct.Bbr.html | 45 + .../congestion/struct.BbrConfig.html | 26 + .../congestion/struct.ControllerMetrics.html | 25 + .../congestion/struct.Cubic.html | 41 + .../congestion/struct.CubicConfig.html | 22 + .../congestion/struct.NewReno.html | 41 + .../congestion/struct.NewRenoConfig.html | 23 + .../congestion/trait.Controller.html | 79 + .../congestion/trait.ControllerFactory.html | 10 + .../connection/assembler/struct.Chunk.html | 11 + .../datagrams/enum.SendDatagramError.html | 11 + .../datagrams/struct.Datagrams.html | 11 + .../connection/enum.ClosePathError.html | 11 + .../connection/enum.ConnectionError.html | 11 + .../connection/enum.Event.html | 11 + .../connection/enum.PathError.html | 11 + .../connection/paths/enum.PathEvent.html | 11 + .../connection/paths/enum.PathStatus.html | 11 + .../paths/enum.SetPathStatusError.html | 11 + .../connection/paths/struct.ClosedPath.html | 11 + .../connection/paths/struct.PathId.html | 11 + .../connection/paths/struct.RttEstimator.html | 11 + .../stats/struct.ConnectionStats.html | 11 + .../connection/stats/struct.FrameStats.html | 11 + .../connection/stats/struct.PathStats.html | 11 + .../connection/stats/struct.UdpStats.html | 11 + .../connection/streams/enum.StreamEvent.html | 11 + .../streams/recv/enum.ReadError.html | 11 + .../streams/recv/enum.ReadableError.html | 11 + .../streams/recv/struct.Chunks.html | 11 + .../streams/send/enum.FinishError.html | 11 + .../streams/send/enum.WriteError.html | 11 + .../streams/send/struct.Written.html | 11 + .../streams/struct.ClosedStream.html | 11 + .../connection/streams/struct.RecvStream.html | 11 + .../connection/streams/struct.SendStream.html | 11 + .../streams/struct.ShouldTransmit.html | 11 + .../connection/streams/struct.Streams.html | 11 + .../connection/struct.Connection.html | 11 + ...NCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html | 3 + .../constant.DEFAULT_SUPPORTED_VERSIONS.html | 2 + .../docs/iroh_quinn_proto/crypto/index.html | 8 + .../crypto/rustls/enum.Error.html | 94 + .../iroh_quinn_proto/crypto/rustls/index.html | 2 + .../crypto/rustls/sidebar-items.js | 1 + .../crypto/rustls/struct.HandshakeData.html | 27 + .../rustls/struct.NoInitialCipherSuite.html | 24 + .../rustls/struct.QuicClientConfig.html | 41 + .../rustls/struct.QuicServerConfig.html | 45 + .../crypto/rustls/struct.TlsSession.html | 33 + .../iroh_quinn_proto/crypto/sidebar-items.js | 1 + .../crypto/struct.CryptoError.html | 18 + .../struct.ExportKeyingMaterialError.html | 24 + .../crypto/struct.KeyPair.html | 29 + .../iroh_quinn_proto/crypto/struct.Keys.html | 23 + .../crypto/struct.UnsupportedVersion.html | 18 + .../crypto/trait.AeadKey.html | 32 + .../crypto/trait.ClientConfig.html | 16 + .../crypto/trait.HandshakeTokenKey.html | 6 + .../crypto/trait.HeaderKey.html | 10 + .../crypto/trait.HmacKey.html | 10 + .../crypto/trait.PacketKey.html | 51 + .../crypto/trait.ServerConfig.html | 38 + .../crypto/trait.Session.html | 72 + .../endpoint/enum.ConnectError.html | 11 + .../endpoint/enum.DatagramEvent.html | 11 + .../endpoint/struct.AcceptError.html | 11 + .../endpoint/struct.ConnectionHandle.html | 11 + .../endpoint/struct.Endpoint.html | 11 + .../endpoint/struct.Incoming.html | 11 + .../endpoint/struct.RetryError.html | 11 + .../iroh_quinn_proto/enum.ClosePathError.html | 31 + .../iroh_quinn_proto/enum.ConfigError.html | 29 + .../iroh_quinn_proto/enum.ConnectError.html | 44 + .../enum.ConnectionError.html | 47 + .../iroh_quinn_proto/enum.DatagramEvent.html | 25 + pr/234/docs/iroh_quinn_proto/enum.Dir.html | 46 + .../iroh_quinn_proto/enum.EcnCodepoint.html | 34 + pr/234/docs/iroh_quinn_proto/enum.Event.html | 41 + .../iroh_quinn_proto/enum.FinishError.html | 33 + .../docs/iroh_quinn_proto/enum.LongType.html | 30 + .../enum.PacketDecodeError.html | 48 + .../docs/iroh_quinn_proto/enum.PathError.html | 39 + .../docs/iroh_quinn_proto/enum.PathEvent.html | 73 + .../iroh_quinn_proto/enum.PathStatus.html | 36 + .../enum.ProtectedHeader.html | 68 + .../docs/iroh_quinn_proto/enum.ReadError.html | 44 + .../iroh_quinn_proto/enum.ReadableError.html | 43 + .../enum.SendDatagramError.html | 47 + .../enum.SetPathStatusError.html | 31 + pr/234/docs/iroh_quinn_proto/enum.Side.html | 47 + .../iroh_quinn_proto/enum.StreamEvent.html | 57 + .../iroh_quinn_proto/enum.WriteError.html | 47 + .../frame/struct.ApplicationClose.html | 11 + .../frame/struct.ConnectionClose.html | 11 + .../frame/struct.Datagram.html | 11 + .../frame/struct.FrameType.html | 11 + pr/234/docs/iroh_quinn_proto/index.html | 14 + .../iroh_quinn_proto/iroh_hp/enum.Error.html | 30 + .../iroh_quinn_proto/iroh_hp/enum.Event.html | 25 + .../docs/iroh_quinn_proto/iroh_hp/index.html | 2 + .../iroh_quinn_proto/iroh_hp/sidebar-items.js | 1 + .../packet/enum.LongType.html | 11 + .../packet/enum.PacketDecodeError.html | 11 + .../packet/enum.ProtectedHeader.html | 11 + .../struct.FixedLengthConnectionIdParser.html | 11 + .../packet/struct.PartialDecode.html | 11 + .../packet/struct.ProtectedInitialHeader.html | 11 + .../packet/trait.ConnectionIdParser.html | 11 + .../shared/enum.EcnCodepoint.html | 11 + .../shared/struct.ConnectionEvent.html | 11 + .../shared/struct.ConnectionId.html | 11 + .../shared/struct.EndpointEvent.html | 11 + pr/234/docs/iroh_quinn_proto/sidebar-items.js | 1 + .../iroh_quinn_proto/struct.AcceptError.html | 23 + .../struct.AckFrequencyConfig.html | 52 + .../struct.ApplicationClose.html | 31 + .../struct.BloomTokenLog.html | 41 + .../docs/iroh_quinn_proto/struct.Chunk.html | 28 + .../docs/iroh_quinn_proto/struct.Chunks.html | 34 + .../iroh_quinn_proto/struct.ClientConfig.html | 40 + .../iroh_quinn_proto/struct.ClosedPath.html | 26 + .../iroh_quinn_proto/struct.ClosedStream.html | 26 + .../iroh_quinn_proto/struct.Connection.html | 244 + .../struct.ConnectionClose.html | 33 + .../struct.ConnectionEvent.html | 18 + .../struct.ConnectionHandle.html | 35 + .../iroh_quinn_proto/struct.ConnectionId.html | 2461 ++++++ .../struct.ConnectionStats.html | 29 + .../iroh_quinn_proto/struct.Datagram.html | 23 + .../iroh_quinn_proto/struct.Datagrams.html | 37 + .../iroh_quinn_proto/struct.Endpoint.html | 77 + .../struct.EndpointConfig.html | 59 + .../struct.EndpointEvent.html | 23 + .../struct.FixedLengthConnectionIdParser.html | 19 + .../iroh_quinn_proto/struct.FrameStats.html | 58 + .../iroh_quinn_proto/struct.FrameType.html | 26 + .../struct.HashedConnectionIdGenerator.html | 26 + .../iroh_quinn_proto/struct.IdleTimeout.html | 42 + .../iroh_quinn_proto/struct.Incoming.html | 30 + .../iroh_quinn_proto/struct.InvalidCid.html | 20 + .../struct.MtuDiscoveryConfig.html | 78 + .../iroh_quinn_proto/struct.NoneTokenLog.html | 24 + .../struct.NoneTokenStore.html | 18 + .../struct.PartialDecode.html | 35 + .../docs/iroh_quinn_proto/struct.PathId.html | 42 + .../iroh_quinn_proto/struct.PathStats.html | 47 + .../struct.ProtectedInitialHeader.html | 31 + .../iroh_quinn_proto/struct.QlogConfig.html | 27 + .../struct.QlogFileFactory.html | 32 + .../struct.RandomConnectionIdGenerator.html | 26 + .../iroh_quinn_proto/struct.RecvStream.html | 37 + .../iroh_quinn_proto/struct.RetryError.html | 20 + .../iroh_quinn_proto/struct.RttEstimator.html | 25 + .../iroh_quinn_proto/struct.SendStream.html | 46 + .../iroh_quinn_proto/struct.ServerConfig.html | 95 + .../struct.ShouldTransmit.html | 28 + .../struct.StdSystemTime.html | 19 + .../iroh_quinn_proto/struct.StreamId.html | 45 + .../docs/iroh_quinn_proto/struct.Streams.html | 28 + .../struct.TokenMemoryCache.html | 21 + .../struct.TokenReuseError.html | 18 + .../iroh_quinn_proto/struct.Transmit.html | 30 + .../struct.TransportConfig.html | 215 + .../struct.TransportError.html | 38 + .../struct.TransportErrorCode.html | 48 + .../iroh_quinn_proto/struct.UdpStats.html | 29 + .../struct.ValidationTokenConfig.html | 67 + .../docs/iroh_quinn_proto/struct.VarInt.html | 55 + .../struct.VarIntBoundsExceeded.html | 26 + .../docs/iroh_quinn_proto/struct.Written.html | 31 + .../token/struct.NoneTokenLog.html | 11 + .../token/struct.NoneTokenStore.html | 11 + .../token/struct.TokenReuseError.html | 11 + .../token/trait.TokenLog.html | 11 + .../token/trait.TokenStore.html | 11 + .../struct.TokenMemoryCache.html | 11 + .../trait.ConnectionIdGenerator.html | 21 + .../trait.ConnectionIdParser.html | 9 + .../iroh_quinn_proto/trait.QlogFactory.html | 23 + .../iroh_quinn_proto/trait.TimeSource.html | 7 + .../docs/iroh_quinn_proto/trait.TokenLog.html | 45 + .../iroh_quinn_proto/trait.TokenStore.html | 13 + .../transport_error/struct.Code.html | 11 + .../transport_error/struct.Error.html | 11 + .../transport_parameters/enum.Error.html | 31 + .../transport_parameters/index.html | 7 + .../transport_parameters/sidebar-items.js | 1 + .../struct.TransportParameters.html | 31 + .../varint/struct.VarInt.html | 11 + .../varint/struct.VarIntBoundsExceeded.html | 11 + pr/234/docs/iroh_quinn_udp/all.html | 1 + .../iroh_quinn_udp/constant.BATCH_SIZE.html | 2 + .../iroh_quinn_udp/enum.EcnCodepoint.html | 28 + .../imp/struct.UdpSocketState.html | 11 + pr/234/docs/iroh_quinn_udp/index.html | 22 + pr/234/docs/iroh_quinn_udp/sidebar-items.js | 1 + .../docs/iroh_quinn_udp/struct.RecvMeta.html | 41 + .../docs/iroh_quinn_udp/struct.Transmit.html | 31 + .../iroh_quinn_udp/struct.UdpSockRef.html | 19 + .../iroh_quinn_udp/struct.UdpSocketState.html | 62 + pr/234/docs/perf/all.html | 1 + pr/234/docs/perf/client/fn.run.html | 1 + pr/234/docs/perf/client/index.html | 1 + pr/234/docs/perf/client/sidebar-items.js | 1 + pr/234/docs/perf/client/struct.Opt.html | 36 + .../docs/perf/enum.CongestionAlgorithm.html | 23 + pr/234/docs/perf/fn.parse_byte_size.html | 1 + pr/234/docs/perf/index.html | 1 + pr/234/docs/perf/noprotection/index.html | 1 + .../docs/perf/noprotection/sidebar-items.js | 1 + .../struct.NoProtectionClientConfig.html | 22 + .../struct.NoProtectionServerConfig.html | 30 + pr/234/docs/perf/server/fn.run.html | 1 + pr/234/docs/perf/server/index.html | 1 + pr/234/docs/perf/server/sidebar-items.js | 1 + pr/234/docs/perf/server/struct.Opt.html | 35 + pr/234/docs/perf/sidebar-items.js | 1 + .../docs/perf/static.PERF_CIPHER_SUITES.html | 1 + pr/234/docs/perf/stats/index.html | 1 + pr/234/docs/perf/stats/sidebar-items.js | 1 + .../perf/stats/struct.OpenStreamStats.html | 28 + pr/234/docs/perf/stats/struct.Stats.html | 17 + .../docs/perf/stats/struct.StreamStats.html | 17 + pr/234/docs/perf/struct.CommonOpt.html | 77 + pr/234/docs/quinn_perf/all.html | 1 + pr/234/docs/quinn_perf/enum.Commands.html | 33 + pr/234/docs/quinn_perf/fn.main.html | 1 + pr/234/docs/quinn_perf/index.html | 1 + pr/234/docs/quinn_perf/sidebar-items.js | 1 + pr/234/docs/quinn_perf/struct.Cli.html | 38 + pr/234/docs/search.index/003a3a433ad6.js | 1 + pr/234/docs/search.index/005aa45d6078.js | 1 + pr/234/docs/search.index/00663acd356b.js | 1 + pr/234/docs/search.index/006b398a6f2a.js | 1 + pr/234/docs/search.index/00b1c72d15ee.js | 1 + pr/234/docs/search.index/019bd8f229e4.js | 1 + pr/234/docs/search.index/01bda78c46d2.js | 1 + pr/234/docs/search.index/01d8a434418f.js | 1 + pr/234/docs/search.index/02e652a4afc3.js | 1 + pr/234/docs/search.index/0369b203eb42.js | 1 + pr/234/docs/search.index/03a2caa15777.js | 1 + pr/234/docs/search.index/059f564e1f6b.js | 1 + pr/234/docs/search.index/05ae80dc1c58.js | 1 + pr/234/docs/search.index/05df7f0f67ba.js | 1 + pr/234/docs/search.index/0731b0da5a08.js | 1 + pr/234/docs/search.index/081bb183a3a6.js | 1 + pr/234/docs/search.index/083bd29ff9ff.js | 1 + pr/234/docs/search.index/0850db5b7e99.js | 1 + pr/234/docs/search.index/0852167fd512.js | 1 + pr/234/docs/search.index/0878d7ee02a1.js | 1 + pr/234/docs/search.index/0977b4e74445.js | 1 + pr/234/docs/search.index/0a2079f1a35f.js | 1 + pr/234/docs/search.index/0b5dc05327dc.js | 1 + pr/234/docs/search.index/0ba626d32cbc.js | 1 + pr/234/docs/search.index/0baf5c0b970e.js | 1 + pr/234/docs/search.index/0c04c5c572cf.js | 1 + pr/234/docs/search.index/0c918467df3c.js | 1 + pr/234/docs/search.index/0caf669e19ba.js | 1 + pr/234/docs/search.index/0d537b6a73b7.js | 1 + pr/234/docs/search.index/0e480fb4ebc7.js | 1 + pr/234/docs/search.index/0f7753ba0d50.js | 1 + pr/234/docs/search.index/10355c6b286e.js | 1 + pr/234/docs/search.index/1180cfa8fde3.js | 1 + pr/234/docs/search.index/11b8f016baed.js | 1 + pr/234/docs/search.index/12171281f9bb.js | 1 + pr/234/docs/search.index/12df68ee6610.js | 1 + pr/234/docs/search.index/132765159445.js | 1 + pr/234/docs/search.index/132fa3273a2a.js | 1 + pr/234/docs/search.index/1404ee0d6cc3.js | 1 + pr/234/docs/search.index/149132598270.js | 1 + pr/234/docs/search.index/149b3bfa7d2f.js | 1 + pr/234/docs/search.index/14af5b4de5e7.js | 1 + pr/234/docs/search.index/150ff4191cab.js | 1 + pr/234/docs/search.index/156e94e5de34.js | 1 + pr/234/docs/search.index/161ff8529816.js | 1 + pr/234/docs/search.index/16c553f3af58.js | 1 + pr/234/docs/search.index/16c72901534d.js | 1 + pr/234/docs/search.index/170f27c7a8bd.js | 1 + pr/234/docs/search.index/18822e341642.js | 1 + pr/234/docs/search.index/1882ea84a648.js | 1 + pr/234/docs/search.index/1a37a0ff7662.js | 1 + pr/234/docs/search.index/1a46991231d2.js | 1 + pr/234/docs/search.index/1a8751cccd15.js | 1 + pr/234/docs/search.index/1acf55718c6c.js | 1 + pr/234/docs/search.index/1c0669362394.js | 1 + pr/234/docs/search.index/1c7b86fac1f5.js | 1 + pr/234/docs/search.index/1d9d469499b8.js | 1 + pr/234/docs/search.index/1e65bf627061.js | 1 + pr/234/docs/search.index/201202f6eb90.js | 1 + pr/234/docs/search.index/20292df8ad95.js | 1 + pr/234/docs/search.index/20f24dd90959.js | 1 + pr/234/docs/search.index/2196a484188a.js | 1 + pr/234/docs/search.index/22338a595bbb.js | 1 + pr/234/docs/search.index/2334fd1256bd.js | 1 + pr/234/docs/search.index/243791893159.js | 1 + pr/234/docs/search.index/247e576b2cc4.js | 1 + pr/234/docs/search.index/2574b34a8660.js | 1 + pr/234/docs/search.index/27441501e1c3.js | 1 + pr/234/docs/search.index/27797ee166ea.js | 1 + pr/234/docs/search.index/27f6cb7cd2c6.js | 1 + pr/234/docs/search.index/2849bc9335e1.js | 1 + pr/234/docs/search.index/285e4fa908fe.js | 1 + pr/234/docs/search.index/295d133206ed.js | 1 + pr/234/docs/search.index/299e6d3a1674.js | 1 + pr/234/docs/search.index/29beed7d2cc8.js | 1 + pr/234/docs/search.index/2a0d8ab46fb3.js | 1 + pr/234/docs/search.index/2a464ed978c7.js | 1 + pr/234/docs/search.index/2a6c1f20350c.js | 1 + pr/234/docs/search.index/2acf01fb4c07.js | 1 + pr/234/docs/search.index/2b385adf4f14.js | 1 + pr/234/docs/search.index/2c4d0eda72da.js | 1 + pr/234/docs/search.index/2da70c21552d.js | 1 + pr/234/docs/search.index/2e6dfbf22b19.js | 1 + pr/234/docs/search.index/309b4ef99850.js | 1 + pr/234/docs/search.index/30a8ca0b3469.js | 1 + pr/234/docs/search.index/319143b9dcad.js | 1 + pr/234/docs/search.index/31ec884fea72.js | 1 + pr/234/docs/search.index/322c33796bd9.js | 1 + pr/234/docs/search.index/341a074166cf.js | 1 + pr/234/docs/search.index/3439f6b04774.js | 1 + pr/234/docs/search.index/37041e27a69e.js | 1 + pr/234/docs/search.index/3766bd3b524f.js | 1 + pr/234/docs/search.index/37fbc343ca2b.js | 1 + pr/234/docs/search.index/38bf81f092c9.js | 1 + pr/234/docs/search.index/39f1ae6853b3.js | 1 + pr/234/docs/search.index/3ad40b841b32.js | 1 + pr/234/docs/search.index/3b9c9d813a13.js | 1 + pr/234/docs/search.index/3c0affdb0268.js | 1 + pr/234/docs/search.index/3c13ca3e4e61.js | 1 + pr/234/docs/search.index/3c4b2624d9b8.js | 1 + pr/234/docs/search.index/3e6b1a75c307.js | 1 + pr/234/docs/search.index/3e8fa26efc53.js | 1 + pr/234/docs/search.index/3ea05a9a63fc.js | 1 + pr/234/docs/search.index/3ecfb741bfaf.js | 1 + pr/234/docs/search.index/40410c9d55a4.js | 1 + pr/234/docs/search.index/41c76bd7e9b0.js | 1 + pr/234/docs/search.index/42740cbde866.js | 1 + pr/234/docs/search.index/435ef6b1a68d.js | 1 + pr/234/docs/search.index/440ad754dc7b.js | 1 + pr/234/docs/search.index/441c00806b58.js | 1 + pr/234/docs/search.index/442973fdf353.js | 1 + pr/234/docs/search.index/44b01bde9d64.js | 1 + pr/234/docs/search.index/44d1c75fe36d.js | 1 + pr/234/docs/search.index/464be0b62530.js | 1 + pr/234/docs/search.index/4683bd92a354.js | 1 + pr/234/docs/search.index/46f1494df7bd.js | 1 + pr/234/docs/search.index/47698cc593eb.js | 1 + pr/234/docs/search.index/47e65f6bbe8e.js | 1 + pr/234/docs/search.index/47f0dfc2bfd8.js | 1 + pr/234/docs/search.index/48120e13d7d1.js | 1 + pr/234/docs/search.index/498bb15a8c92.js | 1 + pr/234/docs/search.index/49c82ba58485.js | 1 + pr/234/docs/search.index/4aa239b6a7ae.js | 1 + pr/234/docs/search.index/4b421ea6441b.js | 1 + pr/234/docs/search.index/4d5fb1407792.js | 1 + pr/234/docs/search.index/4e0bb34e56c4.js | 1 + pr/234/docs/search.index/4ec66add1e98.js | 1 + pr/234/docs/search.index/4fbd4ea92e70.js | 1 + pr/234/docs/search.index/50184a00a93c.js | 1 + pr/234/docs/search.index/50f6f3198519.js | 1 + pr/234/docs/search.index/510fd0e4bb9e.js | 1 + pr/234/docs/search.index/516a9f043d12.js | 1 + pr/234/docs/search.index/51a21faaeab8.js | 1 + pr/234/docs/search.index/51a886ba80e8.js | 1 + pr/234/docs/search.index/54064ee22fd2.js | 1 + pr/234/docs/search.index/54679bb88e4d.js | 1 + pr/234/docs/search.index/54aa7ed4bc9a.js | 1 + pr/234/docs/search.index/5527af3a0275.js | 1 + pr/234/docs/search.index/5583dbff6ecd.js | 1 + pr/234/docs/search.index/558b25ba5cd4.js | 1 + pr/234/docs/search.index/579abe8832f0.js | 1 + pr/234/docs/search.index/57a002b9f9d0.js | 1 + pr/234/docs/search.index/57da63a54625.js | 1 + pr/234/docs/search.index/57ec46f6e8db.js | 1 + pr/234/docs/search.index/58c1c76c1b23.js | 1 + pr/234/docs/search.index/592306864b13.js | 1 + pr/234/docs/search.index/59a125e0550b.js | 1 + pr/234/docs/search.index/5af7c9ebff9c.js | 1 + pr/234/docs/search.index/5da06bd3cb28.js | 1 + pr/234/docs/search.index/5dba02cd6a4e.js | 1 + pr/234/docs/search.index/5e7543ac82c6.js | 1 + pr/234/docs/search.index/5fb178f0c084.js | 1 + pr/234/docs/search.index/6015e8720a21.js | 1 + pr/234/docs/search.index/60416c1e7d76.js | 1 + pr/234/docs/search.index/604a3991077d.js | 1 + pr/234/docs/search.index/6059e72d2543.js | 1 + pr/234/docs/search.index/6192a27f0e53.js | 1 + pr/234/docs/search.index/61f88a01075d.js | 1 + pr/234/docs/search.index/62e97f3b5f23.js | 1 + pr/234/docs/search.index/63d0f2065b1f.js | 1 + pr/234/docs/search.index/6417a7048837.js | 1 + pr/234/docs/search.index/64aa132e56e9.js | 1 + pr/234/docs/search.index/64f30fe8b248.js | 1 + pr/234/docs/search.index/67a6347fe3ae.js | 1 + pr/234/docs/search.index/68314aeade4e.js | 1 + pr/234/docs/search.index/683f4785394c.js | 1 + pr/234/docs/search.index/69af465a5e19.js | 1 + pr/234/docs/search.index/6a4dbb0f44b4.js | 1 + pr/234/docs/search.index/6ae1dbe7be1c.js | 1 + pr/234/docs/search.index/6bf9d14a8094.js | 1 + pr/234/docs/search.index/6dcb10ceafda.js | 1 + pr/234/docs/search.index/6e2902148b8e.js | 1 + pr/234/docs/search.index/6f777c411721.js | 1 + pr/234/docs/search.index/6fa98d7155d9.js | 1 + pr/234/docs/search.index/71761381578a.js | 1 + pr/234/docs/search.index/724bf250813c.js | 1 + pr/234/docs/search.index/739826fcec2f.js | 1 + pr/234/docs/search.index/74051be7e32a.js | 1 + pr/234/docs/search.index/740bdb0f158e.js | 1 + pr/234/docs/search.index/74722d2b5b62.js | 1 + pr/234/docs/search.index/749bbd73b08e.js | 1 + pr/234/docs/search.index/74bdadfc1d31.js | 1 + pr/234/docs/search.index/74fafa2468b8.js | 1 + pr/234/docs/search.index/7561fbf6279c.js | 1 + pr/234/docs/search.index/75aa6f082a69.js | 1 + pr/234/docs/search.index/75bce28ef9fb.js | 1 + pr/234/docs/search.index/7880cad733fe.js | 1 + pr/234/docs/search.index/794cab3d1528.js | 1 + pr/234/docs/search.index/79753bcfbf4f.js | 1 + pr/234/docs/search.index/79c04eb28188.js | 1 + pr/234/docs/search.index/7b2046dab109.js | 1 + pr/234/docs/search.index/7b3aaaa369a3.js | 1 + pr/234/docs/search.index/7bb9fa36e896.js | 1 + pr/234/docs/search.index/7bcd03fe6a97.js | 1 + pr/234/docs/search.index/7c75104910e5.js | 1 + pr/234/docs/search.index/7d2ea3138a91.js | 1 + pr/234/docs/search.index/7d7822a13b6a.js | 1 + pr/234/docs/search.index/7d862dad6f48.js | 1 + pr/234/docs/search.index/7dc3252609e4.js | 1 + pr/234/docs/search.index/7f27b460fa5c.js | 1 + pr/234/docs/search.index/7f728bb3feca.js | 1 + .../docs/search.index/alias/a762150c532c.js | 1 + .../search.index/crateNames/8a381fcf2bd4.js | 1 + pr/234/docs/search.index/desc/f10ce68b8c31.js | 1 + .../docs/search.index/entry/1a5b5718bf93.js | 1 + .../search.index/function/6f4f4cff046f.js | 1 + .../generic_inverted_index/6183c510d241.js | 1 + pr/234/docs/search.index/name/d1009e9c1189.js | 1 + .../normalizedName/87730ecd4586.js | 1 + pr/234/docs/search.index/path/6aab90dd54df.js | 1 + pr/234/docs/search.index/root.js | 1 + pr/234/docs/search.index/type/2aa9cf747fcf.js | 1 + pr/234/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/234/docs/set_up_connection/fn.client.html | 1 + pr/234/docs/set_up_connection/fn.main.html | 1 + pr/234/docs/set_up_connection/fn.server.html | 1 + pr/234/docs/set_up_connection/index.html | 1 + .../docs/set_up_connection/sidebar-items.js | 1 + pr/234/docs/settings.html | 1 + pr/234/docs/src-files.js | 2 + pr/234/docs/src/bench/lib.rs.html | 254 + pr/234/docs/src/bench/stats.rs.html | 95 + pr/234/docs/src/bulk/bulk.rs.html | 219 + .../docs/src/certificate/certificate.rs.html | 106 + .../src/data_transfer/data-transfer.rs.html | 56 + pr/234/docs/src/iroh_quinn/connection.rs.html | 1731 +++++ pr/234/docs/src/iroh_quinn/endpoint.rs.html | 930 +++ pr/234/docs/src/iroh_quinn/incoming.rs.html | 152 + pr/234/docs/src/iroh_quinn/lib.rs.html | 138 + pr/234/docs/src/iroh_quinn/mutex.rs.html | 163 + pr/234/docs/src/iroh_quinn/path.rs.html | 297 + .../docs/src/iroh_quinn/recv_stream.rs.html | 699 ++ .../docs/src/iroh_quinn/runtime/mod.rs.html | 250 + .../docs/src/iroh_quinn/runtime/smol.rs.html | 101 + .../docs/src/iroh_quinn/runtime/tokio.rs.html | 105 + .../docs/src/iroh_quinn/send_stream.rs.html | 443 ++ .../docs/src/iroh_quinn/work_limiter.rs.html | 224 + .../address_discovery.rs.html | 126 + .../iroh_quinn_proto/bloom_token_log.rs.html | 368 + .../iroh_quinn_proto/cid_generator.rs.html | 180 + .../src/iroh_quinn_proto/cid_queue.rs.html | 486 ++ .../docs/src/iroh_quinn_proto/coding.rs.html | 130 + .../src/iroh_quinn_proto/config/mod.rs.html | 703 ++ .../src/iroh_quinn_proto/config/qlog.rs.html | 173 + .../iroh_quinn_proto/config/transport.rs.html | 912 +++ .../src/iroh_quinn_proto/congestion.rs.html | 112 + .../congestion/bbr/bw_estimation.rs.html | 101 + .../congestion/bbr/min_max.rs.html | 152 + .../congestion/bbr/mod.rs.html | 647 ++ .../iroh_quinn_proto/congestion/cubic.rs.html | 299 + .../congestion/new_reno.rs.html | 173 + .../connection/ack_frequency.rs.html | 159 + .../connection/assembler.rs.html | 845 +++ .../connection/cid_state.rs.html | 229 + .../connection/datagrams.rs.html | 214 + .../iroh_quinn_proto/connection/mod.rs.html | 6690 +++++++++++++++++ .../iroh_quinn_proto/connection/mtud.rs.html | 980 +++ .../connection/pacing.rs.html | 325 + .../connection/packet_builder.rs.html | 375 + .../connection/packet_crypto.rs.html | 197 + .../iroh_quinn_proto/connection/paths.rs.html | 889 +++ .../iroh_quinn_proto/connection/qlog.rs.html | 1066 +++ .../connection/send_buffer.rs.html | 394 + .../connection/spaces.rs.html | 1334 ++++ .../iroh_quinn_proto/connection/state.rs.html | 346 + .../iroh_quinn_proto/connection/stats.rs.html | 219 + .../connection/streams/mod.rs.html | 528 ++ .../connection/streams/recv.rs.html | 543 ++ .../connection/streams/send.rs.html | 402 + .../connection/streams/state.rs.html | 2107 ++++++ .../iroh_quinn_proto/connection/timer.rs.html | 484 ++ .../connection/transmit_buf.rs.html | 242 + .../iroh_quinn_proto/constant_time.rs.html | 22 + .../docs/src/iroh_quinn_proto/crypto.rs.html | 221 + .../iroh_quinn_proto/crypto/ring_like.rs.html | 57 + .../iroh_quinn_proto/crypto/rustls.rs.html | 660 ++ .../src/iroh_quinn_proto/endpoint.rs.html | 1399 ++++ .../docs/src/iroh_quinn_proto/frame.rs.html | 2077 +++++ .../docs/src/iroh_quinn_proto/iroh_hp.rs.html | 419 ++ pr/234/docs/src/iroh_quinn_proto/lib.rs.html | 340 + .../docs/src/iroh_quinn_proto/packet.rs.html | 1053 +++ .../range_set/array_range_set.rs.html | 210 + .../iroh_quinn_proto/range_set/mod.rs.html | 5 + .../docs/src/iroh_quinn_proto/shared.rs.html | 198 + .../docs/src/iroh_quinn_proto/token.rs.html | 506 ++ .../token_memory_cache.rs.html | 247 + .../iroh_quinn_proto/transport_error.rs.html | 162 + .../transport_parameters.rs.html | 978 +++ .../docs/src/iroh_quinn_proto/varint.rs.html | 217 + .../docs/src/iroh_quinn_udp/cmsg/mod.rs.html | 157 + .../docs/src/iroh_quinn_udp/cmsg/unix.rs.html | 81 + pr/234/docs/src/iroh_quinn_udp/lib.rs.html | 240 + pr/234/docs/src/iroh_quinn_udp/unix.rs.html | 1095 +++ pr/234/docs/src/perf/client.rs.html | 419 ++ pr/234/docs/src/perf/lib.rs.html | 219 + pr/234/docs/src/perf/noprotection.rs.html | 205 + pr/234/docs/src/perf/server.rs.html | 202 + pr/234/docs/src/perf/stats.rs.html | 413 + pr/234/docs/src/quinn_perf/perf.rs.html | 46 + .../set-up-connection.rs.html | 38 + .../docs/static.files/COPYRIGHT-7fb11f4e.txt | 71 + .../FiraMono-Medium-86f75c8c.woff2 | Bin 0 -> 64572 bytes .../FiraMono-Regular-87c26294.woff2 | Bin 0 -> 64868 bytes .../FiraSans-Italic-81dc35de.woff2 | Bin 0 -> 136300 bytes .../FiraSans-LICENSE-05ab6dbd.txt | 98 + .../FiraSans-Medium-e1aa3f0a.woff2 | Bin 0 -> 132780 bytes .../FiraSans-MediumItalic-ccf7e434.woff2 | Bin 0 -> 140588 bytes .../FiraSans-Regular-0fe48ade.woff2 | Bin 0 -> 129188 bytes .../static.files/LICENSE-APACHE-a60eea81.txt | 201 + .../static.files/LICENSE-MIT-23f18e03.txt | 23 + .../NanumBarunGothic-13b3dcba.ttf.woff2 | Bin 0 -> 399468 bytes .../NanumBarunGothic-LICENSE-a37d393b.txt | 103 + .../SourceCodePro-It-fc8b9304.ttf.woff2 | Bin 0 -> 44896 bytes .../SourceCodePro-LICENSE-67f54ca7.txt | 97 + .../SourceCodePro-Regular-8badfe75.ttf.woff2 | Bin 0 -> 52228 bytes .../SourceCodePro-Semibold-aa29a496.ttf.woff2 | Bin 0 -> 52348 bytes .../SourceSerif4-Bold-6d4fd4c0.ttf.woff2 | Bin 0 -> 81540 bytes .../SourceSerif4-It-ca3b17ed.ttf.woff2 | Bin 0 -> 59716 bytes .../SourceSerif4-LICENSE-a2cfd9d5.md | 98 + .../SourceSerif4-Regular-6b053e98.ttf.woff2 | Bin 0 -> 76260 bytes .../SourceSerif4-Semibold-457a13ac.ttf.woff2 | Bin 0 -> 80732 bytes pr/234/docs/static.files/favicon-044be391.svg | 24 + .../static.files/favicon-32x32-eab170b8.png | Bin 0 -> 690 bytes pr/234/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/234/docs/static.files/rustdoc-ca0dd0c4.css | 86 + .../static.files/scrape-examples-2bbcccac.js | 1 + pr/234/docs/static.files/search-8d3311b9.js | 5 + pr/234/docs/static.files/settings-c38705f0.js | 17 + .../docs/static.files/src-script-813739b1.js | 1 + pr/234/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/234/docs/trait.impl/core/cmp/trait.Eq.js | 9 + pr/234/docs/trait.impl/core/cmp/trait.Ord.js | 9 + .../trait.impl/core/cmp/trait.PartialEq.js | 9 + .../trait.impl/core/cmp/trait.PartialOrd.js | 9 + .../trait.impl/core/convert/trait.From.js | 9 + .../trait.impl/core/convert/trait.TryFrom.js | 9 + .../trait.impl/core/default/trait.Default.js | 9 + .../docs/trait.impl/core/error/trait.Error.js | 9 + .../docs/trait.impl/core/fmt/trait.Debug.js | 9 + .../docs/trait.impl/core/fmt/trait.Display.js | 9 + .../core/future/future/trait.Future.js | 9 + .../future/into_future/trait.IntoFuture.js | 9 + .../docs/trait.impl/core/hash/trait.Hash.js | 9 + .../docs/trait.impl/core/marker/trait.Copy.js | 9 + .../trait.impl/core/marker/trait.Freeze.js | 9 + .../docs/trait.impl/core/marker/trait.Send.js | 9 + .../core/marker/trait.StructuralPartialEq.js | 9 + .../docs/trait.impl/core/marker/trait.Sync.js | 9 + .../trait.impl/core/marker/trait.Unpin.js | 9 + .../core/marker/trait.UnsafeUnpin.js | 9 + .../docs/trait.impl/core/ops/bit/trait.Not.js | 9 + .../trait.impl/core/ops/deref/trait.Deref.js | 9 + .../core/ops/deref/trait.DerefMut.js | 9 + .../trait.impl/core/ops/drop/trait.Drop.js | 9 + .../trait.impl/core/ops/index/trait.Index.js | 9 + .../core/ops/index/trait.IndexMut.js | 9 + .../panic/unwind_safe/trait.RefUnwindSafe.js | 9 + .../panic/unwind_safe/trait.UnwindSafe.js | 9 + .../core/str/traits/trait.FromStr.js | 9 + .../futures_io/if_std/trait.AsyncRead.js | 9 + .../futures_io/if_std/trait.AsyncWrite.js | 9 + .../identity_hash/trait.IdentityHashable.js | 9 + .../iroh_quinn/runtime/trait.AsyncTimer.js | 9 + .../iroh_quinn/runtime/trait.Runtime.js | 9 + .../trait.ConnectionIdGenerator.js | 9 + .../iroh_quinn_proto/coding/trait.Codec.js | 9 + .../config/qlog/trait.QlogFactory.js | 9 + .../config/trait.TimeSource.js | 9 + .../congestion/trait.Controller.js | 9 + .../congestion/trait.ControllerFactory.js | 9 + .../iroh_quinn_proto/crypto/trait.AeadKey.js | 9 + .../crypto/trait.ClientConfig.js | 9 + .../crypto/trait.HandshakeTokenKey.js | 9 + .../crypto/trait.HeaderKey.js | 9 + .../iroh_quinn_proto/crypto/trait.HmacKey.js | 9 + .../crypto/trait.PacketKey.js | 9 + .../crypto/trait.ServerConfig.js | 9 + .../iroh_quinn_proto/crypto/trait.Session.js | 9 + .../packet/trait.ConnectionIdParser.js | 9 + .../iroh_quinn_proto/token/trait.TokenLog.js | 9 + .../token/trait.TokenStore.js | 9 + .../rustls/verify/trait.ServerCertVerifier.js | 9 + .../tokio/io/async_read/trait.AsyncRead.js | 9 + .../tokio/io/async_write/trait.AsyncWrite.js | 9 + .../docs/type.impl/core/result/enum.Result.js | 9 + 864 files changed, 59150 insertions(+) create mode 100644 pr/234/docs/.lock create mode 100644 pr/234/docs/bench/all.html create mode 100644 pr/234/docs/bench/enum.CipherSuite.html create mode 100644 pr/234/docs/bench/fn.configure_tracing_subscriber.html create mode 100644 pr/234/docs/bench/fn.connect_client.html create mode 100644 pr/234/docs/bench/fn.drain_stream.html create mode 100644 pr/234/docs/bench/fn.rt.html create mode 100644 pr/234/docs/bench/fn.send_data_on_stream.html create mode 100644 pr/234/docs/bench/fn.server_endpoint.html create mode 100644 pr/234/docs/bench/fn.transport_config.html create mode 100644 pr/234/docs/bench/index.html create mode 100644 pr/234/docs/bench/sidebar-items.js create mode 100644 pr/234/docs/bench/stats/fn.throughput_bps.html create mode 100644 pr/234/docs/bench/stats/index.html create mode 100644 pr/234/docs/bench/stats/sidebar-items.js create mode 100644 pr/234/docs/bench/stats/struct.Stats.html create mode 100644 pr/234/docs/bench/stats/struct.StreamStats.html create mode 100644 pr/234/docs/bench/stats/struct.TransferResult.html create mode 100644 pr/234/docs/bench/struct.Opt.html create mode 100644 pr/234/docs/bulk/all.html create mode 100644 pr/234/docs/bulk/fn.client.html create mode 100644 pr/234/docs/bulk/fn.handle_client_stream.html create mode 100644 pr/234/docs/bulk/fn.main.html create mode 100644 pr/234/docs/bulk/fn.server.html create mode 100644 pr/234/docs/bulk/index.html create mode 100644 pr/234/docs/bulk/sidebar-items.js create mode 100644 pr/234/docs/bulk/struct.ClientStats.html create mode 100644 pr/234/docs/certificate/all.html create mode 100644 pr/234/docs/certificate/fn.configure_client.html create mode 100644 pr/234/docs/certificate/fn.generate_self_signed_cert.html create mode 100644 pr/234/docs/certificate/fn.main.html create mode 100644 pr/234/docs/certificate/fn.read_certs_from_file.html create mode 100644 pr/234/docs/certificate/index.html create mode 100644 pr/234/docs/certificate/sidebar-items.js create mode 100644 pr/234/docs/certificate/struct.SkipServerVerification.html create mode 100644 pr/234/docs/crates.js create mode 100644 pr/234/docs/data_transfer/all.html create mode 100644 pr/234/docs/data_transfer/fn.main.html create mode 100644 pr/234/docs/data_transfer/fn.open_bidirectional_stream.html create mode 100644 pr/234/docs/data_transfer/fn.open_unidirectional_stream.html create mode 100644 pr/234/docs/data_transfer/fn.receive_bidirectional_stream.html create mode 100644 pr/234/docs/data_transfer/fn.receive_datagram.html create mode 100644 pr/234/docs/data_transfer/fn.receive_unidirectional_stream.html create mode 100644 pr/234/docs/data_transfer/fn.send_unreliable.html create mode 100644 pr/234/docs/data_transfer/index.html create mode 100644 pr/234/docs/data_transfer/sidebar-items.js create mode 100644 pr/234/docs/help.html create mode 100644 pr/234/docs/iroh_quinn/all.html create mode 100644 pr/234/docs/iroh_quinn/congestion/index.html create mode 100644 pr/234/docs/iroh_quinn/congestion/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.Bbr.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.BbrConfig.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.ControllerMetrics.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.Cubic.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.CubicConfig.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.NewReno.html create mode 100644 pr/234/docs/iroh_quinn/congestion/struct.NewRenoConfig.html create mode 100644 pr/234/docs/iroh_quinn/congestion/trait.Controller.html create mode 100644 pr/234/docs/iroh_quinn/congestion/trait.ControllerFactory.html create mode 100644 pr/234/docs/iroh_quinn/connection/enum.SendDatagramError.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.AcceptBi.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.AcceptUni.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.Connecting.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.Connection.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.OnClosed.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.OpenBi.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.OpenUni.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.ReadDatagram.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.SendDatagram.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.WeakConnectionHandle.html create mode 100644 pr/234/docs/iroh_quinn/connection/struct.ZeroRttAccepted.html create mode 100644 pr/234/docs/iroh_quinn/crypto/index.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/enum.Error.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/index.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/234/docs/iroh_quinn/crypto/rustls/struct.TlsSession.html create mode 100644 pr/234/docs/iroh_quinn/crypto/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn/crypto/struct.CryptoError.html create mode 100644 pr/234/docs/iroh_quinn/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/234/docs/iroh_quinn/crypto/struct.KeyPair.html create mode 100644 pr/234/docs/iroh_quinn/crypto/struct.Keys.html create mode 100644 pr/234/docs/iroh_quinn/crypto/struct.UnsupportedVersion.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.AeadKey.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.ClientConfig.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.HeaderKey.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.HmacKey.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.PacketKey.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.ServerConfig.html create mode 100644 pr/234/docs/iroh_quinn/crypto/trait.Session.html create mode 100644 pr/234/docs/iroh_quinn/endpoint/struct.Accept.html create mode 100644 pr/234/docs/iroh_quinn/endpoint/struct.Endpoint.html create mode 100644 pr/234/docs/iroh_quinn/endpoint/struct.EndpointStats.html create mode 100644 pr/234/docs/iroh_quinn/enum.ConfigError.html create mode 100644 pr/234/docs/iroh_quinn/enum.ConnectError.html create mode 100644 pr/234/docs/iroh_quinn/enum.ConnectionError.html create mode 100644 pr/234/docs/iroh_quinn/enum.Dir.html create mode 100644 pr/234/docs/iroh_quinn/enum.EcnCodepoint.html create mode 100644 pr/234/docs/iroh_quinn/enum.ReadError.html create mode 100644 pr/234/docs/iroh_quinn/enum.ReadExactError.html create mode 100644 pr/234/docs/iroh_quinn/enum.ReadToEndError.html create mode 100644 pr/234/docs/iroh_quinn/enum.ResetError.html create mode 100644 pr/234/docs/iroh_quinn/enum.SendDatagramError.html create mode 100644 pr/234/docs/iroh_quinn/enum.Side.html create mode 100644 pr/234/docs/iroh_quinn/enum.StoppedError.html create mode 100644 pr/234/docs/iroh_quinn/enum.WriteError.html create mode 100644 pr/234/docs/iroh_quinn/fn.default_runtime.html create mode 100644 pr/234/docs/iroh_quinn/incoming/struct.Incoming.html create mode 100644 pr/234/docs/iroh_quinn/incoming/struct.IncomingFuture.html create mode 100644 pr/234/docs/iroh_quinn/incoming/struct.RetryError.html create mode 100644 pr/234/docs/iroh_quinn/index.html create mode 100644 pr/234/docs/iroh_quinn/path/struct.OpenPath.html create mode 100644 pr/234/docs/iroh_quinn/path/struct.Path.html create mode 100644 pr/234/docs/iroh_quinn/recv_stream/enum.ReadError.html create mode 100644 pr/234/docs/iroh_quinn/recv_stream/enum.ReadExactError.html create mode 100644 pr/234/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html create mode 100644 pr/234/docs/iroh_quinn/recv_stream/enum.ResetError.html create mode 100644 pr/234/docs/iroh_quinn/recv_stream/struct.RecvStream.html create mode 100644 pr/234/docs/iroh_quinn/runtime/fn.default_runtime.html create mode 100644 pr/234/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html create mode 100644 pr/234/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html create mode 100644 pr/234/docs/iroh_quinn/runtime/trait.AsyncTimer.html create mode 100644 pr/234/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html create mode 100644 pr/234/docs/iroh_quinn/runtime/trait.Runtime.html create mode 100644 pr/234/docs/iroh_quinn/runtime/trait.UdpSender.html create mode 100644 pr/234/docs/iroh_quinn/send_stream/enum.StoppedError.html create mode 100644 pr/234/docs/iroh_quinn/send_stream/enum.WriteError.html create mode 100644 pr/234/docs/iroh_quinn/send_stream/struct.SendStream.html create mode 100644 pr/234/docs/iroh_quinn/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn/struct.Accept.html create mode 100644 pr/234/docs/iroh_quinn/struct.AcceptBi.html create mode 100644 pr/234/docs/iroh_quinn/struct.AcceptUni.html create mode 100644 pr/234/docs/iroh_quinn/struct.AckFrequencyConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.ApplicationClose.html create mode 100644 pr/234/docs/iroh_quinn/struct.BloomTokenLog.html create mode 100644 pr/234/docs/iroh_quinn/struct.Chunk.html create mode 100644 pr/234/docs/iroh_quinn/struct.ClientConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.ClosedStream.html create mode 100644 pr/234/docs/iroh_quinn/struct.Connecting.html create mode 100644 pr/234/docs/iroh_quinn/struct.Connection.html create mode 100644 pr/234/docs/iroh_quinn/struct.ConnectionClose.html create mode 100644 pr/234/docs/iroh_quinn/struct.ConnectionId.html create mode 100644 pr/234/docs/iroh_quinn/struct.ConnectionStats.html create mode 100644 pr/234/docs/iroh_quinn/struct.Endpoint.html create mode 100644 pr/234/docs/iroh_quinn/struct.EndpointConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.EndpointStats.html create mode 100644 pr/234/docs/iroh_quinn/struct.FrameStats.html create mode 100644 pr/234/docs/iroh_quinn/struct.FrameType.html create mode 100644 pr/234/docs/iroh_quinn/struct.IdleTimeout.html create mode 100644 pr/234/docs/iroh_quinn/struct.Incoming.html create mode 100644 pr/234/docs/iroh_quinn/struct.IncomingFuture.html create mode 100644 pr/234/docs/iroh_quinn/struct.MtuDiscoveryConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.NoneTokenLog.html create mode 100644 pr/234/docs/iroh_quinn/struct.NoneTokenStore.html create mode 100644 pr/234/docs/iroh_quinn/struct.OnClosed.html create mode 100644 pr/234/docs/iroh_quinn/struct.OpenBi.html create mode 100644 pr/234/docs/iroh_quinn/struct.OpenPath.html create mode 100644 pr/234/docs/iroh_quinn/struct.OpenUni.html create mode 100644 pr/234/docs/iroh_quinn/struct.Path.html create mode 100644 pr/234/docs/iroh_quinn/struct.PathId.html create mode 100644 pr/234/docs/iroh_quinn/struct.PathStats.html create mode 100644 pr/234/docs/iroh_quinn/struct.QlogConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.QlogFileFactory.html create mode 100644 pr/234/docs/iroh_quinn/struct.ReadDatagram.html create mode 100644 pr/234/docs/iroh_quinn/struct.RecvStream.html create mode 100644 pr/234/docs/iroh_quinn/struct.RetryError.html create mode 100644 pr/234/docs/iroh_quinn/struct.SendDatagram.html create mode 100644 pr/234/docs/iroh_quinn/struct.SendStream.html create mode 100644 pr/234/docs/iroh_quinn/struct.ServerConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.SmolRuntime.html create mode 100644 pr/234/docs/iroh_quinn/struct.StdSystemTime.html create mode 100644 pr/234/docs/iroh_quinn/struct.StreamId.html create mode 100644 pr/234/docs/iroh_quinn/struct.TokenMemoryCache.html create mode 100644 pr/234/docs/iroh_quinn/struct.TokenReuseError.html create mode 100644 pr/234/docs/iroh_quinn/struct.TokioRuntime.html create mode 100644 pr/234/docs/iroh_quinn/struct.Transmit.html create mode 100644 pr/234/docs/iroh_quinn/struct.TransportConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.TransportErrorCode.html create mode 100644 pr/234/docs/iroh_quinn/struct.UdpStats.html create mode 100644 pr/234/docs/iroh_quinn/struct.ValidationTokenConfig.html create mode 100644 pr/234/docs/iroh_quinn/struct.VarInt.html create mode 100644 pr/234/docs/iroh_quinn/struct.VarIntBoundsExceeded.html create mode 100644 pr/234/docs/iroh_quinn/struct.WeakConnectionHandle.html create mode 100644 pr/234/docs/iroh_quinn/struct.Written.html create mode 100644 pr/234/docs/iroh_quinn/struct.ZeroRttAccepted.html create mode 100644 pr/234/docs/iroh_quinn/trait.AsyncTimer.html create mode 100644 pr/234/docs/iroh_quinn/trait.AsyncUdpSocket.html create mode 100644 pr/234/docs/iroh_quinn/trait.ConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn/trait.QlogFactory.html create mode 100644 pr/234/docs/iroh_quinn/trait.Runtime.html create mode 100644 pr/234/docs/iroh_quinn/trait.TimeSource.html create mode 100644 pr/234/docs/iroh_quinn/trait.TokenLog.html create mode 100644 pr/234/docs/iroh_quinn/trait.TokenStore.html create mode 100644 pr/234/docs/iroh_quinn/trait.UdpSender.html create mode 100644 pr/234/docs/iroh_quinn_proto/all.html create mode 100644 pr/234/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html create mode 100644 pr/234/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/coding/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/coding/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html create mode 100644 pr/234/docs/iroh_quinn_proto/coding/trait.Codec.html create mode 100644 pr/234/docs/iroh_quinn_proto/coding/type.Result.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/enum.ConfigError.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/struct.ClientConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/struct.EndpointConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/struct.ServerConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/struct.StdSystemTime.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/trait.TimeSource.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.Bbr.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.Cubic.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.NewReno.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/trait.Controller.html create mode 100644 pr/234/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/enum.ClosePathError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/enum.ConnectionError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/enum.Event.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/enum.PathError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/struct.PathId.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/streams/struct.Streams.html create mode 100644 pr/234/docs/iroh_quinn_proto/connection/struct.Connection.html create mode 100644 pr/234/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html create mode 100644 pr/234/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/struct.CryptoError.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/struct.KeyPair.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/struct.Keys.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.AeadKey.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.HmacKey.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.PacketKey.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/crypto/trait.Session.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/struct.Incoming.html create mode 100644 pr/234/docs/iroh_quinn_proto/endpoint/struct.RetryError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ClosePathError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ConfigError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ConnectError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ConnectionError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.DatagramEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.Dir.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.EcnCodepoint.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.Event.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.FinishError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.LongType.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.PacketDecodeError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.PathError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.PathEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.PathStatus.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ProtectedHeader.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ReadError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.ReadableError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.SendDatagramError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.SetPathStatusError.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.Side.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.StreamEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/enum.WriteError.html create mode 100644 pr/234/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html create mode 100644 pr/234/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html create mode 100644 pr/234/docs/iroh_quinn_proto/frame/struct.Datagram.html create mode 100644 pr/234/docs/iroh_quinn_proto/frame/struct.FrameType.html create mode 100644 pr/234/docs/iroh_quinn_proto/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Error.html create mode 100644 pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Event.html create mode 100644 pr/234/docs/iroh_quinn_proto/iroh_hp/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/packet/enum.LongType.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/struct.PartialDecode.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html create mode 100644 pr/234/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html create mode 100644 pr/234/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html create mode 100644 pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionId.html create mode 100644 pr/234/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/struct.AcceptError.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ApplicationClose.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.BloomTokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Chunk.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Chunks.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ClientConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ClosedPath.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ClosedStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Connection.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ConnectionClose.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ConnectionEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ConnectionHandle.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ConnectionId.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ConnectionStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Datagram.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Datagrams.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Endpoint.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.EndpointConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.EndpointEvent.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.FrameStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.FrameType.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.IdleTimeout.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Incoming.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.InvalidCid.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.NoneTokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.NoneTokenStore.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.PartialDecode.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.PathId.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.PathStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.QlogConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.QlogFileFactory.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.RecvStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.RetryError.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.RttEstimator.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.SendStream.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ServerConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ShouldTransmit.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.StdSystemTime.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.StreamId.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Streams.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.TokenMemoryCache.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.TokenReuseError.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Transmit.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.TransportConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.TransportError.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.TransportErrorCode.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.UdpStats.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.VarInt.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html create mode 100644 pr/234/docs/iroh_quinn_proto/struct.Written.html create mode 100644 pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html create mode 100644 pr/234/docs/iroh_quinn_proto/token/struct.TokenReuseError.html create mode 100644 pr/234/docs/iroh_quinn_proto/token/trait.TokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/token/trait.TokenStore.html create mode 100644 pr/234/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.ConnectionIdParser.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.QlogFactory.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.TimeSource.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.TokenLog.html create mode 100644 pr/234/docs/iroh_quinn_proto/trait.TokenStore.html create mode 100644 pr/234/docs/iroh_quinn_proto/transport_error/struct.Code.html create mode 100644 pr/234/docs/iroh_quinn_proto/transport_error/struct.Error.html create mode 100644 pr/234/docs/iroh_quinn_proto/transport_parameters/enum.Error.html create mode 100644 pr/234/docs/iroh_quinn_proto/transport_parameters/index.html create mode 100644 pr/234/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html create mode 100644 pr/234/docs/iroh_quinn_proto/varint/struct.VarInt.html create mode 100644 pr/234/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html create mode 100644 pr/234/docs/iroh_quinn_udp/all.html create mode 100644 pr/234/docs/iroh_quinn_udp/constant.BATCH_SIZE.html create mode 100644 pr/234/docs/iroh_quinn_udp/enum.EcnCodepoint.html create mode 100644 pr/234/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html create mode 100644 pr/234/docs/iroh_quinn_udp/index.html create mode 100644 pr/234/docs/iroh_quinn_udp/sidebar-items.js create mode 100644 pr/234/docs/iroh_quinn_udp/struct.RecvMeta.html create mode 100644 pr/234/docs/iroh_quinn_udp/struct.Transmit.html create mode 100644 pr/234/docs/iroh_quinn_udp/struct.UdpSockRef.html create mode 100644 pr/234/docs/iroh_quinn_udp/struct.UdpSocketState.html create mode 100644 pr/234/docs/perf/all.html create mode 100644 pr/234/docs/perf/client/fn.run.html create mode 100644 pr/234/docs/perf/client/index.html create mode 100644 pr/234/docs/perf/client/sidebar-items.js create mode 100644 pr/234/docs/perf/client/struct.Opt.html create mode 100644 pr/234/docs/perf/enum.CongestionAlgorithm.html create mode 100644 pr/234/docs/perf/fn.parse_byte_size.html create mode 100644 pr/234/docs/perf/index.html create mode 100644 pr/234/docs/perf/noprotection/index.html create mode 100644 pr/234/docs/perf/noprotection/sidebar-items.js create mode 100644 pr/234/docs/perf/noprotection/struct.NoProtectionClientConfig.html create mode 100644 pr/234/docs/perf/noprotection/struct.NoProtectionServerConfig.html create mode 100644 pr/234/docs/perf/server/fn.run.html create mode 100644 pr/234/docs/perf/server/index.html create mode 100644 pr/234/docs/perf/server/sidebar-items.js create mode 100644 pr/234/docs/perf/server/struct.Opt.html create mode 100644 pr/234/docs/perf/sidebar-items.js create mode 100644 pr/234/docs/perf/static.PERF_CIPHER_SUITES.html create mode 100644 pr/234/docs/perf/stats/index.html create mode 100644 pr/234/docs/perf/stats/sidebar-items.js create mode 100644 pr/234/docs/perf/stats/struct.OpenStreamStats.html create mode 100644 pr/234/docs/perf/stats/struct.Stats.html create mode 100644 pr/234/docs/perf/stats/struct.StreamStats.html create mode 100644 pr/234/docs/perf/struct.CommonOpt.html create mode 100644 pr/234/docs/quinn_perf/all.html create mode 100644 pr/234/docs/quinn_perf/enum.Commands.html create mode 100644 pr/234/docs/quinn_perf/fn.main.html create mode 100644 pr/234/docs/quinn_perf/index.html create mode 100644 pr/234/docs/quinn_perf/sidebar-items.js create mode 100644 pr/234/docs/quinn_perf/struct.Cli.html create mode 100644 pr/234/docs/search.index/003a3a433ad6.js create mode 100644 pr/234/docs/search.index/005aa45d6078.js create mode 100644 pr/234/docs/search.index/00663acd356b.js create mode 100644 pr/234/docs/search.index/006b398a6f2a.js create mode 100644 pr/234/docs/search.index/00b1c72d15ee.js create mode 100644 pr/234/docs/search.index/019bd8f229e4.js create mode 100644 pr/234/docs/search.index/01bda78c46d2.js create mode 100644 pr/234/docs/search.index/01d8a434418f.js create mode 100644 pr/234/docs/search.index/02e652a4afc3.js create mode 100644 pr/234/docs/search.index/0369b203eb42.js create mode 100644 pr/234/docs/search.index/03a2caa15777.js create mode 100644 pr/234/docs/search.index/059f564e1f6b.js create mode 100644 pr/234/docs/search.index/05ae80dc1c58.js create mode 100644 pr/234/docs/search.index/05df7f0f67ba.js create mode 100644 pr/234/docs/search.index/0731b0da5a08.js create mode 100644 pr/234/docs/search.index/081bb183a3a6.js create mode 100644 pr/234/docs/search.index/083bd29ff9ff.js create mode 100644 pr/234/docs/search.index/0850db5b7e99.js create mode 100644 pr/234/docs/search.index/0852167fd512.js create mode 100644 pr/234/docs/search.index/0878d7ee02a1.js create mode 100644 pr/234/docs/search.index/0977b4e74445.js create mode 100644 pr/234/docs/search.index/0a2079f1a35f.js create mode 100644 pr/234/docs/search.index/0b5dc05327dc.js create mode 100644 pr/234/docs/search.index/0ba626d32cbc.js create mode 100644 pr/234/docs/search.index/0baf5c0b970e.js create mode 100644 pr/234/docs/search.index/0c04c5c572cf.js create mode 100644 pr/234/docs/search.index/0c918467df3c.js create mode 100644 pr/234/docs/search.index/0caf669e19ba.js create mode 100644 pr/234/docs/search.index/0d537b6a73b7.js create mode 100644 pr/234/docs/search.index/0e480fb4ebc7.js create mode 100644 pr/234/docs/search.index/0f7753ba0d50.js create mode 100644 pr/234/docs/search.index/10355c6b286e.js create mode 100644 pr/234/docs/search.index/1180cfa8fde3.js create mode 100644 pr/234/docs/search.index/11b8f016baed.js create mode 100644 pr/234/docs/search.index/12171281f9bb.js create mode 100644 pr/234/docs/search.index/12df68ee6610.js create mode 100644 pr/234/docs/search.index/132765159445.js create mode 100644 pr/234/docs/search.index/132fa3273a2a.js create mode 100644 pr/234/docs/search.index/1404ee0d6cc3.js create mode 100644 pr/234/docs/search.index/149132598270.js create mode 100644 pr/234/docs/search.index/149b3bfa7d2f.js create mode 100644 pr/234/docs/search.index/14af5b4de5e7.js create mode 100644 pr/234/docs/search.index/150ff4191cab.js create mode 100644 pr/234/docs/search.index/156e94e5de34.js create mode 100644 pr/234/docs/search.index/161ff8529816.js create mode 100644 pr/234/docs/search.index/16c553f3af58.js create mode 100644 pr/234/docs/search.index/16c72901534d.js create mode 100644 pr/234/docs/search.index/170f27c7a8bd.js create mode 100644 pr/234/docs/search.index/18822e341642.js create mode 100644 pr/234/docs/search.index/1882ea84a648.js create mode 100644 pr/234/docs/search.index/1a37a0ff7662.js create mode 100644 pr/234/docs/search.index/1a46991231d2.js create mode 100644 pr/234/docs/search.index/1a8751cccd15.js create mode 100644 pr/234/docs/search.index/1acf55718c6c.js create mode 100644 pr/234/docs/search.index/1c0669362394.js create mode 100644 pr/234/docs/search.index/1c7b86fac1f5.js create mode 100644 pr/234/docs/search.index/1d9d469499b8.js create mode 100644 pr/234/docs/search.index/1e65bf627061.js create mode 100644 pr/234/docs/search.index/201202f6eb90.js create mode 100644 pr/234/docs/search.index/20292df8ad95.js create mode 100644 pr/234/docs/search.index/20f24dd90959.js create mode 100644 pr/234/docs/search.index/2196a484188a.js create mode 100644 pr/234/docs/search.index/22338a595bbb.js create mode 100644 pr/234/docs/search.index/2334fd1256bd.js create mode 100644 pr/234/docs/search.index/243791893159.js create mode 100644 pr/234/docs/search.index/247e576b2cc4.js create mode 100644 pr/234/docs/search.index/2574b34a8660.js create mode 100644 pr/234/docs/search.index/27441501e1c3.js create mode 100644 pr/234/docs/search.index/27797ee166ea.js create mode 100644 pr/234/docs/search.index/27f6cb7cd2c6.js create mode 100644 pr/234/docs/search.index/2849bc9335e1.js create mode 100644 pr/234/docs/search.index/285e4fa908fe.js create mode 100644 pr/234/docs/search.index/295d133206ed.js create mode 100644 pr/234/docs/search.index/299e6d3a1674.js create mode 100644 pr/234/docs/search.index/29beed7d2cc8.js create mode 100644 pr/234/docs/search.index/2a0d8ab46fb3.js create mode 100644 pr/234/docs/search.index/2a464ed978c7.js create mode 100644 pr/234/docs/search.index/2a6c1f20350c.js create mode 100644 pr/234/docs/search.index/2acf01fb4c07.js create mode 100644 pr/234/docs/search.index/2b385adf4f14.js create mode 100644 pr/234/docs/search.index/2c4d0eda72da.js create mode 100644 pr/234/docs/search.index/2da70c21552d.js create mode 100644 pr/234/docs/search.index/2e6dfbf22b19.js create mode 100644 pr/234/docs/search.index/309b4ef99850.js create mode 100644 pr/234/docs/search.index/30a8ca0b3469.js create mode 100644 pr/234/docs/search.index/319143b9dcad.js create mode 100644 pr/234/docs/search.index/31ec884fea72.js create mode 100644 pr/234/docs/search.index/322c33796bd9.js create mode 100644 pr/234/docs/search.index/341a074166cf.js create mode 100644 pr/234/docs/search.index/3439f6b04774.js create mode 100644 pr/234/docs/search.index/37041e27a69e.js create mode 100644 pr/234/docs/search.index/3766bd3b524f.js create mode 100644 pr/234/docs/search.index/37fbc343ca2b.js create mode 100644 pr/234/docs/search.index/38bf81f092c9.js create mode 100644 pr/234/docs/search.index/39f1ae6853b3.js create mode 100644 pr/234/docs/search.index/3ad40b841b32.js create mode 100644 pr/234/docs/search.index/3b9c9d813a13.js create mode 100644 pr/234/docs/search.index/3c0affdb0268.js create mode 100644 pr/234/docs/search.index/3c13ca3e4e61.js create mode 100644 pr/234/docs/search.index/3c4b2624d9b8.js create mode 100644 pr/234/docs/search.index/3e6b1a75c307.js create mode 100644 pr/234/docs/search.index/3e8fa26efc53.js create mode 100644 pr/234/docs/search.index/3ea05a9a63fc.js create mode 100644 pr/234/docs/search.index/3ecfb741bfaf.js create mode 100644 pr/234/docs/search.index/40410c9d55a4.js create mode 100644 pr/234/docs/search.index/41c76bd7e9b0.js create mode 100644 pr/234/docs/search.index/42740cbde866.js create mode 100644 pr/234/docs/search.index/435ef6b1a68d.js create mode 100644 pr/234/docs/search.index/440ad754dc7b.js create mode 100644 pr/234/docs/search.index/441c00806b58.js create mode 100644 pr/234/docs/search.index/442973fdf353.js create mode 100644 pr/234/docs/search.index/44b01bde9d64.js create mode 100644 pr/234/docs/search.index/44d1c75fe36d.js create mode 100644 pr/234/docs/search.index/464be0b62530.js create mode 100644 pr/234/docs/search.index/4683bd92a354.js create mode 100644 pr/234/docs/search.index/46f1494df7bd.js create mode 100644 pr/234/docs/search.index/47698cc593eb.js create mode 100644 pr/234/docs/search.index/47e65f6bbe8e.js create mode 100644 pr/234/docs/search.index/47f0dfc2bfd8.js create mode 100644 pr/234/docs/search.index/48120e13d7d1.js create mode 100644 pr/234/docs/search.index/498bb15a8c92.js create mode 100644 pr/234/docs/search.index/49c82ba58485.js create mode 100644 pr/234/docs/search.index/4aa239b6a7ae.js create mode 100644 pr/234/docs/search.index/4b421ea6441b.js create mode 100644 pr/234/docs/search.index/4d5fb1407792.js create mode 100644 pr/234/docs/search.index/4e0bb34e56c4.js create mode 100644 pr/234/docs/search.index/4ec66add1e98.js create mode 100644 pr/234/docs/search.index/4fbd4ea92e70.js create mode 100644 pr/234/docs/search.index/50184a00a93c.js create mode 100644 pr/234/docs/search.index/50f6f3198519.js create mode 100644 pr/234/docs/search.index/510fd0e4bb9e.js create mode 100644 pr/234/docs/search.index/516a9f043d12.js create mode 100644 pr/234/docs/search.index/51a21faaeab8.js create mode 100644 pr/234/docs/search.index/51a886ba80e8.js create mode 100644 pr/234/docs/search.index/54064ee22fd2.js create mode 100644 pr/234/docs/search.index/54679bb88e4d.js create mode 100644 pr/234/docs/search.index/54aa7ed4bc9a.js create mode 100644 pr/234/docs/search.index/5527af3a0275.js create mode 100644 pr/234/docs/search.index/5583dbff6ecd.js create mode 100644 pr/234/docs/search.index/558b25ba5cd4.js create mode 100644 pr/234/docs/search.index/579abe8832f0.js create mode 100644 pr/234/docs/search.index/57a002b9f9d0.js create mode 100644 pr/234/docs/search.index/57da63a54625.js create mode 100644 pr/234/docs/search.index/57ec46f6e8db.js create mode 100644 pr/234/docs/search.index/58c1c76c1b23.js create mode 100644 pr/234/docs/search.index/592306864b13.js create mode 100644 pr/234/docs/search.index/59a125e0550b.js create mode 100644 pr/234/docs/search.index/5af7c9ebff9c.js create mode 100644 pr/234/docs/search.index/5da06bd3cb28.js create mode 100644 pr/234/docs/search.index/5dba02cd6a4e.js create mode 100644 pr/234/docs/search.index/5e7543ac82c6.js create mode 100644 pr/234/docs/search.index/5fb178f0c084.js create mode 100644 pr/234/docs/search.index/6015e8720a21.js create mode 100644 pr/234/docs/search.index/60416c1e7d76.js create mode 100644 pr/234/docs/search.index/604a3991077d.js create mode 100644 pr/234/docs/search.index/6059e72d2543.js create mode 100644 pr/234/docs/search.index/6192a27f0e53.js create mode 100644 pr/234/docs/search.index/61f88a01075d.js create mode 100644 pr/234/docs/search.index/62e97f3b5f23.js create mode 100644 pr/234/docs/search.index/63d0f2065b1f.js create mode 100644 pr/234/docs/search.index/6417a7048837.js create mode 100644 pr/234/docs/search.index/64aa132e56e9.js create mode 100644 pr/234/docs/search.index/64f30fe8b248.js create mode 100644 pr/234/docs/search.index/67a6347fe3ae.js create mode 100644 pr/234/docs/search.index/68314aeade4e.js create mode 100644 pr/234/docs/search.index/683f4785394c.js create mode 100644 pr/234/docs/search.index/69af465a5e19.js create mode 100644 pr/234/docs/search.index/6a4dbb0f44b4.js create mode 100644 pr/234/docs/search.index/6ae1dbe7be1c.js create mode 100644 pr/234/docs/search.index/6bf9d14a8094.js create mode 100644 pr/234/docs/search.index/6dcb10ceafda.js create mode 100644 pr/234/docs/search.index/6e2902148b8e.js create mode 100644 pr/234/docs/search.index/6f777c411721.js create mode 100644 pr/234/docs/search.index/6fa98d7155d9.js create mode 100644 pr/234/docs/search.index/71761381578a.js create mode 100644 pr/234/docs/search.index/724bf250813c.js create mode 100644 pr/234/docs/search.index/739826fcec2f.js create mode 100644 pr/234/docs/search.index/74051be7e32a.js create mode 100644 pr/234/docs/search.index/740bdb0f158e.js create mode 100644 pr/234/docs/search.index/74722d2b5b62.js create mode 100644 pr/234/docs/search.index/749bbd73b08e.js create mode 100644 pr/234/docs/search.index/74bdadfc1d31.js create mode 100644 pr/234/docs/search.index/74fafa2468b8.js create mode 100644 pr/234/docs/search.index/7561fbf6279c.js create mode 100644 pr/234/docs/search.index/75aa6f082a69.js create mode 100644 pr/234/docs/search.index/75bce28ef9fb.js create mode 100644 pr/234/docs/search.index/7880cad733fe.js create mode 100644 pr/234/docs/search.index/794cab3d1528.js create mode 100644 pr/234/docs/search.index/79753bcfbf4f.js create mode 100644 pr/234/docs/search.index/79c04eb28188.js create mode 100644 pr/234/docs/search.index/7b2046dab109.js create mode 100644 pr/234/docs/search.index/7b3aaaa369a3.js create mode 100644 pr/234/docs/search.index/7bb9fa36e896.js create mode 100644 pr/234/docs/search.index/7bcd03fe6a97.js create mode 100644 pr/234/docs/search.index/7c75104910e5.js create mode 100644 pr/234/docs/search.index/7d2ea3138a91.js create mode 100644 pr/234/docs/search.index/7d7822a13b6a.js create mode 100644 pr/234/docs/search.index/7d862dad6f48.js create mode 100644 pr/234/docs/search.index/7dc3252609e4.js create mode 100644 pr/234/docs/search.index/7f27b460fa5c.js create mode 100644 pr/234/docs/search.index/7f728bb3feca.js create mode 100644 pr/234/docs/search.index/alias/a762150c532c.js create mode 100644 pr/234/docs/search.index/crateNames/8a381fcf2bd4.js create mode 100644 pr/234/docs/search.index/desc/f10ce68b8c31.js create mode 100644 pr/234/docs/search.index/entry/1a5b5718bf93.js create mode 100644 pr/234/docs/search.index/function/6f4f4cff046f.js create mode 100644 pr/234/docs/search.index/generic_inverted_index/6183c510d241.js create mode 100644 pr/234/docs/search.index/name/d1009e9c1189.js create mode 100644 pr/234/docs/search.index/normalizedName/87730ecd4586.js create mode 100644 pr/234/docs/search.index/path/6aab90dd54df.js create mode 100644 pr/234/docs/search.index/root.js create mode 100644 pr/234/docs/search.index/type/2aa9cf747fcf.js create mode 100644 pr/234/docs/set_up_connection/all.html create mode 100644 pr/234/docs/set_up_connection/constant.CLIENT_ADDR.html create mode 100644 pr/234/docs/set_up_connection/constant.LOCALHOST_V4.html create mode 100644 pr/234/docs/set_up_connection/constant.SERVER_ADDR.html create mode 100644 pr/234/docs/set_up_connection/constant.SERVER_NAME.html create mode 100644 pr/234/docs/set_up_connection/fn.client.html create mode 100644 pr/234/docs/set_up_connection/fn.main.html create mode 100644 pr/234/docs/set_up_connection/fn.server.html create mode 100644 pr/234/docs/set_up_connection/index.html create mode 100644 pr/234/docs/set_up_connection/sidebar-items.js create mode 100644 pr/234/docs/settings.html create mode 100644 pr/234/docs/src-files.js create mode 100644 pr/234/docs/src/bench/lib.rs.html create mode 100644 pr/234/docs/src/bench/stats.rs.html create mode 100644 pr/234/docs/src/bulk/bulk.rs.html create mode 100644 pr/234/docs/src/certificate/certificate.rs.html create mode 100644 pr/234/docs/src/data_transfer/data-transfer.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/connection.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/endpoint.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/incoming.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/lib.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/mutex.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/path.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/recv_stream.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/runtime/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/runtime/smol.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/runtime/tokio.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/send_stream.rs.html create mode 100644 pr/234/docs/src/iroh_quinn/work_limiter.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/address_discovery.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/bloom_token_log.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/cid_generator.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/cid_queue.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/coding.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/config/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/config/qlog.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/config/transport.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion/cubic.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/assembler.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/cid_state.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/datagrams.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/mtud.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/pacing.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/paths.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/qlog.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/spaces.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/state.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/stats.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/streams/send.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/streams/state.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/timer.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/constant_time.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/crypto.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/crypto/rustls.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/endpoint.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/frame.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/iroh_hp.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/lib.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/packet.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/range_set/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/shared.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/token.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/token_memory_cache.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/transport_error.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/transport_parameters.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_proto/varint.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_udp/cmsg/mod.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_udp/cmsg/unix.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_udp/lib.rs.html create mode 100644 pr/234/docs/src/iroh_quinn_udp/unix.rs.html create mode 100644 pr/234/docs/src/perf/client.rs.html create mode 100644 pr/234/docs/src/perf/lib.rs.html create mode 100644 pr/234/docs/src/perf/noprotection.rs.html create mode 100644 pr/234/docs/src/perf/server.rs.html create mode 100644 pr/234/docs/src/perf/stats.rs.html create mode 100644 pr/234/docs/src/quinn_perf/perf.rs.html create mode 100644 pr/234/docs/src/set_up_connection/set-up-connection.rs.html create mode 100644 pr/234/docs/static.files/COPYRIGHT-7fb11f4e.txt create mode 100644 pr/234/docs/static.files/FiraMono-Medium-86f75c8c.woff2 create mode 100644 pr/234/docs/static.files/FiraMono-Regular-87c26294.woff2 create mode 100644 pr/234/docs/static.files/FiraSans-Italic-81dc35de.woff2 create mode 100644 pr/234/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt create mode 100644 pr/234/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 create mode 100644 pr/234/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 create mode 100644 pr/234/docs/static.files/FiraSans-Regular-0fe48ade.woff2 create mode 100644 pr/234/docs/static.files/LICENSE-APACHE-a60eea81.txt create mode 100644 pr/234/docs/static.files/LICENSE-MIT-23f18e03.txt create mode 100644 pr/234/docs/static.files/NanumBarunGothic-13b3dcba.ttf.woff2 create mode 100644 pr/234/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt create mode 100644 pr/234/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt create mode 100644 pr/234/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceCodePro-Semibold-aa29a496.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceSerif4-LICENSE-a2cfd9d5.md create mode 100644 pr/234/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 create mode 100644 pr/234/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 create mode 100644 pr/234/docs/static.files/favicon-044be391.svg create mode 100644 pr/234/docs/static.files/favicon-32x32-eab170b8.png create mode 100644 pr/234/docs/static.files/main-ce535bd0.js create mode 100644 pr/234/docs/static.files/normalize-9960930a.css create mode 100644 pr/234/docs/static.files/noscript-263c88ec.css create mode 100644 pr/234/docs/static.files/rust-logo-9a9549ea.svg create mode 100644 pr/234/docs/static.files/rustdoc-ca0dd0c4.css create mode 100644 pr/234/docs/static.files/scrape-examples-2bbcccac.js create mode 100644 pr/234/docs/static.files/search-8d3311b9.js create mode 100644 pr/234/docs/static.files/settings-c38705f0.js create mode 100644 pr/234/docs/static.files/src-script-813739b1.js create mode 100644 pr/234/docs/static.files/storage-e2aeef58.js create mode 100644 pr/234/docs/static.files/stringdex-828709d0.js create mode 100644 pr/234/docs/trait.impl/arbitrary/trait.Arbitrary.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.Args.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.Parser.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.Subcommand.js create mode 100644 pr/234/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js create mode 100644 pr/234/docs/trait.impl/core/clone/trait.Clone.js create mode 100644 pr/234/docs/trait.impl/core/cmp/trait.Eq.js create mode 100644 pr/234/docs/trait.impl/core/cmp/trait.Ord.js create mode 100644 pr/234/docs/trait.impl/core/cmp/trait.PartialEq.js create mode 100644 pr/234/docs/trait.impl/core/cmp/trait.PartialOrd.js create mode 100644 pr/234/docs/trait.impl/core/convert/trait.From.js create mode 100644 pr/234/docs/trait.impl/core/convert/trait.TryFrom.js create mode 100644 pr/234/docs/trait.impl/core/default/trait.Default.js create mode 100644 pr/234/docs/trait.impl/core/error/trait.Error.js create mode 100644 pr/234/docs/trait.impl/core/fmt/trait.Debug.js create mode 100644 pr/234/docs/trait.impl/core/fmt/trait.Display.js create mode 100644 pr/234/docs/trait.impl/core/future/future/trait.Future.js create mode 100644 pr/234/docs/trait.impl/core/future/into_future/trait.IntoFuture.js create mode 100644 pr/234/docs/trait.impl/core/hash/trait.Hash.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.Copy.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.Freeze.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.Send.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.StructuralPartialEq.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.Sync.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.Unpin.js create mode 100644 pr/234/docs/trait.impl/core/marker/trait.UnsafeUnpin.js create mode 100644 pr/234/docs/trait.impl/core/ops/bit/trait.Not.js create mode 100644 pr/234/docs/trait.impl/core/ops/deref/trait.Deref.js create mode 100644 pr/234/docs/trait.impl/core/ops/deref/trait.DerefMut.js create mode 100644 pr/234/docs/trait.impl/core/ops/drop/trait.Drop.js create mode 100644 pr/234/docs/trait.impl/core/ops/index/trait.Index.js create mode 100644 pr/234/docs/trait.impl/core/ops/index/trait.IndexMut.js create mode 100644 pr/234/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js create mode 100644 pr/234/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js create mode 100644 pr/234/docs/trait.impl/core/str/traits/trait.FromStr.js create mode 100644 pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js create mode 100644 pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js create mode 100644 pr/234/docs/trait.impl/identity_hash/trait.IdentityHashable.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/coding/trait.Codec.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js create mode 100644 pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js create mode 100644 pr/234/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js create mode 100644 pr/234/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js create mode 100644 pr/234/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js create mode 100644 pr/234/docs/type.impl/core/result/enum.Result.js diff --git a/pr/234/docs/.lock b/pr/234/docs/.lock new file mode 100644 index 000000000..e69de29bb diff --git a/pr/234/docs/bench/all.html b/pr/234/docs/bench/all.html new file mode 100644 index 000000000..6778d9fe3 --- /dev/null +++ b/pr/234/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/234/docs/bench/enum.CipherSuite.html b/pr/234/docs/bench/enum.CipherSuite.html new file mode 100644 index 000000000..4e2a5df97 --- /dev/null +++ b/pr/234/docs/bench/enum.CipherSuite.html @@ -0,0 +1,28 @@ +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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/bench/fn.configure_tracing_subscriber.html b/pr/234/docs/bench/fn.configure_tracing_subscriber.html new file mode 100644 index 000000000..b90c9f5a0 --- /dev/null +++ b/pr/234/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/234/docs/bench/fn.connect_client.html b/pr/234/docs/bench/fn.connect_client.html new file mode 100644 index 000000000..f9d47c825 --- /dev/null +++ b/pr/234/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/234/docs/bench/fn.drain_stream.html b/pr/234/docs/bench/fn.drain_stream.html new file mode 100644 index 000000000..06117e3c7 --- /dev/null +++ b/pr/234/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: &mut RecvStream,
+    read_unordered: bool,
+) -> Result<usize>
\ No newline at end of file diff --git a/pr/234/docs/bench/fn.rt.html b/pr/234/docs/bench/fn.rt.html new file mode 100644 index 000000000..d225b959e --- /dev/null +++ b/pr/234/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/234/docs/bench/fn.send_data_on_stream.html b/pr/234/docs/bench/fn.send_data_on_stream.html new file mode 100644 index 000000000..1297e62fe --- /dev/null +++ b/pr/234/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/234/docs/bench/fn.server_endpoint.html b/pr/234/docs/bench/fn.server_endpoint.html new file mode 100644 index 000000000..5bc3586f1 --- /dev/null +++ b/pr/234/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/234/docs/bench/fn.transport_config.html b/pr/234/docs/bench/fn.transport_config.html new file mode 100644 index 000000000..3793983be --- /dev/null +++ b/pr/234/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/234/docs/bench/index.html b/pr/234/docs/bench/index.html new file mode 100644 index 000000000..c4071a8f4 --- /dev/null +++ b/pr/234/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/234/docs/bench/sidebar-items.js b/pr/234/docs/bench/sidebar-items.js new file mode 100644 index 000000000..f56525d6b --- /dev/null +++ b/pr/234/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/234/docs/bench/stats/fn.throughput_bps.html b/pr/234/docs/bench/stats/fn.throughput_bps.html new file mode 100644 index 000000000..0a49b335e --- /dev/null +++ b/pr/234/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/234/docs/bench/stats/index.html b/pr/234/docs/bench/stats/index.html new file mode 100644 index 000000000..3655ae338 --- /dev/null +++ b/pr/234/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/234/docs/bench/stats/sidebar-items.js b/pr/234/docs/bench/stats/sidebar-items.js new file mode 100644 index 000000000..6272a13ae --- /dev/null +++ b/pr/234/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/234/docs/bench/stats/struct.Stats.html b/pr/234/docs/bench/stats/struct.Stats.html new file mode 100644 index 000000000..f254e544c --- /dev/null +++ b/pr/234/docs/bench/stats/struct.Stats.html @@ -0,0 +1,22 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/bench/stats/struct.StreamStats.html b/pr/234/docs/bench/stats/struct.StreamStats.html new file mode 100644 index 000000000..dde056aa9 --- /dev/null +++ b/pr/234/docs/bench/stats/struct.StreamStats.html @@ -0,0 +1,20 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/bench/stats/struct.TransferResult.html b/pr/234/docs/bench/stats/struct.TransferResult.html new file mode 100644 index 000000000..637c9d5bf --- /dev/null +++ b/pr/234/docs/bench/stats/struct.TransferResult.html @@ -0,0 +1,21 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/bench/struct.Opt.html b/pr/234/docs/bench/struct.Opt.html new file mode 100644 index 000000000..f341a191d --- /dev/null +++ b/pr/234/docs/bench/struct.Opt.html @@ -0,0 +1,61 @@ +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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/bulk/all.html b/pr/234/docs/bulk/all.html new file mode 100644 index 000000000..0a6875d72 --- /dev/null +++ b/pr/234/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/234/docs/bulk/fn.client.html b/pr/234/docs/bulk/fn.client.html new file mode 100644 index 000000000..6a2f1e48a --- /dev/null +++ b/pr/234/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/234/docs/bulk/fn.handle_client_stream.html b/pr/234/docs/bulk/fn.handle_client_stream.html new file mode 100644 index 000000000..ba79a8757 --- /dev/null +++ b/pr/234/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/234/docs/bulk/fn.main.html b/pr/234/docs/bulk/fn.main.html new file mode 100644 index 000000000..e8cfaa7ca --- /dev/null +++ b/pr/234/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/234/docs/bulk/fn.server.html b/pr/234/docs/bulk/fn.server.html new file mode 100644 index 000000000..2ea0860dd --- /dev/null +++ b/pr/234/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/234/docs/bulk/index.html b/pr/234/docs/bulk/index.html new file mode 100644 index 000000000..6b9249f74 --- /dev/null +++ b/pr/234/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/234/docs/bulk/sidebar-items.js b/pr/234/docs/bulk/sidebar-items.js new file mode 100644 index 000000000..4b4dceb17 --- /dev/null +++ b/pr/234/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/234/docs/bulk/struct.ClientStats.html b/pr/234/docs/bulk/struct.ClientStats.html new file mode 100644 index 000000000..cb4fbe399 --- /dev/null +++ b/pr/234/docs/bulk/struct.ClientStats.html @@ -0,0 +1,20 @@ +ClientStats in bulk - Rust

ClientStats

Struct ClientStats 

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

Fields§

§upload_stats: Stats§download_stats: Stats

Implementations§

Source§

impl ClientStats

Source

pub fn print(&self, client_id: usize)

Trait Implementations§

Source§

impl Default for ClientStats

Source§

fn default() -> ClientStats

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/certificate/all.html b/pr/234/docs/certificate/all.html new file mode 100644 index 000000000..d6e1b79d5 --- /dev/null +++ b/pr/234/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/234/docs/certificate/fn.configure_client.html b/pr/234/docs/certificate/fn.configure_client.html new file mode 100644 index 000000000..95e203add --- /dev/null +++ b/pr/234/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/234/docs/certificate/fn.generate_self_signed_cert.html b/pr/234/docs/certificate/fn.generate_self_signed_cert.html new file mode 100644 index 000000000..e91269156 --- /dev/null +++ b/pr/234/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/234/docs/certificate/fn.main.html b/pr/234/docs/certificate/fn.main.html new file mode 100644 index 000000000..461b95b57 --- /dev/null +++ b/pr/234/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/234/docs/certificate/fn.read_certs_from_file.html b/pr/234/docs/certificate/fn.read_certs_from_file.html new file mode 100644 index 000000000..bd63cf355 --- /dev/null +++ b/pr/234/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/234/docs/certificate/index.html b/pr/234/docs/certificate/index.html new file mode 100644 index 000000000..eb429342b --- /dev/null +++ b/pr/234/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/234/docs/certificate/sidebar-items.js b/pr/234/docs/certificate/sidebar-items.js new file mode 100644 index 000000000..0f8f69b2b --- /dev/null +++ b/pr/234/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/234/docs/certificate/struct.SkipServerVerification.html b/pr/234/docs/certificate/struct.SkipServerVerification.html new file mode 100644 index 000000000..7228a9ce6 --- /dev/null +++ b/pr/234/docs/certificate/struct.SkipServerVerification.html @@ -0,0 +1,37 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/crates.js b/pr/234/docs/crates.js new file mode 100644 index 000000000..25a54490d --- /dev/null +++ b/pr/234/docs/crates.js @@ -0,0 +1,2 @@ +window.ALL_CRATES = ["bench","bulk","certificate","data_transfer","iroh_quinn","iroh_quinn_proto","iroh_quinn_udp","perf","quinn_perf","set_up_connection"]; +//{"start":21,"fragment_lengths":[7,7,14,16,13,19,17,7,13,20]} \ No newline at end of file diff --git a/pr/234/docs/data_transfer/all.html b/pr/234/docs/data_transfer/all.html new file mode 100644 index 000000000..fd64bbb8f --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.main.html b/pr/234/docs/data_transfer/fn.main.html new file mode 100644 index 000000000..8bcfacdf5 --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.open_bidirectional_stream.html b/pr/234/docs/data_transfer/fn.open_bidirectional_stream.html new file mode 100644 index 000000000..2c097384f --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.open_unidirectional_stream.html b/pr/234/docs/data_transfer/fn.open_unidirectional_stream.html new file mode 100644 index 000000000..0d5cfbb84 --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.receive_bidirectional_stream.html b/pr/234/docs/data_transfer/fn.receive_bidirectional_stream.html new file mode 100644 index 000000000..0180f8dca --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.receive_datagram.html b/pr/234/docs/data_transfer/fn.receive_datagram.html new file mode 100644 index 000000000..f5467d0f5 --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.receive_unidirectional_stream.html b/pr/234/docs/data_transfer/fn.receive_unidirectional_stream.html new file mode 100644 index 000000000..5d00e7b8f --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/fn.send_unreliable.html b/pr/234/docs/data_transfer/fn.send_unreliable.html new file mode 100644 index 000000000..ab8421938 --- /dev/null +++ b/pr/234/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/234/docs/data_transfer/index.html b/pr/234/docs/data_transfer/index.html new file mode 100644 index 000000000..bc41bd1c4 --- /dev/null +++ b/pr/234/docs/data_transfer/index.html @@ -0,0 +1 @@ +data_transfer - Rust

Crate data_transfer

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

All

Rustdoc help

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

All

List of all items

Structs

Enums

Traits

Functions

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

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

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

Traits§

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

Bbr

Struct Bbr 

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

Experimental! Use at your own risk.

+

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

+

Implementations§

Source§

impl Bbr

Source

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

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

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

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

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

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

One or more packets were just sent
Source§

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

Packet deliveries were confirmed Read more
Source§

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

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

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

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

BbrConfig

Struct BbrConfig 

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

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

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

Default limit on the amount of outstanding data in bytes.

+

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

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

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

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

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

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

impl Default for BbrConfig

Source§

fn default() -> BbrConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

ControllerMetrics

Struct ControllerMetrics 

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

Common congestion controller metrics

+

Fields (Non-exhaustive)§

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

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

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

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

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Cubic

Struct Cubic 

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

The RFC8312 congestion controller, as widely used for TCP

+

Implementations§

Source§

impl Cubic

Source

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

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Cubic

Source§

fn clone(&self) -> Cubic

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

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

Performs copy-assignment from source. Read more
Source§

impl Controller for Cubic

Source§

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

Packet deliveries were confirmed Read more
Source§

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

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

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

One or more packets were just sent
Source§

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

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

impl Debug for Cubic

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

CubicConfig

Struct CubicConfig 

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

Configuration for the Cubic congestion controller

+

Implementations§

Source§

impl CubicConfig

Source

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

Default limit on the amount of outstanding data in bytes.

+

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

+

Trait Implementations§

Source§

impl Clone for CubicConfig

Source§

fn clone(&self) -> CubicConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for CubicConfig

Source§

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

Construct a fresh Controller
Source§

impl Debug for CubicConfig

Source§

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

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

impl Default for CubicConfig

Source§

fn default() -> CubicConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

NewReno

Struct NewReno 

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

A simple, standard congestion controller

+

Implementations§

Source§

impl NewReno

Source

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

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for NewReno

Source§

fn clone(&self) -> NewReno

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

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

Performs copy-assignment from source. Read more
Source§

impl Controller for NewReno

Source§

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

Packet deliveries were confirmed Read more
Source§

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

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

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

fn window(&self) -> u64

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

fn metrics(&self) -> ControllerMetrics

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

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

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

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

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

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

One or more packets were just sent
Source§

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

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

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

NewRenoConfig

Struct NewRenoConfig 

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

Configuration for the NewReno congestion controller

+

Implementations§

Source§

impl NewRenoConfig

Source

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

Default limit on the amount of outstanding data in bytes.

+

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

+
Source

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

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

+

Trait Implementations§

Source§

impl Clone for NewRenoConfig

Source§

fn clone(&self) -> NewRenoConfig

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

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

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for NewRenoConfig

Source§

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

Construct a fresh Controller
Source§

impl Debug for NewRenoConfig

Source§

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

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

impl Default for NewRenoConfig

Source§

fn default() -> NewRenoConfig

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Controller

Trait Controller 

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

Common interface for different congestion controllers

+

Required Methods§

Source

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

Packets were deemed lost or marked congested

+

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

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

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

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

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

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

+

Provided Methods§

Source

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

One or more packets were just sent

+
Source

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

Packet deliveries were confirmed

+

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

+
Source

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

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

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

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

+
Source

fn metrics(&self) -> ControllerMetrics

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

+

Implementors§

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

ControllerFactory

Trait ControllerFactory 

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

Constructs controllers on demand

+

Required Methods§

Source

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

Construct a fresh Controller

+

Implementors§

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

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

+

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

+

Modules§

rustls
TLS interface based on rustls

Structs§

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

Traits§

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

Error

Enum Error 

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

rustls reports protocol errors using this type.

+

Variants (Non-exhaustive)§

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

InappropriateMessage

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

+

Fields

§expect_types: Vec<ContentType>

Which types we expected

+
§got_type: ContentType

What type we received

+
§

InappropriateHandshakeMessage

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

+

Fields

§expect_types: Vec<HandshakeType>

Which handshake type we expected

+
§got_type: HandshakeType

What handshake type we received

+
§

InvalidEncryptedClientHello(EncryptedClientHelloError)

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

+
§

InvalidMessage(InvalidMessage)

The peer sent us a TLS message with invalid contents.

+
§

NoCertificatesPresented

The peer didn’t give us any certificates.

+
§

UnsupportedNameType

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

+
§

DecryptError

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

+
§

EncryptError

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

+
§

PeerIncompatible(PeerIncompatible)

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

+
§

PeerMisbehaved(PeerMisbehaved)

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

+
§

AlertReceived(AlertDescription)

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

+
§

InvalidCertificate(CertificateError)

We saw an invalid certificate.

+

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

+
§

InvalidCertRevocationList(CertRevocationListError)

A provided certificate revocation list (CRL) was invalid.

+
§

General(String)

A catch-all error for unlikely errors.

+
§

FailedToGetCurrentTime

We failed to figure out what time it currently is.

+
§

FailedToGetRandomBytes

We failed to acquire random bytes from the system.

+
§

HandshakeNotComplete

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

+
§

PeerSentOversizedRecord

The peer sent an oversized record/fragment.

+
§

NoApplicationProtocol

An incoming connection did not support any known application protocol.

+
§

BadMaxFragmentSize

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

+
§

InconsistentKeys(InconsistentKeys)

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

+
§

Other(OtherError)

Any other error.

+

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

+

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

+

Trait Implementations§

§

impl Clone for Error

§

fn clone(&self) -> Error

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

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

Performs copy-assignment from source. Read more
§

impl Debug for Error

§

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

Formats the value using the given formatter. Read more
§

impl Display for Error

§

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

Formats the value using the given formatter. Read more
§

impl Error for Error

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl From<CertRevocationListError> for Error

§

fn from(e: CertRevocationListError) -> Error

Converts to this type from the input type.
§

impl From<CertificateError> for Error

§

fn from(e: CertificateError) -> Error

Converts to this type from the input type.
§

impl From<EncryptedClientHelloError> for Error

§

fn from(e: EncryptedClientHelloError) -> Error

Converts to this type from the input type.
§

impl From<GetRandomFailed> for Error

§

fn from(_: GetRandomFailed) -> Error

Converts to this type from the input type.
§

impl From<InconsistentKeys> for Error

§

fn from(e: InconsistentKeys) -> Error

Converts to this type from the input type.
§

impl From<InvalidMessage> for Error

§

fn from(e: InvalidMessage) -> Error

Converts to this type from the input type.
§

impl From<OtherError> for Error

§

fn from(value: OtherError) -> Error

Converts to this type from the input type.
§

impl From<PeerIncompatible> for Error

§

fn from(e: PeerIncompatible) -> Error

Converts to this type from the input type.
§

impl From<PeerMisbehaved> for Error

§

fn from(e: PeerMisbehaved) -> Error

Converts to this type from the input type.
§

impl From<SystemTimeError> for Error

§

fn from(_: SystemTimeError) -> Error

Converts to this type from the input type.
§

impl From<UnsupportedOperationError> for Error

§

fn from(value: UnsupportedOperationError) -> Error

Converts to this type from the input type.
§

impl PartialEq for Error

§

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

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

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

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

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/crypto/rustls/index.html b/pr/234/docs/iroh_quinn/crypto/rustls/index.html new file mode 100644 index 000000000..35a3c6577 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/rustls/index.html @@ -0,0 +1,2 @@ +iroh_quinn::crypto::rustls - Rust

Module rustls

Module rustls 

Source
Expand description

TLS interface based on rustls

+

Structs§

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

Enums§

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

HandshakeData

Struct HandshakeData 

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

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

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

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

NoInitialCipherSuite

Struct NoInitialCipherSuite 

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

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

+

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

+

Trait Implementations§

Source§

impl Clone for NoInitialCipherSuite

Source§

fn clone(&self) -> NoInitialCipherSuite

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for NoInitialCipherSuite

Source§

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

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

impl Display for NoInitialCipherSuite

Source§

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

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

impl Error for NoInitialCipherSuite

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html b/pr/234/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..f7fffe2c0 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,45 @@ +QuicClientConfig in iroh_quinn::crypto::rustls - Rust

QuicClientConfig

Struct QuicClientConfig 

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

A QUIC-compatible TLS client configuration

+

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

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

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

+

Implementations§

Source§

impl QuicClientConfig

Source

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

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

+

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

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

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

Start a client session with this configuration
Source§

impl TryFrom<Arc<ClientConfig>> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

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

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

Performs the conversion.
Source§

impl TryFrom<ClientConfig> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

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

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

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

QuicServerConfig

Struct QuicServerConfig 

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

A QUIC-compatible TLS server configuration

+

Quinn implicitly constructs a QuicServerConfig with reasonable defaults within +ServerConfig::with_single_cert(). Alternatively, QuicServerConfig’s TryFrom +implementation or with_initial method can be used to wrap around a custom +[rustls::ServerConfig], in which case care should be taken around certain points:

+
    +
  • If max_early_data_size is not set to u32::MAX, the server will not be able to accept +incoming 0-RTT data. QUIC prohibits max_early_data_size values other than 0 or u32::MAX.
  • +
  • The rustls::ServerConfig must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

Implementations§

Source§

impl QuicServerConfig

Source

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

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

+

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

+

Trait Implementations§

Source§

impl ServerConfig for QuicServerConfig

Source§

fn start_session( + self: Arc<QuicServerConfig>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more
Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

impl TryFrom<Arc<ServerConfig>> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

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

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

Performs the conversion.
Source§

impl TryFrom<ServerConfig> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

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

fn try_from( + inner: ServerConfig, +) -> Result<QuicServerConfig, <QuicServerConfig as TryFrom<ServerConfig>>::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

TlsSession

Struct TlsSession 

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

A rustls TLS session

+

Trait Implementations§

Source§

impl Session for TlsSession

Source§

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

For the rustls TlsSession, the Any type is Vec<rustls::pki_types::CertificateDer>

+
Source§

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

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

Get data negotiated during the handshake, if available Read more
Source§

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only) Read more
Source§

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer
Source§

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.
Source§

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>

Read bytes of handshake data Read more
Source§

fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>

The peer’s QUIC transport parameters Read more
Source§

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys Read more
Source§

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update
Source§

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet
Source§

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

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/sidebar-items.js b/pr/234/docs/iroh_quinn/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["AeadKey","ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/struct.CryptoError.html b/pr/234/docs/iroh_quinn/crypto/struct.CryptoError.html new file mode 100644 index 000000000..a8fab2b33 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/struct.CryptoError.html @@ -0,0 +1,18 @@ +CryptoError in iroh_quinn::crypto - Rust

CryptoError

Struct CryptoError 

Source
pub struct CryptoError;
Expand description

Generic crypto errors

+

Trait Implementations§

Source§

impl Debug for CryptoError

Source§

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

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

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> CryptoError

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

ExportKeyingMaterialError

Struct ExportKeyingMaterialError 

Source
pub struct ExportKeyingMaterialError;
Expand description

Error returned by Session::export_keying_material.

+

This error occurs if the requested output length is too large.

+

Trait Implementations§

Source§

impl Debug for ExportKeyingMaterialError

Source§

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

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

impl PartialEq for ExportKeyingMaterialError

Source§

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

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

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

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

impl Eq for ExportKeyingMaterialError

Source§

impl StructuralPartialEq for ExportKeyingMaterialError

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
§

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

KeyPair

Struct KeyPair 

Source
pub struct KeyPair<T> {
+    pub local: T,
+    pub remote: T,
+}
Expand description

A pair of keys for bidirectional communication

+

Fields§

§local: T

Key for encrypting data

+
§remote: T

Key for decrypting data

+

Auto Trait Implementations§

§

impl<T> Freeze for KeyPair<T>
where + T: Freeze,

§

impl<T> RefUnwindSafe for KeyPair<T>
where + T: RefUnwindSafe,

§

impl<T> Send for KeyPair<T>
where + T: Send,

§

impl<T> Sync for KeyPair<T>
where + T: Sync,

§

impl<T> Unpin for KeyPair<T>
where + T: Unpin,

§

impl<T> UnwindSafe for KeyPair<T>
where + T: UnwindSafe,

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

Keys

Struct Keys 

Source
pub struct Keys {
+    pub header: KeyPair<Box<dyn HeaderKey>>,
+    pub packet: KeyPair<Box<dyn PacketKey>>,
+}
Expand description

A complete set of keys for a certain packet space

+

Fields§

§header: KeyPair<Box<dyn HeaderKey>>

Header protection keys

+
§packet: KeyPair<Box<dyn PacketKey>>

Packet protection keys

+

Auto Trait Implementations§

§

impl Freeze for Keys

§

impl !RefUnwindSafe for Keys

§

impl Send for Keys

§

impl Sync for Keys

§

impl Unpin for Keys

§

impl !UnwindSafe for Keys

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

UnsupportedVersion

Struct UnsupportedVersion 

Source
pub struct UnsupportedVersion;
Expand description

Error indicating that the specified QUIC version is not supported

+

Trait Implementations§

Source§

impl Debug for UnsupportedVersion

Source§

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

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

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> ConnectError

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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

AeadKey

Trait AeadKey 

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

A key for sealing data with AEAD-based algorithms

+

Required Methods§

Source

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Method for sealing message data

+
Source

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

Method for opening a sealed message data

+

Implementations on Foreign Types§

Source§

impl AeadKey for LessSafeKey

Source§

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Source§

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

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.ClientConfig.html b/pr/234/docs/iroh_quinn/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..46555b5ff --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in iroh_quinn::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        server_name: &str,
+        params: &TransportParameters,
+    ) -> Result<Box<dyn Session>, ConnectError>;
+}
Expand description

Client-side configuration for the crypto protocol

+

Required Methods§

Source

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

Start a client session with this configuration

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html b/pr/234/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..85576ce8e --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,6 @@ +HandshakeTokenKey in iroh_quinn::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required method
+    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+}
Expand description

A pseudo random key for HKDF

+

Required Methods§

Source

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Derive AEAD using hkdf

+

Implementations on Foreign Types§

Source§

impl HandshakeTokenKey for Prk

Source§

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.HeaderKey.html b/pr/234/docs/iroh_quinn/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..bc22e9b18 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in iroh_quinn::crypto - Rust

HeaderKey

Trait HeaderKey 

Source
pub trait HeaderKey: Send + Sync {
+    // Required methods
+    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn sample_size(&self) -> usize;
+}
Expand description

Keys used to protect packet headers

+

Required Methods§

Source

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Decrypt the given packet’s header

+
Source

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Encrypt the given packet’s header

+
Source

fn sample_size(&self) -> usize

The sample size used for this key’s algorithm

+

Implementations on Foreign Types§

Source§

impl HeaderKey for Box<dyn HeaderProtectionKey>

Source§

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn sample_size(&self) -> usize

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.HmacKey.html b/pr/234/docs/iroh_quinn/crypto/trait.HmacKey.html new file mode 100644 index 000000000..684cfc6a1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in iroh_quinn::crypto - Rust

HmacKey

Trait HmacKey 

Source
pub trait HmacKey: Send + Sync {
+    // Required methods
+    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+    fn signature_len(&self) -> usize;
+    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+}
Expand description

A key for signing with HMAC-based algorithms

+

Required Methods§

Source

fn sign(&self, data: &[u8], signature_out: &mut [u8])

Method for signing a message

+
Source

fn signature_len(&self) -> usize

Length of sign’s output

+
Source

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Method for verifying a message

+

Implementations on Foreign Types§

Source§

impl HmacKey for Key

Source§

fn sign(&self, data: &[u8], out: &mut [u8])

Source§

fn signature_len(&self) -> usize

Source§

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.PacketKey.html b/pr/234/docs/iroh_quinn/crypto/trait.PacketKey.html new file mode 100644 index 000000000..b4580faf0 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in iroh_quinn::crypto - Rust

PacketKey

Trait PacketKey 

Source
pub trait PacketKey: Send + Sync {
+    // Required methods
+    fn encrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        buf: &mut [u8],
+        header_len: usize,
+    );
+    fn decrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        header: &[u8],
+        payload: &mut BytesMut,
+    ) -> Result<(), CryptoError>;
+    fn tag_len(&self) -> usize;
+    fn confidentiality_limit(&self) -> u64;
+    fn integrity_limit(&self) -> u64;
+}
Expand description

Keys used to protect packet payloads

+

Required Methods§

Source

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Encrypt the packet payload with the given packet number

+
Source

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Decrypt the packet payload with the given packet number

+
Source

fn tag_len(&self) -> usize

The length of the AEAD tag appended to packets on encryption

+
Source

fn confidentiality_limit(&self) -> u64

Maximum number of packets that may be sent using a single key

+
Source

fn integrity_limit(&self) -> u64

Maximum number of incoming packets that may fail decryption before the connection must be +abandoned

+

Implementations on Foreign Types§

Source§

impl PacketKey for Box<dyn PacketKey>

Source§

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Source§

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Source§

fn tag_len(&self) -> usize

Source§

fn confidentiality_limit(&self) -> u64

Source§

fn integrity_limit(&self) -> u64

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.ServerConfig.html b/pr/234/docs/iroh_quinn/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..45f057eae --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.ServerConfig.html @@ -0,0 +1,38 @@ +ServerConfig in iroh_quinn::crypto - Rust

ServerConfig

Trait ServerConfig 

Source
pub trait ServerConfig: Send + Sync {
+    // Required methods
+    fn initial_keys(
+        &self,
+        version: u32,
+        dst_cid: ConnectionId,
+    ) -> Result<Keys, UnsupportedVersion>;
+    fn retry_tag(
+        &self,
+        version: u32,
+        orig_dst_cid: ConnectionId,
+        packet: &[u8],
+    ) -> [u8; 16];
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        params: &TransportParameters,
+    ) -> Box<dyn Session>;
+}
Expand description

Server-side configuration for the crypto protocol

+

Required Methods§

Source

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet

+

Never called if initial_keys rejected version.

+
Source

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration

+

Never called if initial_keys rejected version.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/crypto/trait.Session.html b/pr/234/docs/iroh_quinn/crypto/trait.Session.html new file mode 100644 index 000000000..490c06363 --- /dev/null +++ b/pr/234/docs/iroh_quinn/crypto/trait.Session.html @@ -0,0 +1,68 @@ +Session in iroh_quinn::crypto - Rust

Session

Trait Session 

Source
pub trait Session:
+    Send
+    + Sync
+    + 'static {
+    // Required methods
+    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+    fn early_data_accepted(&self) -> Option<bool>;
+    fn is_handshaking(&self) -> bool;
+    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>;
+    fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>;
+    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+    fn is_valid_retry(
+        &self,
+        orig_dst_cid: ConnectionId,
+        header: &[u8],
+        payload: &[u8],
+    ) -> bool;
+    fn export_keying_material(
+        &self,
+        output: &mut [u8],
+        label: &[u8],
+        context: &[u8],
+    ) -> Result<(), ExportKeyingMaterialError>;
+}
Expand description

A cryptographic session (commonly TLS)

+

Required Methods§

Source

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

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

Get data negotiated during the handshake, if available

+

Returns None until the connection emits HandshakeDataReady.

+
Source

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

Get the peer’s identity, if available

+
Source

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only)

+

On the client side, this method can be used to see if 0-RTT key material is available +to start sending data before the protocol handshake has completed.

+

Returns None if the key material is not available. This might happen if you have +not connected to this server before.

+
Source

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer

+
Source

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.

+
Source

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, Error>

Read bytes of handshake data

+

This should be called with the contents of CRYPTO frames. If it returns Ok, the +caller should call write_handshake() to check if the crypto protocol has anything +to send to the peer. This method will only return true the first time that +handshake data is available. Future calls will always return false.

+

On success, returns true iff self.handshake_data() has been populated.

+
Source

fn transport_parameters(&self) -> Result<Option<TransportParameters>, Error>

The peer’s QUIC transport parameters

+

These are only available after the first flight from the peer has been received.

+
Source

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys

+

When the handshake proceeds to the next phase, this method will return a new set of +keys to encrypt data with.

+
Source

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update

+
Source

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet

+
Source

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

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation.

+

This function will fail, returning ExportKeyingMaterialError, +if the requested output length is too large.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/endpoint/struct.Accept.html b/pr/234/docs/iroh_quinn/endpoint/struct.Accept.html new file mode 100644 index 000000000..4a9a29dbf --- /dev/null +++ b/pr/234/docs/iroh_quinn/endpoint/struct.Accept.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Accept.html...

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

Redirecting to ../../iroh_quinn/struct.Endpoint.html...

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

Redirecting to ../../iroh_quinn/struct.EndpointStats.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/enum.ConfigError.html b/pr/234/docs/iroh_quinn/enum.ConfigError.html new file mode 100644 index 000000000..be8d34e78 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ConfigError.html @@ -0,0 +1,29 @@ +ConfigError in iroh_quinn - Rust

ConfigError

Enum ConfigError 

Source
#[non_exhaustive]
pub enum ConfigError { + OutOfBounds, +}
Expand description

Errors in the configuration of an endpoint

+

Variants (Non-exhaustive)§

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

OutOfBounds

Value exceeds supported bounds

+

Trait Implementations§

Source§

impl Clone for ConfigError

Source§

fn clone(&self) -> ConfigError

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConfigError

Source§

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

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

impl Display for ConfigError

Source§

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

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

impl Error for ConfigError

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl From<TryFromIntError> for ConfigError

Source§

fn from(_: TryFromIntError) -> ConfigError

Converts to this type from the input type.
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> ConfigError

Converts to this type from the input type.
Source§

impl PartialEq for ConfigError

Source§

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

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

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

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

impl Eq for ConfigError

Source§

impl StructuralPartialEq for ConfigError

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.ConnectError.html b/pr/234/docs/iroh_quinn/enum.ConnectError.html new file mode 100644 index 000000000..bd9aad982 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ConnectError.html @@ -0,0 +1,44 @@ +ConnectError in iroh_quinn - Rust

ConnectError

Enum ConnectError 

Source
pub enum ConnectError {
+    EndpointStopping,
+    CidsExhausted,
+    InvalidServerName(String),
+    InvalidRemoteAddress(SocketAddr),
+    NoDefaultClientConfig,
+    UnsupportedVersion,
+}
Expand description

Errors in the parameters being used to create a new connection

+

These arise before any I/O has been performed.

+

Variants§

§

EndpointStopping

The endpoint can no longer create new connections

+

Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs

+
§

InvalidServerName(String)

The given server name was malformed

+
§

InvalidRemoteAddress(SocketAddr)

The remote SocketAddr supplied was malformed

+

Examples include attempting to connect to port 0, or using an inappropriate address family.

+
§

NoDefaultClientConfig

No default client configuration was set up

+

Use Endpoint::connect_with to specify a client configuration.

+
§

UnsupportedVersion

The local endpoint does not support the QUIC version specified in the client configuration

+

Trait Implementations§

Source§

impl Clone for ConnectError

Source§

fn clone(&self) -> ConnectError

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectError

Source§

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

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

impl Display for ConnectError

Source§

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

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

impl Error for ConnectError

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> ConnectError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectError

Source§

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

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

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

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

impl Eq for ConnectError

Source§

impl StructuralPartialEq for ConnectError

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

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

§

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

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

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

§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

+

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

+
Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.ConnectionError.html b/pr/234/docs/iroh_quinn/enum.ConnectionError.html new file mode 100644 index 000000000..b56987fda --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ConnectionError.html @@ -0,0 +1,47 @@ +ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+    VersionMismatch,
+    TransportError(Error),
+    ConnectionClosed(ConnectionClose),
+    ApplicationClosed(ApplicationClose),
+    Reset,
+    TimedOut,
+    LocallyClosed,
+    CidsExhausted,
+}
Expand description

Reasons why a connection might be lost

+

Variants§

§

VersionMismatch

The peer doesn’t implement any supported version

+
§

TransportError(Error)

The peer violated the QUIC specification as understood by this implementation

+
§

ConnectionClosed(ConnectionClose)

The peer’s QUIC stack aborted the connection automatically

+
§

ApplicationClosed(ApplicationClose)

The peer closed the connection

+
§

Reset

The peer is unable to continue processing this connection, usually due to having restarted

+
§

TimedOut

Communication with the peer has lapsed for longer than the negotiated idle timeout

+

If neither side is sending keep-alives, a connection will time out after a long enough idle +period even if the peer is still reachable. See also TransportConfig::max_idle_timeout() +and TransportConfig::keep_alive_interval().

+
§

LocallyClosed

The local application closed the connection

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs.

+

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

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

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

impl Display for ConnectionError

Source§

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

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

impl Error for ConnectionError

Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl From<Close> for ConnectionError

Source§

fn from(x: Close) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<CloseReason> for ConnectionError

Source§

fn from(value: CloseReason) -> ConnectionError

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ReadError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: Error) -> ConnectionError

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

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

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

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

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

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

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

Source§

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

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

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.Dir.html b/pr/234/docs/iroh_quinn/enum.Dir.html new file mode 100644 index 000000000..306413e34 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.Dir.html @@ -0,0 +1,47 @@ +Dir in iroh_quinn - Rust

Dir

Enum Dir 

Source
pub enum Dir {
+    Bi = 0,
+    Uni = 1,
+}
Expand description

Whether a stream communicates data in both directions or only from the initiator

+

Variants§

§

Bi = 0

Data flows in both directions

+
§

Uni = 1

Data flows only from the stream’s initiator

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Dir

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Dir, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Dir, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Dir

Source§

fn clone(&self) -> Dir

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for Dir

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Dir

Source§

fn cmp(&self, other: &Dir) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Dir

Source§

fn eq(&self, other: &Dir) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Dir

Source§

fn partial_cmp(&self, other: &Dir) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Dir

Source§

impl Eq for Dir

Source§

impl StructuralPartialEq for Dir

Auto Trait Implementations§

§

impl Freeze for Dir

§

impl RefUnwindSafe for Dir

§

impl Send for Dir

§

impl Sync for Dir

§

impl Unpin for Dir

§

impl UnwindSafe for Dir

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.EcnCodepoint.html b/pr/234/docs/iroh_quinn/enum.EcnCodepoint.html new file mode 100644 index 000000000..8024ef7fd --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.EcnCodepoint.html @@ -0,0 +1,34 @@ +EcnCodepoint in iroh_quinn - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<EcnCodepoint>

Create new object from the given bits

+
Source

pub fn is_ce(self) -> bool

Returns whether the codepoint is a CE, signalling that congestion was experienced

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/enum.ReadError.html b/pr/234/docs/iroh_quinn/enum.ReadError.html new file mode 100644 index 000000000..052d0dd0c --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ReadError.html @@ -0,0 +1,42 @@ +ReadError in iroh_quinn - Rust

ReadError

Enum ReadError 

Source
pub enum ReadError {
+    Reset(VarInt),
+    ConnectionLost(ConnectionError),
+    ClosedStream,
+    IllegalOrderedRead,
+    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

+
§

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.

+
§

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<ReadableError> for ReadError

Source§

fn from(e: ReadableError) -> 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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.ReadExactError.html b/pr/234/docs/iroh_quinn/enum.ReadExactError.html new file mode 100644 index 000000000..de3f83b84 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ReadExactError.html @@ -0,0 +1,31 @@ +ReadExactError in iroh_quinn - Rust

ReadExactError

Enum ReadExactError 

Source
pub enum ReadExactError {
+    FinishedEarly(usize),
+    ReadError(ReadError),
+}
Expand description

Errors that arise from reading from a stream.

+

Variants§

§

FinishedEarly(usize)

The stream finished before all bytes were read

+
§

ReadError(ReadError)

A read error occurred

+

Trait Implementations§

Source§

impl Clone for ReadExactError

Source§

fn clone(&self) -> ReadExactError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadExactError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadExactError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadExactError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ReadError> for ReadExactError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ReadExactError

Source§

fn eq(&self, other: &ReadExactError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ReadExactError

Source§

impl StructuralPartialEq for ReadExactError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.ReadToEndError.html b/pr/234/docs/iroh_quinn/enum.ReadToEndError.html new file mode 100644 index 000000000..2800dd35c --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ReadToEndError.html @@ -0,0 +1,31 @@ +ReadToEndError in iroh_quinn - Rust

ReadToEndError

Enum ReadToEndError 

Source
pub enum ReadToEndError {
+    Read(ReadError),
+    TooLong,
+}
Expand description

Variants§

§

Read(ReadError)

An error occurred during reading

+
§

TooLong

The stream is larger than the user-supplied limit

+

Trait Implementations§

Source§

impl Clone for ReadToEndError

Source§

fn clone(&self) -> ReadToEndError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadToEndError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadToEndError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadToEndError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ReadError> for ReadToEndError

Source§

fn from(source: ReadError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ReadToEndError

Source§

fn eq(&self, other: &ReadToEndError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ReadToEndError

Source§

impl StructuralPartialEq for ReadToEndError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.ResetError.html b/pr/234/docs/iroh_quinn/enum.ResetError.html new file mode 100644 index 000000000..7fc762853 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.ResetError.html @@ -0,0 +1,33 @@ +ResetError in iroh_quinn - Rust

ResetError

Enum ResetError 

Source
pub enum ResetError {
+    ConnectionLost(ConnectionError),
+    ZeroRttRejected,
+}
Expand description

Errors that arise while waiting for a stream to be reset

+

Variants§

§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for ResetError

Source§

fn clone(&self) -> ResetError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ResetError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ResetError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ResetError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for ResetError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<ResetError> for Error

Source§

fn from(x: ResetError) -> Self

Converts to this type from the input type.
Source§

impl From<ResetError> for ReadError

Source§

fn from(e: ResetError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ResetError

Source§

fn eq(&self, other: &ResetError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ResetError

Source§

impl StructuralPartialEq for ResetError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.SendDatagramError.html b/pr/234/docs/iroh_quinn/enum.SendDatagramError.html new file mode 100644 index 000000000..05ca62f17 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.SendDatagramError.html @@ -0,0 +1,37 @@ +SendDatagramError in iroh_quinn - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+    UnsupportedByPeer,
+    Disabled,
+    TooLarge,
+    ConnectionLost(ConnectionError),
+}
Expand description

Errors that can arise when sending a datagram

+

Variants§

§

UnsupportedByPeer

The peer does not support receiving datagram frames

+
§

Disabled

Datagram support is disabled locally

+
§

TooLarge

The datagram is larger than the connection can currently accommodate

+

Indicates that the path MTU minus overhead or the limit advertised by the peer has been +exceeded.

+
§

ConnectionLost(ConnectionError)

The connection was lost

+

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SendDatagramError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SendDatagramError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SendDatagramError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for SendDatagramError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.Side.html b/pr/234/docs/iroh_quinn/enum.Side.html new file mode 100644 index 000000000..eec5b44c6 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.Side.html @@ -0,0 +1,48 @@ +Side in iroh_quinn - Rust

Side

Enum Side 

Source
pub enum Side {
+    Client = 0,
+    Server = 1,
+}
Expand description

Whether an endpoint was the initiator of a connection

+

Variants§

§

Client = 0

The initiator of a connection

+
§

Server = 1

The acceptor of a connection

+

Implementations§

Source§

impl Side

Source

pub fn is_client(self) -> bool

Shorthand for self == Side::Client

+
Source

pub fn is_server(self) -> bool

Shorthand for self == Side::Server

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Side

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Side, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Side, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Side

Source§

fn clone(&self) -> Side

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Side

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for Side

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Not for Side

Source§

type Output = Side

The resulting type after applying the ! operator.
Source§

fn not(self) -> Side

Performs the unary ! operation. Read more
Source§

impl Ord for Side

Source§

fn cmp(&self, other: &Side) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Side

Source§

fn eq(&self, other: &Side) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Side

Source§

fn partial_cmp(&self, other: &Side) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Side

Source§

impl Eq for Side

Source§

impl StructuralPartialEq for Side

Auto Trait Implementations§

§

impl Freeze for Side

§

impl RefUnwindSafe for Side

§

impl Send for Side

§

impl Sync for Side

§

impl Unpin for Side

§

impl UnwindSafe for Side

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/enum.StoppedError.html b/pr/234/docs/iroh_quinn/enum.StoppedError.html new file mode 100644 index 000000000..f7db5c0a9 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.StoppedError.html @@ -0,0 +1,33 @@ +StoppedError in iroh_quinn - Rust

StoppedError

Enum StoppedError 

Source
pub enum StoppedError {
+    ConnectionLost(ConnectionError),
+    ZeroRttRejected,
+}
Expand description

Errors that arise while monitoring for a send stream stop from the peer

+

Variants§

§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for StoppedError

Source§

fn clone(&self) -> StoppedError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for StoppedError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StoppedError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for StoppedError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for StoppedError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for Error

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for WriteError

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for StoppedError

Source§

fn eq(&self, other: &StoppedError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for StoppedError

Source§

impl StructuralPartialEq for StoppedError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/enum.WriteError.html b/pr/234/docs/iroh_quinn/enum.WriteError.html new file mode 100644 index 000000000..dce1c76f1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/enum.WriteError.html @@ -0,0 +1,38 @@ +WriteError in iroh_quinn - Rust

WriteError

Enum WriteError 

Source
pub enum WriteError {
+    Stopped(VarInt),
+    ConnectionLost(ConnectionError),
+    ClosedStream,
+    ZeroRttRejected,
+}
Expand description

Errors that arise from writing to a stream

+

Variants§

§

Stopped(VarInt)

The peer is no longer accepting data on this stream

+

Carries an application-defined error code.

+
§

ConnectionLost(ConnectionError)

The connection was lost

+
§

ClosedStream

The stream has already been finished or reset

+
§

ZeroRttRejected

This was a 0-RTT stream and the server rejected it

+

Can only occur on clients for 0-RTT streams, which can be opened using +Connecting::into_0rtt().

+

Trait Implementations§

Source§

impl Clone for WriteError

Source§

fn clone(&self) -> WriteError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WriteError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for WriteError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for WriteError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for WriteError

Source§

fn from(_: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl From<ConnectionError> for WriteError

Source§

fn from(source: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<StoppedError> for WriteError

Source§

fn from(x: StoppedError) -> Self

Converts to this type from the input type.
Source§

impl From<WriteError> for Error

Source§

fn from(x: WriteError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for WriteError

Source§

fn eq(&self, other: &WriteError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for WriteError

Source§

impl StructuralPartialEq for WriteError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/fn.default_runtime.html b/pr/234/docs/iroh_quinn/fn.default_runtime.html new file mode 100644 index 000000000..448428173 --- /dev/null +++ b/pr/234/docs/iroh_quinn/fn.default_runtime.html @@ -0,0 +1,5 @@ +default_runtime in iroh_quinn - Rust

default_runtime

Function default_runtime 

Source
pub fn default_runtime() -> Option<Arc<dyn Runtime>>
Expand description

Automatically select an appropriate runtime from those enabled at compile time

+

If runtime-tokio is enabled and this function is called from within a Tokio runtime context, +then TokioRuntime is returned. Otherwise, if runtime-smol is enabled, SmolRuntime is +returned. Otherwise, None is returned.

+
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/incoming/struct.Incoming.html b/pr/234/docs/iroh_quinn/incoming/struct.Incoming.html new file mode 100644 index 000000000..abd44a6e1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/incoming/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/incoming/struct.IncomingFuture.html b/pr/234/docs/iroh_quinn/incoming/struct.IncomingFuture.html new file mode 100644 index 000000000..4471430fd --- /dev/null +++ b/pr/234/docs/iroh_quinn/incoming/struct.IncomingFuture.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.IncomingFuture.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/incoming/struct.RetryError.html b/pr/234/docs/iroh_quinn/incoming/struct.RetryError.html new file mode 100644 index 000000000..5376fb9c1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/incoming/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/index.html b/pr/234/docs/iroh_quinn/index.html new file mode 100644 index 000000000..4cc2499d8 --- /dev/null +++ b/pr/234/docs/iroh_quinn/index.html @@ -0,0 +1,34 @@ +iroh_quinn - Rust

Crate iroh_quinn

Crate iroh_quinn 

Source
Expand description

QUIC transport protocol implementation

+

QUIC is a modern transport protocol addressing +shortcomings of TCP, such as head-of-line blocking, poor security, slow handshakes, and +inefficient congestion control. This crate provides a portable userspace implementation. It +builds on top of quinn-proto, which implements protocol logic independent of any particular +runtime.

+

The entry point of this crate is the Endpoint.

+

§About QUIC

+

A QUIC connection is an association between two endpoints. The endpoint which initiates the +connection is termed the client, and the endpoint which accepts it is termed the server. A +single endpoint may function as both client and server for different connections, for example +in a peer-to-peer application. To communicate application data, each endpoint may open streams +up to a limit dictated by its peer. Typically, that limit is increased as old streams are +finished.

+

Streams may be unidirectional or bidirectional, and are cheap to create and disposable. For +example, a traditionally datagram-oriented application could use a new stream for every +message it wants to send, no longer needing to worry about MTUs. Bidirectional streams behave +much like a traditional TCP connection, and are useful for sending messages that have an +immediate response, such as an HTTP request. Stream data is delivered reliably, and there is no +ordering enforced between data on different streams.

+

By avoiding head-of-line blocking and providing unified congestion control across all streams +of a connection, QUIC is able to provide higher throughput and lower latency than one or +multiple TCP connections between the same two hosts, while providing more useful behavior than +raw UDP sockets.

+

Quinn also exposes unreliable datagrams, which are a low-level primitive preferred when +automatic fragmentation and retransmission of certain data is not desired.

+

QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS +server, it is useful for a QUIC server to be identified by a certificate signed by a trusted +authority. If this is infeasible–for example, if servers are short-lived or not associated +with a domain name–then as with TLS, self-signed certificates can be used to provide +encryption alone.

+

Re-exports§

pub use rustls;
pub use udp;

Modules§

congestion
Logic for controlling the rate at which data is sent
crypto
Traits and implementations for the QUIC cryptography protocol

Structs§

Accept
Future produced by Endpoint::accept
AcceptBi
Future produced by Connection::accept_bi
AcceptUni
Future produced by Connection::accept_uni
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
ClientConfig
Configuration for outgoing connections
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connecting
In-progress connection attempt future
Connection
A QUIC connection.
ConnectionClose
Reason given by the transport for closing the connection
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics
Endpoint
A QUIC endpoint.
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointStats
Statistics on Endpoint activity
FrameStats
Number of frames transmitted or received of each frame type
FrameType
A QUIC frame type
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake
IncomingFuture
Basic adapter to let Incoming be await-ed like a Connecting
MtuDiscoveryConfig
Parameters governing MTU discovery.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
OnClosed
Future returned by Connection::on_closed
OpenBi
Future produced by Connection::open_bi
OpenPath
Future produced by crate::Connection::open_path
OpenUni
Future produced by Connection::open_uni
Path
An open (Multi)Path
PathId
Id representing different paths when using multipath extension
PathStats
Statistics related to a transmission path
QlogConfig
Configuration for qlog trace logging.
QlogFileFactory
Enables writing qlog traces to a directory.
ReadDatagram
Future produced by Connection::read_datagram
RecvStream
A stream that can only be used to receive data
RetryError
Error for attempting to retry an Incoming which already bears a token from a previous retry
SendDatagram
Future produced by Connection::send_datagram_wait
SendStream
A stream that can only be used to send data
ServerConfig
Parameters governing incoming connections
SmolRuntime
A Quinn runtime for smol
StdSystemTime
Default implementation of TimeSource
StreamId
Identifier for a stream within a particular connection
TokenMemoryCache
TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory
TokenReuseError
Error for when a validation token may have been reused
TokioRuntime
A Quinn runtime for Tokio
Transmit
An outgoing packet
TransportConfig
Parameters governing the core QUIC state machine
TransportErrorCode
Transport-level error code
UdpStats
Statistics about UDP datagrams transmitted or received on a connection
ValidationTokenConfig
Configuration for sending and handling validation tokens in incoming connections
VarInt
An integer less than 2^62
VarIntBoundsExceeded
Error returned when constructing a VarInt from a value >= 2^62
WeakConnectionHandle
A handle to some connection internals, use with care.
Written
Indicates how many bytes and chunks had been transferred in a write operation
ZeroRttAccepted
Future that completes when a connection is fully established

Enums§

ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
ReadError
Errors that arise from reading from a stream.
ReadExactError
Errors that arise from reading from a stream.
ReadToEndError
Errors from RecvStream::read_to_end
ResetError
Errors that arise while waiting for a stream to be reset
SendDatagramError
Errors that can arise when sending a datagram
Side
Whether an endpoint was the initiator of a connection
StoppedError
Errors that arise while monitoring for a send stream stop from the peer
WriteError
Errors that arise from writing to a stream

Traits§

AsyncTimer
Abstract implementation of an async timer for runtime independence
AsyncUdpSocket
Abstract implementation of a UDP socket for runtime independence
ConnectionIdGenerator
Generates connection IDs for incoming connections
QlogFactory
Constructs a QlogConfig for individual connections.
Runtime
Abstracts I/O and timer operations for runtime independence
TimeSource
Object to get current SystemTime
TokenLog
Responsible for limiting clients’ ability to reuse validation tokens
TokenStore
Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections
UdpSender
An object for asynchronously writing to an associated AsyncUdpSocket.

Functions§

default_runtime
Automatically select an appropriate runtime from those enabled at compile time
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/path/struct.OpenPath.html b/pr/234/docs/iroh_quinn/path/struct.OpenPath.html new file mode 100644 index 000000000..98307dd6b --- /dev/null +++ b/pr/234/docs/iroh_quinn/path/struct.OpenPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.OpenPath.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/path/struct.Path.html b/pr/234/docs/iroh_quinn/path/struct.Path.html new file mode 100644 index 000000000..ca4fa66c4 --- /dev/null +++ b/pr/234/docs/iroh_quinn/path/struct.Path.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.Path.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/recv_stream/enum.ReadError.html b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadError.html new file mode 100644 index 000000000..a7c5a50f0 --- /dev/null +++ b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/recv_stream/enum.ReadExactError.html b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadExactError.html new file mode 100644 index 000000000..5ff8c04ac --- /dev/null +++ b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadExactError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadExactError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html new file mode 100644 index 000000000..0297f93ff --- /dev/null +++ b/pr/234/docs/iroh_quinn/recv_stream/enum.ReadToEndError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ReadToEndError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/recv_stream/enum.ResetError.html b/pr/234/docs/iroh_quinn/recv_stream/enum.ResetError.html new file mode 100644 index 000000000..9f60c8827 --- /dev/null +++ b/pr/234/docs/iroh_quinn/recv_stream/enum.ResetError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.ResetError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/recv_stream/struct.RecvStream.html b/pr/234/docs/iroh_quinn/recv_stream/struct.RecvStream.html new file mode 100644 index 000000000..2842cf45e --- /dev/null +++ b/pr/234/docs/iroh_quinn/recv_stream/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/fn.default_runtime.html b/pr/234/docs/iroh_quinn/runtime/fn.default_runtime.html new file mode 100644 index 000000000..0b519171e --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/fn.default_runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/fn.default_runtime.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html b/pr/234/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html new file mode 100644 index 000000000..1bf6a4945 --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/smol/struct.SmolRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn/struct.SmolRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html b/pr/234/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html new file mode 100644 index 000000000..d4a7961a5 --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/tokio/struct.TokioRuntime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn/struct.TokioRuntime.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/trait.AsyncTimer.html b/pr/234/docs/iroh_quinn/runtime/trait.AsyncTimer.html new file mode 100644 index 000000000..2a738be07 --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/trait.AsyncTimer.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.AsyncTimer.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html b/pr/234/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..47814c9b7 --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/trait.AsyncUdpSocket.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.AsyncUdpSocket.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/trait.Runtime.html b/pr/234/docs/iroh_quinn/runtime/trait.Runtime.html new file mode 100644 index 000000000..bdd2f356e --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/trait.Runtime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.Runtime.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/runtime/trait.UdpSender.html b/pr/234/docs/iroh_quinn/runtime/trait.UdpSender.html new file mode 100644 index 000000000..b63b9c497 --- /dev/null +++ b/pr/234/docs/iroh_quinn/runtime/trait.UdpSender.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/trait.UdpSender.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/send_stream/enum.StoppedError.html b/pr/234/docs/iroh_quinn/send_stream/enum.StoppedError.html new file mode 100644 index 000000000..931afce4d --- /dev/null +++ b/pr/234/docs/iroh_quinn/send_stream/enum.StoppedError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.StoppedError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/send_stream/enum.WriteError.html b/pr/234/docs/iroh_quinn/send_stream/enum.WriteError.html new file mode 100644 index 000000000..b701786e6 --- /dev/null +++ b/pr/234/docs/iroh_quinn/send_stream/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/send_stream/struct.SendStream.html b/pr/234/docs/iroh_quinn/send_stream/struct.SendStream.html new file mode 100644 index 000000000..fec51cd9d --- /dev/null +++ b/pr/234/docs/iroh_quinn/send_stream/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/sidebar-items.js b/pr/234/docs/iroh_quinn/sidebar-items.js new file mode 100644 index 000000000..9f1d6f07e --- /dev/null +++ b/pr/234/docs/iroh_quinn/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ConfigError","ConnectError","ConnectionError","Dir","EcnCodepoint","ReadError","ReadExactError","ReadToEndError","ResetError","SendDatagramError","Side","StoppedError","WriteError"],"fn":["default_runtime"],"mod":["congestion","crypto"],"struct":["Accept","AcceptBi","AcceptUni","AckFrequencyConfig","ApplicationClose","BloomTokenLog","Chunk","ClientConfig","ClosedStream","Connecting","Connection","ConnectionClose","ConnectionId","ConnectionStats","Endpoint","EndpointConfig","EndpointStats","FrameStats","FrameType","IdleTimeout","Incoming","IncomingFuture","MtuDiscoveryConfig","NoneTokenLog","NoneTokenStore","OnClosed","OpenBi","OpenPath","OpenUni","Path","PathId","PathStats","QlogConfig","QlogFileFactory","ReadDatagram","RecvStream","RetryError","SendDatagram","SendStream","ServerConfig","SmolRuntime","StdSystemTime","StreamId","TokenMemoryCache","TokenReuseError","TokioRuntime","Transmit","TransportConfig","TransportErrorCode","UdpStats","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","WeakConnectionHandle","Written","ZeroRttAccepted"],"trait":["AsyncTimer","AsyncUdpSocket","ConnectionIdGenerator","QlogFactory","Runtime","TimeSource","TokenLog","TokenStore","UdpSender"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Accept.html b/pr/234/docs/iroh_quinn/struct.Accept.html new file mode 100644 index 000000000..1f5a6773d --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Accept.html @@ -0,0 +1,43 @@ +Accept in iroh_quinn - Rust

Accept

Struct Accept 

Source
pub struct Accept<'a> { /* private fields */ }
Expand description

Future produced by Endpoint::accept

+

Trait Implementations§

Source§

impl Future for Accept<'_>

Source§

type Output = Option<Incoming>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for Accept<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for Accept<'a>

§

impl<'a> !RefUnwindSafe for Accept<'a>

§

impl<'a> Send for Accept<'a>

§

impl<'a> Sync for Accept<'a>

§

impl<'a> !UnwindSafe for Accept<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.AcceptBi.html b/pr/234/docs/iroh_quinn/struct.AcceptBi.html new file mode 100644 index 000000000..c319c9553 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.AcceptBi.html @@ -0,0 +1,47 @@ +AcceptBi in iroh_quinn - Rust

AcceptBi

Struct AcceptBi 

Source
pub struct AcceptBi<'a> { /* private fields */ }
Expand description

Future produced by Connection::accept_bi

+

Trait Implementations§

Source§

impl Future for AcceptBi<'_>

Source§

type Output = Result<(SendStream, RecvStream), ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for AcceptBi<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for AcceptBi<'a>

§

impl<'a> !RefUnwindSafe for AcceptBi<'a>

§

impl<'a> Send for AcceptBi<'a>

§

impl<'a> Sync for AcceptBi<'a>

§

impl<'a> !UnwindSafe for AcceptBi<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.AcceptUni.html b/pr/234/docs/iroh_quinn/struct.AcceptUni.html new file mode 100644 index 000000000..b6bdf9315 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.AcceptUni.html @@ -0,0 +1,47 @@ +AcceptUni in iroh_quinn - Rust

AcceptUni

Struct AcceptUni 

Source
pub struct AcceptUni<'a> { /* private fields */ }
Expand description

Future produced by Connection::accept_uni

+

Trait Implementations§

Source§

impl Future for AcceptUni<'_>

Source§

type Output = Result<RecvStream, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for AcceptUni<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for AcceptUni<'a>

§

impl<'a> !RefUnwindSafe for AcceptUni<'a>

§

impl<'a> Send for AcceptUni<'a>

§

impl<'a> Sync for AcceptUni<'a>

§

impl<'a> !UnwindSafe for AcceptUni<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.AckFrequencyConfig.html b/pr/234/docs/iroh_quinn/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..4ad4395df --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.AckFrequencyConfig.html @@ -0,0 +1,58 @@ +AckFrequencyConfig in iroh_quinn - Rust

AckFrequencyConfig

Struct AckFrequencyConfig 

Source
pub struct AckFrequencyConfig { /* private fields */ }
Expand description

Parameters for controlling the peer’s acknowledgement frequency

+

The parameters provided in this config will be sent to the peer at the beginning of the +connection, so it can take them into account when sending acknowledgements (see each parameter’s +description for details on how it influences acknowledgement frequency).

+

Quinn’s implementation follows the fourth draft of the +QUIC Acknowledgement Frequency extension. +The defaults produce behavior slightly different than the behavior without this extension, +because they change the way reordered packets are handled (see +AckFrequencyConfig::reordering_threshold for details).

+

Implementations§

Source§

impl AckFrequencyConfig

Source

pub fn ack_eliciting_threshold( + &mut self, + value: VarInt, +) -> &mut AckFrequencyConfig

The ack-eliciting threshold we will request the peer to use

+

This threshold represents the number of ack-eliciting packets an endpoint may receive +without immediately sending an ACK.

+

The remote peer should send at least one ACK frame when more than this number of +ack-eliciting packets have been received. A value of 0 results in a receiver immediately +acknowledging every ack-eliciting packet.

+

Defaults to 1, which sends ACK frames for every other ack-eliciting packet.

+
Source

pub fn max_ack_delay( + &mut self, + value: Option<Duration>, +) -> &mut AckFrequencyConfig

The max_ack_delay we will request the peer to use

+

This parameter represents the maximum amount of time that an endpoint waits before sending +an ACK when the ack-eliciting threshold hasn’t been reached.

+

The effective max_ack_delay will be clamped to be at least the peer’s min_ack_delay +transport parameter, and at most the greater of the current path RTT or 25ms.

+

Defaults to None, in which case the peer’s original max_ack_delay will be used, as +obtained from its transport parameters.

+
Source

pub fn reordering_threshold(&mut self, value: VarInt) -> &mut AckFrequencyConfig

The reordering threshold we will request the peer to use

+

This threshold represents the amount of out-of-order packets that will trigger an endpoint +to send an ACK, without waiting for ack_eliciting_threshold to be exceeded or for +max_ack_delay to be elapsed.

+

A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1 +immediately acknowledges any packets that are received out of order (this is also the +behavior when the extension is disabled).

+

It is recommended to set this value to TransportConfig::packet_threshold minus one. +Since the default value for TransportConfig::packet_threshold is 3, this value defaults +to 2.

+

Trait Implementations§

Source§

impl Clone for AckFrequencyConfig

Source§

fn clone(&self) -> AckFrequencyConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AckFrequencyConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for AckFrequencyConfig

Source§

fn default() -> AckFrequencyConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ApplicationClose.html b/pr/234/docs/iroh_quinn/struct.ApplicationClose.html new file mode 100644 index 000000000..3990e7002 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ApplicationClose.html @@ -0,0 +1,31 @@ +ApplicationClose in iroh_quinn - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+    pub error_code: VarInt,
+    pub reason: Bytes,
+}
Expand description

Reason given by an application for closing the connection

+

Fields§

§error_code: VarInt

Application-specific reason code

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.BloomTokenLog.html b/pr/234/docs/iroh_quinn/struct.BloomTokenLog.html new file mode 100644 index 000000000..233bd74d1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.BloomTokenLog.html @@ -0,0 +1,41 @@ +BloomTokenLog in iroh_quinn - Rust

BloomTokenLog

Struct BloomTokenLog 

Source
pub struct BloomTokenLog(/* private fields */);
Expand description

Bloom filter-based TokenLog

+

Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing +used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter. +This achieves a memory profile of linear growth with an upper bound.

+

Divides time into periods based on lifetime and stores two filters at any given moment, for +each of the two periods currently non-expired tokens could expire in. As such, turns over +filters as time goes on to avoid bloom filter false positive rate increasing infinitely over +time.

+

Implementations§

Source§

impl BloomTokenLog

Source

pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> BloomTokenLog

Construct with an approximate maximum memory usage and expected number of validation token +usages per expiration period

+

Calculates the optimal bloom filter k number automatically.

+
Source

pub fn new(max_bytes: usize, k_num: u32) -> BloomTokenLog

Construct with an approximate maximum memory usage and a bloom filter k number

+

If choosing a custom k number, note that BloomTokenLog always maintains two filters +between them and divides the allocation budget of max_bytes evenly between them. As such, +each bloom filter will contain max_bytes * 4 bits.

+

Trait Implementations§

Source§

impl Default for BloomTokenLog

Default to 20 MiB max memory consumption and expected one million hits

+

With the default validation token lifetime of 2 weeks, this corresponds to one token usage per +1.21 seconds.

+
Source§

fn default() -> BloomTokenLog

Returns the “default value” for a type. Read more
Source§

impl TokenLog for BloomTokenLog

Source§

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Chunk.html b/pr/234/docs/iroh_quinn/struct.Chunk.html new file mode 100644 index 000000000..a1a6b7423 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Chunk.html @@ -0,0 +1,28 @@ +Chunk in iroh_quinn - Rust

Chunk

Struct Chunk 

Source
pub struct Chunk {
+    pub offset: u64,
+    pub bytes: Bytes,
+}
Expand description

A chunk of data from the receive stream

+

Fields§

§offset: u64

The offset in the stream

+
§bytes: Bytes

The contents of the chunk

+

Trait Implementations§

Source§

impl Debug for Chunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Chunk

Source§

fn eq(&self, other: &Chunk) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for Chunk

Source§

impl StructuralPartialEq for Chunk

Auto Trait Implementations§

§

impl !Freeze for Chunk

§

impl RefUnwindSafe for Chunk

§

impl Send for Chunk

§

impl Sync for Chunk

§

impl Unpin for Chunk

§

impl UnwindSafe for Chunk

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ClientConfig.html b/pr/234/docs/iroh_quinn/struct.ClientConfig.html new file mode 100644 index 000000000..0432b1b14 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ClientConfig.html @@ -0,0 +1,43 @@ +ClientConfig in iroh_quinn - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> ClientConfig

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut ClientConfig

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config( + &mut self, + transport: Arc<TransportConfig>, +) -> &mut ClientConfig

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut ClientConfig

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut ClientConfig

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn with_platform_verifier() -> ClientConfig

👎Deprecated since 0.11.13: use try_with_platform_verifier() instead

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn try_with_platform_verifier() -> Result<ClientConfig, Error>

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<ClientConfig, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ClosedStream.html b/pr/234/docs/iroh_quinn/struct.ClosedStream.html new file mode 100644 index 000000000..74ec06833 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ClosedStream.html @@ -0,0 +1,26 @@ +ClosedStream in iroh_quinn - Rust

ClosedStream

Struct ClosedStream 

Source
pub struct ClosedStream { /* private fields */ }
Expand description

Error indicating that a stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for ClosedStream

Source§

fn clone(&self) -> ClosedStream

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedStream

Source§

fn default() -> ClosedStream

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedStream

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedStream

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for WriteError

Source§

fn from(_: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ClosedStream

Source§

fn eq(&self, other: &ClosedStream) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedStream

Source§

impl StructuralPartialEq for ClosedStream

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.Connecting.html b/pr/234/docs/iroh_quinn/struct.Connecting.html new file mode 100644 index 000000000..f91ead67b --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Connecting.html @@ -0,0 +1,94 @@ +Connecting in iroh_quinn - Rust

Connecting

Struct Connecting 

Source
pub struct Connecting { /* private fields */ }
Expand description

In-progress connection attempt future

+

Implementations§

Source§

impl Connecting

Source

pub fn into_0rtt(self) -> Result<(Connection, ZeroRttAccepted), Self>

Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security

+

Returns Ok immediately if the local endpoint is able to attempt sending 0/0.5-RTT data. +If so, the returned Connection can be used to send application data without waiting for +the rest of the handshake to complete, at the cost of weakened cryptographic security +guarantees. The returned ZeroRttAccepted future resolves when the handshake does +complete, at which point subsequently opened streams and written data will have full +cryptographic protection.

+
§Outgoing
+

For outgoing connections, the initial attempt to convert to a Connection which sends +0-RTT data will proceed if the crypto::ClientConfig +attempts to resume a previous TLS session. However, the remote endpoint may not actually +accept the 0-RTT data–yet still accept the connection attempt in general. This +possibility is conveyed through the ZeroRttAccepted future–when the handshake +completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected. +If it was rejected, the existence of streams opened and other application data sent prior +to the handshake completing will not be conveyed to the remote application, and local +operations on them will return ZeroRttRejected errors.

+

A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the +relevant resumption state to be stored in the server, which servers may limit or lose for +various reasons including not persisting resumption state across server restarts.

+

If manually providing a crypto::ClientConfig, check your +implementation’s docs for 0-RTT pitfalls.

+
§Incoming
+

For incoming connections, conversion to 0.5-RTT will always fully succeed. into_0rtt will +always return Ok and the ZeroRttAccepted will always resolve to true.

+

If manually providing a crypto::ServerConfig, check your +implementation’s docs for 0-RTT pitfalls.

+
§Security
+

On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to +replay attacks, and should therefore never invoke non-idempotent operations.

+

On incoming connections, this enables transmission of 0.5-RTT data, which may be sent +before TLS client authentication has occurred, and should therefore not be used to send +data for which client authentication is being used.

+
Source

pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError>

Parameters negotiated during the handshake

+

The dynamic type returned is determined by the configured +Session. For the default rustls session, the return value can +be downcast to a +crypto::rustls::HandshakeData.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established +the connection

+

This can be different from the address the endpoint is bound to, in case +the endpoint is bound to a wildcard address like 0.0.0.0 or ::.

+

This will return None for clients, or when the platform does not expose this +information. See quinn_udp::RecvMeta::dst_ip for a list of +supported platforms when using quinn_udp for I/O, which is the default.

+

Will panic if called after poll has returned Ready.

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+

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<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Connection.html b/pr/234/docs/iroh_quinn/struct.Connection.html new file mode 100644 index 000000000..93064a0e9 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Connection.html @@ -0,0 +1,209 @@ +Connection in iroh_quinn - Rust

Connection

Struct Connection 

Source
pub struct Connection(/* private fields */);
Expand description

A QUIC connection.

+

If all references to a connection (including every clone of the Connection handle, streams of +incoming streams, and the various stream types) have been dropped, then the connection will be +automatically closed with an error_code of 0 and an empty reason. You can also close the +connection explicitly by calling Connection::close().

+

Closing the connection immediately abandons efforts to deliver data to the peer. Upon +receiving CONNECTION_CLOSE the peer may drop any stream data not yet delivered to the +application. Connection::close() describes in more detail how to gracefully close a +connection without losing application data.

+

May be cloned to obtain another handle to the same connection.

+

Implementations§

Source§

impl Connection

Source

pub fn weak_handle(&self) -> WeakConnectionHandle

Returns a weak reference to the inner connection struct.

+
Source

pub fn open_uni(&self) -> OpenUni<'_>

Initiate a new outgoing unidirectional stream.

+

Streams are cheap and instantaneous to open unless blocked by flow control. As a +consequence, the peer won’t be notified that a stream has been opened until the stream is +actually used.

+
Source

pub fn open_bi(&self) -> OpenBi<'_>

Initiate a new outgoing bidirectional stream.

+

Streams are cheap and instantaneous to open unless blocked by flow control. As a +consequence, the peer won’t be notified that a stream has been opened until the stream is +actually used. Calling open_bi() then waiting on the RecvStream without writing +anything to SendStream will never succeed.

+
Source

pub fn accept_uni(&self) -> AcceptUni<'_>

Accept the next incoming uni-directional stream

+
Source

pub fn accept_bi(&self) -> AcceptBi<'_>

Accept the next incoming bidirectional stream

+

Important Note: The Connection that calls open_bi() must write to its SendStream +before the other Connection is able to accept_bi(). Calling open_bi() then +waiting on the RecvStream without writing anything to SendStream will never succeed.

+
Source

pub fn read_datagram(&self) -> ReadDatagram<'_>

Receive an application datagram

+
Source

pub fn open_path_ensure( + &self, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens a new path if no path exists yet for the remote address.

+

Otherwise behaves exactly as open_path.

+
Source

pub fn open_path( + &self, + addr: SocketAddr, + initial_status: PathStatus, +) -> OpenPath

Opens an additional path if the multipath extension is negotiated.

+

The returned future completes once the path is either fully opened and ready to +carry application data, or if there was an error.

+

Dropping the returned future does not cancel the opening of the path, the +PathEvent::Opened event will still be emitted from Self::path_events if the +path opens. The PathId for the events can be extracted from +OpenPath::path_id.

+

Failure to open a path can either occur immediately, before polling the returned +future, or at a later time. If the failure is immediate OpenPath::path_id will +return None and the future will be ready immediately. If the failure happens +later, a PathEvent will be emitted.

+
Source

pub fn path(&self, id: PathId) -> Option<Path>

Returns the Path structure of an open path

+
Source

pub fn path_events(&self) -> Receiver<PathEvent>

A broadcast receiver of PathEvents for all paths in this connection

+
Source

pub fn nat_traversal_updates(&self) -> Receiver<Event>

A broadcast receiver of iroh_hp::Events for updates about server addresses

+
Source

pub async fn closed(&self) -> ConnectionError

Wait for the connection to be closed for any reason

+

Despite the return type’s name, closed connections are often not an error condition at the +application layer. Cases that might be routine include ConnectionError::LocallyClosed +and ConnectionError::ApplicationClosed.

+
Source

pub fn on_closed(&self) -> OnClosed

Wait for the connection to be closed without keeping a strong reference to the connection

+

Returns a future that resolves, once the connection is closed, to a tuple of +(ConnectionError, ConnectionStats).

+

Calling Self::closed keeps the connection alive until it is either closed locally via Connection::close +or closed by the remote peer. This function instead does not keep the connection itself alive, +so if all other clones of the connection are dropped, the connection will be closed implicitly even +if there are futures returned from this function still being awaited.

+
Source

pub fn close_reason(&self) -> Option<ConnectionError>

If the connection is closed, the reason why.

+

Returns None if the connection is still open.

+
Source

pub fn close(&self, error_code: VarInt, reason: &[u8])

Close the connection immediately.

+

Pending operations will fail immediately with ConnectionError::LocallyClosed. No +more data is sent to the peer and the peer may drop buffered data upon receiving +the CONNECTION_CLOSE frame.

+

error_code and reason are not interpreted, and are provided directly to the peer.

+

reason will be truncated to fit in a single packet with overhead; to improve odds that it +is preserved in full, it should be kept under 1KiB.

+
§Gracefully closing a connection
+

Only the peer last receiving application data can be certain that all data is +delivered. The only reliable action it can then take is to close the connection, +potentially with a custom error code. The delivery of the final CONNECTION_CLOSE +frame is very likely if both endpoints stay online long enough, and +Endpoint::wait_idle() can be used to provide sufficient time. Otherwise, the +remote peer will time out the connection, provided that the idle timeout is not +disabled.

+

The sending side can not guarantee all stream data is delivered to the remote +application. It only knows the data is delivered to the QUIC stack of the remote +endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling +close() the remote endpoint may drop any data it received but is as yet +undelivered to the application, including data that was acknowledged as received to +the local endpoint.

+
Source

pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError>

Wait for the handshake to be confirmed.

+

As a server, who must be authenticated by clients, +this happens when the handshake completes +upon receiving a TLS Finished message from the client. +In return, the server send a HANDSHAKE_DONE frame.

+

As a client, this happens when receiving a HANDSHAKE_DONE frame. +At this point, the server has either accepted our authentication, +or, if client authentication is not required, accepted our lack of authentication.

+
Source

pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError>

Transmit data as an unreliable, unordered application datagram

+

Application datagrams are a low-level primitive. They may be lost or delivered out of order, +and data must both fit inside a single QUIC packet and be smaller than the maximum +dictated by the peer.

+

Previously queued datagrams which are still unsent may be discarded to make space for this +datagram, in order of oldest to newest.

+
Source

pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_>

Transmit data as an unreliable, unordered application datagram

+

Unlike send_datagram(), this method will wait for buffer space during congestion +conditions, which effectively prioritizes old datagrams over new datagrams.

+

See send_datagram() for details.

+
Source

pub fn max_datagram_size(&self) -> Option<usize>

Compute the maximum size of datagrams that may be passed to send_datagram().

+

Returns None if datagrams are unsupported by the peer or disabled locally.

+

This may change over the lifetime of a connection according to variation in the path MTU +estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer’s +limit is large this is guaranteed to be a little over a kilobyte at minimum.

+

Not necessarily the maximum size of received datagrams.

+
Source

pub fn datagram_send_buffer_space(&self) -> usize

Bytes available in the outgoing datagram buffer

+

When greater than zero, calling send_datagram() with a datagram of +at most this size is guaranteed not to cause older datagrams to be dropped.

+
Source

pub fn side(&self) -> Side

The side of the connection (client or server)

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+

If ServerConfig::migration is true, clients may change addresses at will, +e.g. when switching to a cellular internet connection.

+

If multipath is enabled this will return the address of any +path, and may not be consistent. Prefer Path::remote_address instead.

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established +the connection

+

This can be different from the address the endpoint is bound to, in case +the endpoint is bound to a wildcard address like 0.0.0.0 or ::.

+

This will return None for clients, or when the platform does not expose this +information. See quinn_udp::RecvMeta::dst_ip for a list of +supported platforms when using quinn_udp for I/O, which is the default.

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn stats(&self) -> ConnectionStats

Returns connection statistics

+
Source

pub fn path_stats(&self, path_id: PathId) -> Option<PathStats>

Returns path statistics

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>>

Current state of the congestion control algorithm, for debugging purposes

+
Source

pub fn handshake_data(&self) -> Option<Box<dyn Any>>

Parameters negotiated during the handshake

+

Guaranteed to return Some on fully established connections or after +Connecting::handshake_data() succeeds. See that method’s documentations for details on +the returned value.

+
Source

pub fn peer_identity(&self) -> Option<Box<dyn Any>>

Cryptographic identity of the peer

+

The dynamic type returned is determined by the configured +Session. For the default rustls session, the return value can +be downcast to a Vec<[rustls::pki_types::CertificateDer]>

+
Source

pub fn stable_id(&self) -> usize

A stable identifier for this connection

+

Peer addresses and connection IDs can change, but this value will remain +fixed for the lifetime of the connection.

+
Source

pub fn force_key_update(&self)

Update traffic keys spontaneously

+

This primarily exists for testing purposes.

+
Source

pub fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Derive keying material from this connection’s TLS session secrets.

+

When both peers call this method with the same label and context +arguments and output buffers of equal length, they will get the +same sequence of bytes in output. These bytes are cryptographically +strong and pseudorandom, and are suitable for use as keying material.

+

See RFC5705 for more information.

+
Source

pub fn set_max_concurrent_uni_streams(&self, count: VarInt)

Modify the number of remotely initiated unidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_send_window(&self, send_window: u64)

Source

pub fn set_receive_window(&self, receive_window: VarInt)

Source

pub fn set_max_concurrent_bi_streams(&self, count: VarInt)

Modify the number of remotely initiated bidirectional streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn observed_external_addr(&self) -> Receiver<Option<SocketAddr>>

Track changed on our external address as reported by the peer.

+
Source

pub fn is_multipath_enabled(&self) -> bool

Is multipath enabled?

+
Source

pub fn add_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Registers one address at which this endpoint might be reachable

+

When the NAT traversal extension is negotiated, servers send these addresses to clients in +ADD_ADDRESS frames. This allows clients to obtain server address candidates to initiate +NAT traversal attempts. Clients provide their own reachable addresses in REACH_OUT frames +when Self::initiate_nat_traversal_round is called.

+
Source

pub fn remove_nat_traversal_address( + &self, + address: SocketAddr, +) -> Result<(), Error>

Removes one or more addresses from the set of addresses at which this endpoint is reachable

+

When the NAT traversal extension is negotiated, servers send address removals to +clients in REMOVE_ADDRESS frames. This allows clients to stop using outdated +server address candidates that are no longer valid for NAT traversal.

+

For clients, removed addresses will no longer be advertised in REACH_OUT frames.

+

Addresses not present in the set will be silently ignored.

+
Source

pub fn get_local_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

+
Source

pub fn get_remote_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

+
Source

pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

+

A nat traversal round involves advertising the client’s local addresses in REACH_OUT +frames, and initiating probing of the known remote addresses. When a new round is +initiated, the previous one is cancelled, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed.

+

Trait Implementations§

Source§

impl Clone for Connection

Source§

fn clone(&self) -> Connection

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ConnectionClose.html b/pr/234/docs/iroh_quinn/struct.ConnectionClose.html new file mode 100644 index 000000000..a822d589d --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ConnectionClose.html @@ -0,0 +1,33 @@ +ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+    pub error_code: Code,
+    pub frame_type: Option<FrameType>,
+    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: Option<FrameType>

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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.ConnectionId.html b/pr/234/docs/iroh_quinn/struct.ConnectionId.html new file mode 100644 index 000000000..34fdf0a8f --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ConnectionId.html @@ -0,0 +1,2462 @@ +ConnectionId in iroh_quinn - Rust

ConnectionId

Struct ConnectionId 

Source
pub struct ConnectionId { /* private fields */ }
Expand description

Protocol-level identifier for a connection.

+

Mainly useful for identifying this connection’s packets on the wire with tools like Wireshark.

+

Implementations§

Source§

impl ConnectionId

Source

pub fn new(bytes: &[u8]) -> ConnectionId

Construct cid from byte array

+
Source

pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> ConnectionId

Constructs cid by reading len bytes from a Buf

+

Callers need to assure that buf.remaining() >= len

+

Methods from Deref<Target = [u8]>§

1.23.0 · Source

pub fn is_ascii(&self) -> bool

Checks if all bytes in this slice are within the ASCII range.

+
Source

pub fn as_ascii(&self) -> Option<&[AsciiChar]>

🔬This is a nightly-only experimental API. (ascii_char)

If this slice is_ascii, returns it as a slice of +ASCII characters, otherwise returns None.

+
Source

pub unsafe fn as_ascii_unchecked(&self) -> &[AsciiChar]

🔬This is a nightly-only experimental API. (ascii_char)

Converts this slice of bytes into a slice of ASCII characters, +without checking whether they’re valid.

+
§Safety
+

Every byte in the slice must be in 0..=127, or else this is UB.

+
1.23.0 · Source

pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool

Checks that two slices are an ASCII case-insensitive match.

+

Same as to_ascii_lowercase(a) == to_ascii_lowercase(b), +but without allocating and copying temporaries.

+
1.23.0 · Source

pub fn make_ascii_uppercase(&mut self)

Converts this slice to its ASCII upper case equivalent in-place.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To return a new uppercased value without modifying the existing one, use +to_ascii_uppercase.

+
1.23.0 · Source

pub fn make_ascii_lowercase(&mut self)

Converts this slice to its ASCII lower case equivalent in-place.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To return a new lowercased value without modifying the existing one, use +to_ascii_lowercase.

+
1.60.0 · Source

pub fn escape_ascii(&self) -> EscapeAscii<'_>

Returns an iterator that produces an escaped version of this slice, +treating it as an ASCII string.

+
§Examples
+
let s = b"0\t\r\n'\"\\\x9d";
+let escaped = s.escape_ascii().to_string();
+assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
1.80.0 · Source

pub fn trim_ascii_start(&self) -> &[u8]

Returns a byte slice with leading ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
+assert_eq!(b"  ".trim_ascii_start(), b"");
+assert_eq!(b"".trim_ascii_start(), b"");
1.80.0 · Source

pub fn trim_ascii_end(&self) -> &[u8]

Returns a byte slice with trailing ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
+assert_eq!(b"  ".trim_ascii_end(), b"");
+assert_eq!(b"".trim_ascii_end(), b"");
1.80.0 · Source

pub fn trim_ascii(&self) -> &[u8]

Returns a byte slice with leading and trailing ASCII whitespace bytes +removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
+assert_eq!(b"  ".trim_ascii(), b"");
+assert_eq!(b"".trim_ascii(), b"");
1.0.0 · Source

pub fn len(&self) -> usize

Returns the number of elements in the slice.

+
§Examples
+
let a = [1, 2, 3];
+assert_eq!(a.len(), 3);
1.0.0 · Source

pub fn is_empty(&self) -> bool

Returns true if the slice has a length of 0.

+
§Examples
+
let a = [1, 2, 3];
+assert!(!a.is_empty());
+
+let b: &[i32] = &[];
+assert!(b.is_empty());
1.0.0 · Source

pub fn first(&self) -> Option<&T>

Returns the first element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&10), v.first());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.first());
1.0.0 · Source

pub fn first_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the first element of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_mut() {
+    *first = 5;
+}
+assert_eq!(x, &[5, 1, 2]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.first_mut());
1.5.0 · Source

pub fn split_first(&self) -> Option<(&T, &[T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first() {
+    assert_eq!(first, &0);
+    assert_eq!(elements, &[1, 2]);
+}
1.5.0 · Source

pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_mut() {
+    *first = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
1.5.0 · Source

pub fn split_last(&self) -> Option<(&T, &[T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((last, elements)) = x.split_last() {
+    assert_eq!(last, &2);
+    assert_eq!(elements, &[0, 1]);
+}
1.5.0 · Source

pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((last, elements)) = x.split_last_mut() {
+    *last = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[4, 5, 3]);
1.0.0 · Source

pub fn last(&self) -> Option<&T>

Returns the last element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&30), v.last());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.last());
1.0.0 · Source

pub fn last_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the last item in the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_mut() {
+    *last = 10;
+}
+assert_eq!(x, &[0, 1, 10]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.last_mut());
1.77.0 · Source

pub fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.first_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.first_chunk::<0>());
1.77.0 · Source

pub fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_chunk_mut::<2>() {
+    first[0] = 5;
+    first[1] = 4;
+}
+assert_eq!(x, &[5, 4, 2]);
+
+assert_eq!(None, x.first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>

Returns an array reference to the first N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk::<2>() {
+    assert_eq!(first, &[0, 1]);
+    assert_eq!(elements, &[2]);
+}
+
+assert_eq!(None, x.split_first_chunk::<4>());
1.77.0 · Source

pub fn split_first_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T; N], &mut [T])>

Returns a mutable array reference to the first N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
+    first[0] = 3;
+    first[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
+
+assert_eq!(None, x.split_first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])>

Returns an array reference to the last N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk::<2>() {
+    assert_eq!(elements, &[0]);
+    assert_eq!(last, &[1, 2]);
+}
+
+assert_eq!(None, x.split_last_chunk::<4>());
1.77.0 · Source

pub fn split_last_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T], &mut [T; N])>

Returns a mutable array reference to the last N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
+    last[0] = 3;
+    last[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[5, 3, 4]);
+
+assert_eq!(None, x.split_last_chunk_mut::<4>());
1.77.0 · Source

pub fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.last_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.last_chunk::<0>());
1.77.0 · Source

pub fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_chunk_mut::<2>() {
+    last[0] = 10;
+    last[1] = 20;
+}
+assert_eq!(x, &[0, 10, 20]);
+
+assert_eq!(None, x.last_chunk_mut::<4>());
1.0.0 · Source

pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice depending on the type of +index.

+
    +
  • If given a position, returns a reference to the element at that +position or None if out of bounds.
  • +
  • If given a range, returns the subslice corresponding to that range, +or None if out of bounds.
  • +
+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&40), v.get(1));
+assert_eq!(Some(&[10, 40][..]), v.get(0..2));
+assert_eq!(None, v.get(3));
+assert_eq!(None, v.get(0..4));
1.0.0 · Source

pub fn get_mut<I>( + &mut self, + index: I, +) -> Option<&mut <I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice depending on the +type of index (see get) or None if the index is out of bounds.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(elem) = x.get_mut(1) {
+    *elem = 42;
+}
+assert_eq!(x, &[0, 42, 2]);
1.0.0 · Source

pub unsafe fn get_unchecked<I>( + &self, + index: I, +) -> &<I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice, without doing bounds +checking.

+

For a safe alternative see get.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get(index).unwrap_unchecked(). It’s UB +to call .get_unchecked(len), even if you immediately convert to a +pointer. And it’s UB to call .get_unchecked(..len + 1), +.get_unchecked(..=len), or similar.

+
§Examples
+
let x = &[1, 2, 4];
+
+unsafe {
+    assert_eq!(x.get_unchecked(1), &2);
+}
1.0.0 · Source

pub unsafe fn get_unchecked_mut<I>( + &mut self, + index: I, +) -> &mut <I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice, without doing +bounds checking.

+

For a safe alternative see get_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get_mut(index).unwrap_unchecked(). It’s +UB to call .get_unchecked_mut(len), even if you immediately convert +to a pointer. And it’s UB to call .get_unchecked_mut(..len + 1), +.get_unchecked_mut(..=len), or similar.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let elem = x.get_unchecked_mut(1);
+    *elem = 13;
+}
+assert_eq!(x, &[1, 13, 4]);
1.0.0 · Source

pub fn as_ptr(&self) -> *const T

Returns a raw pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

The caller must also ensure that the memory the pointer (non-transitively) points to +is never written to (except inside an UnsafeCell) using this pointer or any pointer +derived from it. If you need to mutate the contents of the slice, use as_mut_ptr.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &[1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        assert_eq!(x.get_unchecked(i), &*x_ptr.add(i));
+    }
+}
1.0.0 · Source

pub fn as_mut_ptr(&mut self) -> *mut T

Returns an unsafe mutable pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &mut [1, 2, 4];
+let x_ptr = x.as_mut_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        *x_ptr.add(i) += 2;
+    }
+}
+assert_eq!(x, &[3, 4, 6]);
1.48.0 · Source

pub fn as_ptr_range(&self) -> Range<*const T>

Returns the two raw pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_ptr for warnings on using these pointers. The end pointer +requires extra caution, as it does not point to a valid element in the +slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+

It can also be useful to check if a pointer to an element refers to an +element of this slice:

+ +
let a = [1, 2, 3];
+let x = &a[1] as *const _;
+let y = &5 as *const _;
+
+assert!(a.as_ptr_range().contains(&x));
+assert!(!a.as_ptr_range().contains(&y));
1.48.0 · Source

pub fn as_mut_ptr_range(&mut self) -> Range<*mut T>

Returns the two unsafe mutable pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_mut_ptr for warnings on using these pointers. The end +pointer requires extra caution, as it does not point to a valid element +in the slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+
Source

pub fn as_array<const N: usize>(&self) -> Option<&[T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a reference to the underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
Source

pub fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a mutable reference to the slice’s underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
1.0.0 · Source

pub fn swap(&mut self, a: usize, b: usize)

Swaps two elements in the slice.

+

If a equals to b, it’s guaranteed that elements won’t change value.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Panics
+

Panics if a or b are out of bounds.

+
§Examples
+
let mut v = ["a", "b", "c", "d", "e"];
+v.swap(2, 4);
+assert!(v == ["a", "b", "e", "d", "c"]);
Source

pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize)

🔬This is a nightly-only experimental API. (slice_swap_unchecked)

Swaps two elements in the slice, without doing bounds checking.

+

For a safe alternative see swap.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior. +The caller has to ensure that a < self.len() and b < self.len().

+
§Examples
+
#![feature(slice_swap_unchecked)]
+
+let mut v = ["a", "b", "c", "d"];
+// SAFETY: we know that 1 and 3 are both indices of the slice
+unsafe { v.swap_unchecked(1, 3) };
+assert!(v == ["a", "d", "c", "b"]);
1.0.0 · Source

pub fn reverse(&mut self)

Reverses the order of elements in the slice, in place.

+
§Examples
+
let mut v = [1, 2, 3];
+v.reverse();
+assert!(v == [3, 2, 1]);
1.0.0 · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the slice.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &[1, 2, 4];
+let mut iterator = x.iter();
+
+assert_eq!(iterator.next(), Some(&1));
+assert_eq!(iterator.next(), Some(&2));
+assert_eq!(iterator.next(), Some(&4));
+assert_eq!(iterator.next(), None);
1.0.0 · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns an iterator that allows modifying each value.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &mut [1, 2, 4];
+for elem in x.iter_mut() {
+    *elem += 2;
+}
+assert_eq!(x, &[3, 4, 6]);
1.0.0 · Source

pub fn windows(&self, size: usize) -> Windows<'_, T>

Returns an iterator over all contiguous windows of length +size. The windows overlap. If the slice is shorter than +size, the iterator returns no values.

+
§Panics
+

Panics if size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.windows(3);
+assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
+assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
+assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
+assert!(iter.next().is_none());
+

If the slice is shorter than size:

+ +
let slice = ['f', 'o', 'o'];
+let mut iter = slice.windows(4);
+assert!(iter.next().is_none());
+

Because the Iterator trait cannot represent the required lifetimes, +there is no windows_mut analog to windows; +[0,1,2].windows_mut(2).collect() would violate the rules of references +(though a LendingIterator analog is possible). You can sometimes use +Cell::as_slice_of_cells in +conjunction with windows instead:

+ +
use std::cell::Cell;
+
+let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+let slice = &mut array[..];
+let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+for w in slice_of_cells.windows(3) {
+    Cell::swap(&w[0], &w[2]);
+}
+assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
1.0.0 · Source

pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See chunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and rchunks for the same iterator but starting at the end of the +slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert_eq!(iter.next().unwrap(), &['m']);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See chunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and rchunks_mut for the same iterator but starting at +the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 3]);
1.31.0 · Source

pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks.

+

See chunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and rchunks_exact for the same iterator but starting at the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
1.31.0 · Source

pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See chunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and rchunks_exact_mut for the same iterator but starting at the end of +the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened.

+

As this is unsafe, consider whether you could use as_chunks or +as_rchunks instead, perhaps via something like +if let (chunks, []) = slice.as_chunks() or +let (chunks, []) = slice.as_chunks() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &[[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &[[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (chunks, remainder) = slice.as_chunks();
+assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
+assert_eq!(remainder, &['m']);
+

If you expect the slice to be an exact multiple, you can combine +let-else with an empty slice pattern:

+ +
let slice = ['R', 'u', 's', 't'];
+let (chunks, []) = slice.as_chunks::<2>() else {
+    panic!("slice didn't have even length")
+};
+assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
1.88.0 · Source

pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (remainder, chunks) = slice.as_rchunks();
+assert_eq!(remainder, &['l']);
+assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked_mut<const N: usize>( + &mut self, +) -> &mut [[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened_mut.

+

As this is unsafe, consider whether you could use as_chunks_mut or +as_rchunks_mut instead, perhaps via something like +if let (chunks, []) = slice.as_chunks_mut() or +let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &mut [[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[0] = ['L'];
+assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &mut [[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[1] = ['a', 'x', '?'];
+assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks_mut(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (chunks, remainder) = v.as_chunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 9]);
1.88.0 · Source

pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks_mut(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (remainder, chunks) = v.as_rchunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[9, 1, 1, 2, 2]);
Source

pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N>

🔬This is a nightly-only experimental API. (array_windows)

Returns an iterator over overlapping windows of N elements of a slice, +starting at the beginning of the slice.

+

This is the const generic equivalent of windows.

+

If N is greater than the size of the slice, it will return no windows.

+
§Panics
+

Panics if N is zero. This check will most probably get changed to a compile time +error before this method gets stabilized.

+
§Examples
+
#![feature(array_windows)]
+let slice = [0, 1, 2, 3];
+let mut iter = slice.array_windows();
+assert_eq!(iter.next().unwrap(), &[0, 1]);
+assert_eq!(iter.next().unwrap(), &[1, 2]);
+assert_eq!(iter.next().unwrap(), &[2, 3]);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and chunks for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert_eq!(iter.next().unwrap(), &['l']);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and chunks_mut for the same iterator but starting at the +beginning of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[3, 2, 2, 1, 1]);
1.31.0 · Source

pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +end of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of rchunks.

+

See rchunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and chunks_exact for the same iterator but starting at the beginning of the +slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['l']);
1.31.0 · Source

pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See rchunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and chunks_exact_mut for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[0, 2, 2, 1, 1]);
1.77.0 · Source

pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping runs +of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&[3, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.77.0 · Source

pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping mutable +runs of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by_mut(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by_mut(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.0.0 · Source

pub fn split_at(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_checked.

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+{
+   let (left, right) = v.split_at(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+{
+    let (left, right) = v.split_at(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+{
+    let (left, right) = v.split_at(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.0.0 · Source

pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_mut_checked.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+let (left, right) = v.split_at_mut(2);
+assert_eq!(left, [1, 0]);
+assert_eq!(right, [3, 0, 5, 6]);
+left[1] = 2;
+right[1] = 4;
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.79.0 · Source

pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+unsafe {
+   let (left, right) = v.split_at_unchecked(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.79.0 · Source

pub unsafe fn split_at_mut_unchecked( + &mut self, + mid: usize, +) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+// scoped to restrict the lifetime of the borrows
+unsafe {
+    let (left, right) = v.split_at_mut_unchecked(2);
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.80.0 · Source

pub fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])>

Divides one slice into two at an index, returning None if the slice is +too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let v = [1, -2, 3, -4, 5, -6];
+
+{
+   let (left, right) = v.split_at_checked(0).unwrap();
+   assert_eq!(left, []);
+   assert_eq!(right, [1, -2, 3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(2).unwrap();
+    assert_eq!(left, [1, -2]);
+    assert_eq!(right, [3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(6).unwrap();
+    assert_eq!(left, [1, -2, 3, -4, 5, -6]);
+    assert_eq!(right, []);
+}
+
+assert_eq!(None, v.split_at_checked(7));
1.80.0 · Source

pub fn split_at_mut_checked( + &mut self, + mid: usize, +) -> Option<(&mut [T], &mut [T])>

Divides one mutable slice into two at an index, returning None if the +slice is too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+
+if let Some((left, right)) = v.split_at_mut_checked(2) {
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
+
+assert_eq!(None, v.split_at_mut_checked(7));
1.0.0 · Source

pub fn split<F>(&self, pred: F) -> Split<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is not contained in the subslices.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the first element is matched, an empty slice will be the first item +returned by the iterator. Similarly, if the last element in the slice +is matched, an empty slice will be the last item returned by the +iterator:

+ +
let slice = [10, 40, 33];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert!(iter.next().is_none());
+

If two matched elements are directly adjacent, an empty slice will be +present between them:

+ +
let slice = [10, 6, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_mut(|num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 1]);
1.51.0 · Source

pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is contained in the end of the previous +subslice as a terminator.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the last element of the slice is matched, +that element will be considered the terminator of the preceding slice. +That slice will be the last item returned by the iterator.

+ +
let slice = [3, 10, 40, 33];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[3]);
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert!(iter.next().is_none());
1.51.0 · Source

pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is contained in the previous +subslice as a terminator.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
+    let terminator_idx = group.len()-1;
+    group[terminator_idx] = 1;
+}
+assert_eq!(v, [10, 40, 1, 20, 1, 1]);
1.27.0 · Source

pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, starting at the end of the slice and working backwards. +The matched element is not contained in the subslices.

+
§Examples
+
let slice = [11, 22, 33, 0, 44, 55];
+let mut iter = slice.rsplit(|num| *num == 0);
+
+assert_eq!(iter.next().unwrap(), &[44, 55]);
+assert_eq!(iter.next().unwrap(), &[11, 22, 33]);
+assert_eq!(iter.next(), None);
+

As with split(), if the first or last element is matched, an empty +slice will be the first (or last) item returned by the iterator.

+ +
let v = &[0, 1, 1, 2, 3, 5, 8];
+let mut it = v.rsplit(|n| *n % 2 == 0);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next().unwrap(), &[3, 5]);
+assert_eq!(it.next().unwrap(), &[1, 1]);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next(), None);
1.27.0 · Source

pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred, starting at the end of the slice and working +backwards. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [100, 400, 300, 200, 600, 500];
+
+let mut count = 0;
+for group in v.rsplit_mut(|num| *num % 3 == 0) {
+    count += 1;
+    group[0] = count;
+}
+assert_eq!(v, [3, 400, 300, 2, 600, 1]);
1.0.0 · Source

pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once by numbers divisible by 3 (i.e., [10, 40], +[20, 60, 50]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 50]);
1.0.0 · Source

pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once, starting from the end, by numbers divisible +by 3 (i.e., [50], [10, 40, 30, 20]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.rsplitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut s = [10, 40, 30, 20, 60, 50];
+
+for group in s.rsplitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(s, [1, 40, 30, 20, 60, 1]);
Source

pub fn split_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the first element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.split_once(|&x| x == 2), Some((
+    &[1][..],
+    &[3, 2, 4][..]
+)));
+assert_eq!(s.split_once(|&x| x == 0), None);
Source

pub fn rsplit_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the last element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.rsplit_once(|&x| x == 2), Some((
+    &[1, 2, 3][..],
+    &[4][..]
+)));
+assert_eq!(s.rsplit_once(|&x| x == 0), None);
1.0.0 · Source

pub fn contains(&self, x: &T) -> bool
where + T: PartialEq,

Returns true if the slice contains an element with the given value.

+

This operation is O(n).

+

Note that if you have a sorted slice, binary_search may be faster.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.contains(&30));
+assert!(!v.contains(&50));
+

If you do not have a &T, but some other value that you can compare +with one (for example, String implements PartialEq<str>), you can +use iter().any:

+ +
let v = [String::from("hello"), String::from("world")]; // slice of `String`
+assert!(v.iter().any(|e| e == "hello")); // search with `&str`
+assert!(!v.iter().any(|e| e == "hi"));
1.0.0 · Source

pub fn starts_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a prefix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.starts_with(&[10]));
+assert!(v.starts_with(&[10, 40]));
+assert!(v.starts_with(&v));
+assert!(!v.starts_with(&[50]));
+assert!(!v.starts_with(&[10, 50]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.starts_with(&[]));
+let v: &[u8] = &[];
+assert!(v.starts_with(&[]));
1.0.0 · Source

pub fn ends_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a suffix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.ends_with(&[30]));
+assert!(v.ends_with(&[40, 30]));
+assert!(v.ends_with(&v));
+assert!(!v.ends_with(&[50]));
+assert!(!v.ends_with(&[50, 30]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.ends_with(&[]));
+let v: &[u8] = &[];
+assert!(v.ends_with(&[]));
1.51.0 · Source

pub fn strip_prefix<P>(&self, prefix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix, wrapped in Some. +If prefix is empty, simply returns the original slice. If prefix is equal to the +original slice, returns an empty slice.

+

If the slice does not start with prefix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..]));
+assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..]));
+assert_eq!(v.strip_prefix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_prefix(&[50]), None);
+assert_eq!(v.strip_prefix(&[10, 50]), None);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".strip_prefix(prefix.as_bytes()),
+           Some(b"llo".as_ref()));
1.51.0 · Source

pub fn strip_suffix<P>(&self, suffix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix, wrapped in Some. +If suffix is empty, simply returns the original slice. If suffix is equal to the +original slice, returns an empty slice.

+

If the slice does not end with suffix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..]));
+assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..]));
+assert_eq!(v.strip_suffix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_suffix(&[50]), None);
+assert_eq!(v.strip_suffix(&[50, 30]), None);
Source

pub fn trim_prefix<P>(&self, prefix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix. If prefix +is empty or the slice does not start with prefix, simply returns the original slice. +If prefix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Prefix present - removes it
+assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+
+// Prefix absent - returns original slice
+assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
Source

pub fn trim_suffix<P>(&self, suffix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix. If suffix +is empty or the slice does not end with suffix, simply returns the original slice. +If suffix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Suffix present - removes it
+assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+
+// Suffix absent - returns original slice
+assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);

Binary searches this slice for a given element. +If the slice is not sorted, the returned result is unspecified and +meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search_by, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+assert_eq!(s.binary_search(&13),  Ok(9));
+assert_eq!(s.binary_search(&4),   Err(7));
+assert_eq!(s.binary_search(&100), Err(13));
+let r = s.binary_search(&1);
+assert!(match r { Ok(1..=4) => true, _ => false, });
+

If you want to find that whole range of matching items, rather than +an arbitrary matching one, that can be done using partition_point:

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let low = s.partition_point(|x| x < &1);
+assert_eq!(low, 1);
+let high = s.partition_point(|x| x <= &1);
+assert_eq!(high, 5);
+let r = s.binary_search(&1);
+assert!((low..high).contains(&r.unwrap()));
+
+assert!(s[..low].iter().all(|&x| x < 1));
+assert!(s[low..high].iter().all(|&x| x == 1));
+assert!(s[high..].iter().all(|&x| x > 1));
+
+// For something not found, the "range" of equal items is empty
+assert_eq!(s.partition_point(|x| x < &11), 9);
+assert_eq!(s.partition_point(|x| x <= &11), 9);
+assert_eq!(s.binary_search(&11), Err(9));
+

If you want to insert an item to a sorted vector, while maintaining +sort order, consider using partition_point:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+// If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+// `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert`
+// to shift less elements.
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.0.0 · Source

pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
where + F: FnMut(&'a T) -> Ordering,

Binary searches this slice with a comparator function.

+

The comparator function should return an order code that indicates +whether its argument is Less, Equal or Greater the desired +target. +If the slice is not sorted or if the comparator function does not +implement an order consistent with the sort order of the underlying +slice, the returned result is unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let seek = 13;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
+let seek = 4;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
+let seek = 100;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
+let seek = 1;
+let r = s.binary_search_by(|probe| probe.cmp(&seek));
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.10.0 · Source

pub fn binary_search_by_key<'a, B, F>( + &'a self, + b: &B, + f: F, +) -> Result<usize, usize>
where + F: FnMut(&'a T) -> B, + B: Ord,

Binary searches this slice with a key extraction function.

+

Assumes that the slice is sorted by the key, for instance with +sort_by_key using the same key extraction function. +If the slice is not sorted by the key, the returned result is +unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by, and partition_point.

+
§Examples
+

Looks up a series of four elements in a slice of pairs sorted by +their second elements. The first is found, with a uniquely +determined position; the second and third are not found; the +fourth could match any position in [1, 4].

+ +
let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
+         (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+         (1, 21), (2, 34), (4, 55)];
+
+assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b),  Ok(9));
+assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b),   Err(7));
+assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
+let r = s.binary_search_by_key(&1, |&(a, b)| b);
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.20.0 · Source

pub fn sort_unstable(&mut self)
where + T: Ord,

Sorts the slice in ascending order without preserving the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for T panics.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_unstable_by such as f32::total_cmp or f64::total_cmp that defines a +total order users can sort slices containing floating-point values. Alternatively, if all +values in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp +forms a total order, it’s possible to sort the slice with sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort_unstable();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.20.0 · Source

pub fn sort_unstable_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, without preserving the +initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the comparison function compare does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if compare panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the compare does not implement a total order, or if +the compare itself panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_unstable_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_unstable_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.20.0 · Source

pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, without preserving +the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for K panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_unstable_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.49.0 · Source

pub fn select_nth_unstable( + &mut self, + index: usize, +) -> (&mut [T], &mut T, &mut [T])
where + T: Ord,

Reorders the slice such that the element at index is at a sort-order position. All +elements before index will be <= to this value, and all elements after will be >= to +it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple that partitions the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy x <= self[index].

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy x >= self[index].

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if the implementation of Ord for T does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `<=` to the median, the median itself, and the items `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable(2);
+
+assert!(lesser == [-3, -5] || lesser == [-5, -3]);
+assert_eq!(median, &mut 1);
+assert!(greater == [4, 2] || greater == [2, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [-3, -5, 1, 2, 4] ||
+        v == [-5, -3, 1, 2, 4] ||
+        v == [-3, -5, 1, 4, 2] ||
+        v == [-5, -3, 1, 4, 2]);
1.49.0 · Source

pub fn select_nth_unstable_by<F>( + &mut self, + index: usize, + compare: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T, &T) -> Ordering,

Reorders the slice with a comparator function such that the element at index is at a +sort-order position. All elements before index will be <= to this value, and all +elements after will be >= to it, according to the comparator function.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy +compare(x, self[index]).is_le().

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy +compare(x, self[index]).is_ge().

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if compare does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `>=` to the median, the median itself, and the items `<=` to it, by using
+// a reversed comparator.
+let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
+
+assert!(before == [4, 2] || before == [2, 4]);
+assert_eq!(median, &mut 1);
+assert!(after == [-3, -5] || after == [-5, -3]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [2, 4, 1, -5, -3] ||
+        v == [2, 4, 1, -3, -5] ||
+        v == [4, 2, 1, -5, -3] ||
+        v == [4, 2, 1, -3, -5]);
1.49.0 · Source

pub fn select_nth_unstable_by_key<K, F>( + &mut self, + index: usize, + f: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T) -> K, + K: Ord,

Reorders the slice with a key extraction function such that the element at index is at a +sort-order position. All elements before index will have keys <= to the key at index, +and all elements after will have keys >= to it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy f(x) <= f(self[index]).

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy f(x) >= f(self[index]).

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), meaning it always panics on empty slices.

+

May panic if K: Ord does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 1, -3, 2];
+
+// Find the items `<=` to the absolute median, the absolute median itself, and the items
+// `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
+
+assert!(lesser == [1, 2] || lesser == [2, 1]);
+assert_eq!(median, &mut -3);
+assert!(greater == [4, -5] || greater == [-5, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [1, 2, -3, 4, -5] ||
+        v == [1, 2, -3, -5, 4] ||
+        v == [2, 1, -3, 4, -5] ||
+        v == [2, 1, -3, -5, 4]);
Source

pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
where + T: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all consecutive repeated elements to the end of the slice according to the +PartialEq trait implementation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+
+let (dedup, duplicates) = slice.partition_dedup();
+
+assert_eq!(dedup, [1, 2, 3, 2, 1]);
+assert_eq!(duplicates, [2, 3, 1]);
Source

pub fn partition_dedup_by<F>(&mut self, same_bucket: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T, &mut T) -> bool,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice satisfying +a given equality relation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

The same_bucket function is passed references to two elements from the slice and +must determine if the elements compare equal. The elements are passed in opposite order +from their order in the slice, so if same_bucket(a, b) returns true, a is moved +at the end of the slice.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+
+let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
Source

pub fn partition_dedup_by_key<K, F>(&mut self, key: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T) -> K, + K: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice that resolve +to the same key.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+
+let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+
+assert_eq!(dedup, [10, 20, 30, 20, 11]);
+assert_eq!(duplicates, [21, 30, 13]);
1.26.0 · Source

pub fn rotate_left(&mut self, mid: usize)

Rotates the slice in-place such that the first mid elements of the +slice move to the end while the last self.len() - mid elements move to +the front.

+

After calling rotate_left, the element previously at index mid will +become the first element in the slice.

+
§Panics
+

This function will panic if mid is greater than the length of the +slice. Note that mid == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_left(2);
+assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_left(1);
+assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
1.26.0 · Source

pub fn rotate_right(&mut self, k: usize)

Rotates the slice in-place such that the first self.len() - k +elements of the slice move to the end while the last k elements move +to the front.

+

After calling rotate_right, the element previously at index +self.len() - k will become the first element in the slice.

+
§Panics
+

This function will panic if k is greater than the length of the +slice. Note that k == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_right(2);
+assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_right(1);
+assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
1.50.0 · Source

pub fn fill(&mut self, value: T)
where + T: Clone,

Fills self with elements by cloning value.

+
§Examples
+
let mut buf = vec![0; 10];
+buf.fill(1);
+assert_eq!(buf, vec![1; 10]);
1.51.0 · Source

pub fn fill_with<F>(&mut self, f: F)
where + F: FnMut() -> T,

Fills self with elements returned by calling a closure repeatedly.

+

This method uses a closure to create new values. If you’d rather +Clone a given value, use fill. If you want to use the Default +trait to generate values, you can pass Default::default as the +argument.

+
§Examples
+
let mut buf = vec![1; 10];
+buf.fill_with(Default::default);
+assert_eq!(buf, vec![0; 10]);
1.7.0 · Source

pub fn clone_from_slice(&mut self, src: &[T])
where + T: Clone,

Copies the elements from src into self.

+

The length of src must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Cloning two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.clone_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use clone_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].clone_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.clone_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.9.0 · Source

pub fn copy_from_slice(&mut self, src: &[T])
where + T: Copy,

Copies all elements from src into self, using a memcpy.

+

The length of src must be the same as self.

+

If T does not implement Copy, use clone_from_slice.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Copying two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.copy_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use copy_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].copy_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.copy_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.37.0 · Source

pub fn copy_within<R>(&mut self, src: R, dest: usize)
where + R: RangeBounds<usize>, + T: Copy,

Copies elements from one part of the slice to another part of itself, +using a memmove.

+

src is the range within self to copy from. dest is the starting +index of the range within self to copy to, which will have the same +length as src. The two ranges may overlap. The ends of the two ranges +must be less than or equal to self.len().

+
§Panics
+

This function will panic if either range exceeds the end of the slice, +or if the end of src is before the start.

+
§Examples
+

Copying four bytes within a slice:

+ +
let mut bytes = *b"Hello, World!";
+
+bytes.copy_within(1..5, 8);
+
+assert_eq!(&bytes, b"Hello, Wello!");
1.27.0 · Source

pub fn swap_with_slice(&mut self, other: &mut [T])

Swaps all elements in self with those in other.

+

The length of other must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Example
+

Swapping two elements across slices:

+ +
let mut slice1 = [0, 0];
+let mut slice2 = [1, 2, 3, 4];
+
+slice1.swap_with_slice(&mut slice2[2..]);
+
+assert_eq!(slice1, [3, 4]);
+assert_eq!(slice2, [1, 2, 0, 0]);
+

Rust enforces that there can only be one mutable reference to a +particular piece of data in a particular scope. Because of this, +attempting to use swap_with_slice on a single slice will result in +a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+slice[..2].swap_with_slice(&mut slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +mutable sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.swap_with_slice(&mut right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 1, 2]);
1.30.0 · Source

pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T])

Transmutes the slice to a slice of another type, ensuring alignment of the types is +maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
1.30.0 · Source

pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T])

Transmutes the mutable slice to a mutable slice of another type, ensuring alignment of the +types is maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
Source

pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
where + Simd<T, LANES>: AsRef<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix.

+

This is a safe wrapper around slice::align_to, so inherits the same +guarantees as that method.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
§Examples
+
#![feature(portable_simd)]
+use core::simd::prelude::*;
+
+let short = &[1, 2, 3];
+let (prefix, middle, suffix) = short.as_simd::<4>();
+assert_eq!(middle, []); // Not enough elements for anything in the middle
+
+// They might be split in any possible way between prefix and suffix
+let it = prefix.iter().chain(suffix).copied();
+assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
+
+fn basic_simd_sum(x: &[f32]) -> f32 {
+    use std::ops::Add;
+    let (prefix, middle, suffix) = x.as_simd();
+    let sums = f32x4::from_array([
+        prefix.iter().copied().sum(),
+        0.0,
+        0.0,
+        suffix.iter().copied().sum(),
+    ]);
+    let sums = middle.iter().copied().fold(sums, f32x4::add);
+    sums.reduce_sum()
+}
+
+let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
+assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
Source

pub fn as_simd_mut<const LANES: usize>( + &mut self, +) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
where + Simd<T, LANES>: AsMut<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a mutable slice into a mutable prefix, a middle of aligned SIMD types, +and a mutable suffix.

+

This is a safe wrapper around slice::align_to_mut, so inherits the same +guarantees as that method.

+

This is the mutable version of slice::as_simd; see that for examples.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
1.82.0 · Source

pub fn is_sorted(&self) -> bool
where + T: PartialOrd,

Checks if the elements of this slice are sorted.

+

That is, for each element a and its following element b, a <= b must hold. If the +slice yields exactly zero or one element, true is returned.

+

Note that if Self::Item is only PartialOrd, but not Ord, the above definition +implies that this function returns false if any two consecutive items are not +comparable.

+
§Examples
+
let empty: [i32; 0] = [];
+
+assert!([1, 2, 2, 9].is_sorted());
+assert!(![1, 3, 2, 4].is_sorted());
+assert!([0].is_sorted());
+assert!(empty.is_sorted());
+assert!(![0.0, 1.0, f32::NAN].is_sorted());
1.82.0 · Source

pub fn is_sorted_by<'a, F>(&'a self, compare: F) -> bool
where + F: FnMut(&'a T, &'a T) -> bool,

Checks if the elements of this slice are sorted using the given comparator function.

+

Instead of using PartialOrd::partial_cmp, this function uses the given compare +function to determine whether two elements are to be considered in sorted order.

+
§Examples
+
assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b));
+assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b));
+
+assert!([0].is_sorted_by(|a, b| true));
+assert!([0].is_sorted_by(|a, b| false));
+
+let empty: [i32; 0] = [];
+assert!(empty.is_sorted_by(|a, b| false));
+assert!(empty.is_sorted_by(|a, b| true));
1.82.0 · Source

pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool
where + F: FnMut(&'a T) -> K, + K: PartialOrd,

Checks if the elements of this slice are sorted using the given key extraction function.

+

Instead of comparing the slice’s elements directly, this function compares the keys of the +elements, as determined by f. Apart from that, it’s equivalent to is_sorted; see its +documentation for more information.

+
§Examples
+
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
+assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
1.52.0 · Source

pub fn partition_point<P>(&self, pred: P) -> usize
where + P: FnMut(&T) -> bool,

Returns the index of the partition point according to the given predicate +(the index of the first element of the second partition).

+

The slice is assumed to be partitioned according to the given predicate. +This means that all elements for which the predicate returns true are at the start of the slice +and all elements for which the predicate returns false are at the end. +For example, [7, 15, 3, 5, 4, 12, 6] is partitioned under the predicate x % 2 != 0 +(all odd numbers are at the start, all even at the end).

+

If this slice is not partitioned, the returned result is unspecified and meaningless, +as this method performs a kind of binary search.

+

See also binary_search, binary_search_by, and binary_search_by_key.

+
§Examples
+
let v = [1, 2, 3, 3, 5, 6, 7];
+let i = v.partition_point(|&x| x < 5);
+
+assert_eq!(i, 4);
+assert!(v[..i].iter().all(|&x| x < 5));
+assert!(v[i..].iter().all(|&x| !(x < 5)));
+

If all elements of the slice match the predicate, including if the slice +is empty, then the length of the slice will be returned:

+ +
let a = [2, 4, 8];
+assert_eq!(a.partition_point(|x| x < &100), a.len());
+let a: [i32; 0] = [];
+assert_eq!(a.partition_point(|x| x < &100), 0);
+

If you want to insert an item to a sorted vector, while maintaining +sort order:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.87.0 · Source

pub fn split_off<'a, R>(self: &mut &'a [T], range: R) -> Option<&'a [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off(..3).unwrap();
+
+assert_eq!(slice, &['d']);
+assert_eq!(first_three, &['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off(2..).unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(tail, &['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off(5..));
+assert_eq!(None, slice.split_off(..5));
+assert_eq!(None, slice.split_off(..=4));
+let expected: &[char] = &['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off(..4));
1.87.0 · Source

pub fn split_off_mut<'a, R>( + self: &mut &'a mut [T], + range: R, +) -> Option<&'a mut [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a mutable reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off_mut(..3).unwrap();
+
+assert_eq!(slice, &mut ['d']);
+assert_eq!(first_three, &mut ['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off_mut(2..).unwrap();
+
+assert_eq!(slice, &mut ['a', 'b']);
+assert_eq!(tail, &mut ['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off_mut(5..));
+assert_eq!(None, slice.split_off_mut(..5));
+assert_eq!(None, slice.split_off_mut(..=4));
+let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off_mut(..4));
1.87.0 · Source

pub fn split_off_first<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the first element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let first = slice.split_off_first().unwrap();
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'a');
1.87.0 · Source

pub fn split_off_first_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the first element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let first = slice.split_off_first_mut().unwrap();
+*first = 'd';
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'d');
1.87.0 · Source

pub fn split_off_last<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the last element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let last = slice.split_off_last().unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'c');
1.87.0 · Source

pub fn split_off_last_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the last element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let last = slice.split_off_last_mut().unwrap();
+*last = 'd';
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'d');
1.86.0 · Source

pub unsafe fn get_disjoint_unchecked_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> [&mut <I as SliceIndex<[T]>>::Output; N]

Returns mutable references to many indices at once, without doing any checks.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

For a safe alternative see get_disjoint_mut.

+
§Safety
+

Calling this method with overlapping or out-of-bounds indices is undefined behavior +even if the resulting references are not used.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0, 2]);
+    *a *= 10;
+    *b *= 100;
+}
+assert_eq!(x, &[10, 2, 400]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0..1, 1..3]);
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(x, &[8, 88, 888]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([1..=2, 0..=0]);
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(x, &[1, 11, 111]);
1.86.0 · Source

pub fn get_disjoint_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> Result<[&mut <I as SliceIndex<[T]>>::Output; N], GetDisjointMutError>

Returns mutable references to many indices at once.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

Returns an error if any index is out-of-bounds, or if there are overlapping indices. +An empty range is not considered to overlap if it is located at the beginning or at +the end of another range, but is considered to overlap if it is located in the middle.

+

This method does a O(n^2) check to check that there are no overlapping indices, so be careful +when passing many indices.

+
§Examples
+
let v = &mut [1, 2, 3];
+if let Ok([a, b]) = v.get_disjoint_mut([0, 2]) {
+    *a = 413;
+    *b = 612;
+}
+assert_eq!(v, &[413, 2, 612]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([0..1, 1..3]) {
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(v, &[8, 88, 888]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([1..=2, 0..=0]) {
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(v, &[1, 11, 111]);
Source

pub fn element_offset(&self, element: &T) -> Option<usize>

🔬This is a nightly-only experimental API. (substr_range)

Returns the index that an element reference points to.

+

Returns None if element does not point to the start of an element within the slice.

+

This method is useful for extending slice iterators like slice::split.

+

Note that this uses pointer arithmetic and does not compare elements. +To find the index of an element via comparison, use +.iter().position() instead.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums: &[u32] = &[1, 7, 1, 1];
+let num = &nums[2];
+
+assert_eq!(num, &1);
+assert_eq!(nums.element_offset(num), Some(2));
+

Returning None with an unaligned element:

+ +
#![feature(substr_range)]
+
+let arr: &[[u32; 2]] = &[[0, 1], [2, 3]];
+let flat_arr: &[u32] = arr.as_flattened();
+
+let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap();
+let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap();
+
+assert_eq!(ok_elm, &[0, 1]);
+assert_eq!(weird_elm, &[1, 2]);
+
+assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0
+assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1
Source

pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>>

🔬This is a nightly-only experimental API. (substr_range)

Returns the range of indices that a subslice points to.

+

Returns None if subslice does not point within the slice or if it is not aligned with the +elements in the slice.

+

This method does not compare elements. Instead, this method finds the location in the slice that +subslice was obtained from. To find the index of a subslice via comparison, instead use +.windows().position().

+

This method is useful for extending slice iterators like slice::split.

+

Note that this may return a false positive (either Some(0..0) or Some(self.len()..self.len())) +if subslice has a length of zero and points to the beginning or end of another, separate, slice.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums = &[0, 5, 10, 0, 0, 5];
+
+let mut iter = nums
+    .split(|t| *t == 0)
+    .map(|n| nums.subslice_range(n).unwrap());
+
+assert_eq!(iter.next(), Some(0..0));
+assert_eq!(iter.next(), Some(1..3));
+assert_eq!(iter.next(), Some(4..4));
+assert_eq!(iter.next(), Some(5..6));
1.79.0 · Source

pub fn utf8_chunks(&self) -> Utf8Chunks<'_>

Creates an iterator over the contiguous valid UTF-8 ranges of this +slice, and the non-UTF-8 fragments in between.

+

See the Utf8Chunk type for documentation of the items yielded by this iterator.

+
§Examples
+

This function formats arbitrary but mostly-UTF-8 bytes into Rust source +code in the form of a C-string literal (c"...").

+ +
use std::fmt::Write as _;
+
+pub fn cstr_literal(bytes: &[u8]) -> String {
+    let mut repr = String::new();
+    repr.push_str("c\"");
+    for chunk in bytes.utf8_chunks() {
+        for ch in chunk.valid().chars() {
+            // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for non-printable characters.
+            write!(repr, "{}", ch.escape_debug()).unwrap();
+        }
+        for byte in chunk.invalid() {
+            write!(repr, "\\x{:02X}", byte).unwrap();
+        }
+    }
+    repr.push('"');
+    repr
+}
+
+fn main() {
+    let lit = cstr_literal(b"\xferris the \xf0\x9f\xa6\x80\x07");
+    let expected = stringify!(c"\xFErris the 🦀\u{7}");
+    assert_eq!(lit, expected);
+}
1.0.0 · Source

pub fn sort(&mut self)
where + T: Ord,

Sorts the slice in ascending order, preserving initial order of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

When applicable, unstable sorting is preferred because it is generally faster than stable +sorting and it doesn’t allocate auxiliary memory. See +sort_unstable. The exception are partially sorted slices, which +may be better served with slice::sort.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_by such as f32::total_cmp or f64::total_cmp that defines a total +order users can sort slices containing floating-point values. Alternatively, if all values +in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp forms a +total order, it’s possible to sort the slice with sort_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.0.0 · Source

pub fn sort_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, preserving initial order of +equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the comparison function compare does not implement a total order, the function may +panic; even if the function exits normally, the resulting order of elements in the slice is +unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if compare does not implement a total order, or if compare itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.7.0 · Source

pub fn sort_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n * log(n)) +worst-case, where the key function is O(m).

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation or the key-function f panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.34.0 · Source

pub fn sort_by_cached_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n + n * +log(n)) worst-case, where the key function is O(m).

+

During sorting, the key function is called at most once per element, by using temporary +storage to remember the results of key evaluation. The order of calls to the key function is +unspecified and may change in future versions of the standard library.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For simple key functions (e.g., functions that are property accesses or basic operations), +sort_by_key is likely to be faster.

+
§Current implementation
+

The current implementation is based on instruction-parallel-network sort by Lukas +Bergdoll, which combines the fast average case of randomized quicksort with the fast worst +case of heapsort, while achieving linear time on fully sorted and reversed inputs. And +O(k * log(n)) where k is the number of distinct elements in the input. It leverages +superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently +perform the operation.

+

In the worst case, the algorithm allocates temporary storage in a Vec<(K, usize)> the +length of the slice.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2, 10];
+
+// Strings are sorted by lexicographical order.
+v.sort_by_cached_key(|k| k.to_string());
+assert_eq!(v, [-3, -5, 1, 10, 2, 4]);
1.0.0 · Source

pub fn to_vec(&self) -> Vec<T>
where + T: Clone,

Copies self into a new Vec.

+
§Examples
+
let s = [10, 40, 30];
+let x = s.to_vec();
+// Here, `s` and `x` can be modified independently.
Source

pub fn to_vec_in<A>(&self, alloc: A) -> Vec<T, A>
where + A: Allocator, + T: Clone,

🔬This is a nightly-only experimental API. (allocator_api)

Copies self into a new Vec with an allocator.

+
§Examples
+
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let s = [10, 40, 30];
+let x = s.to_vec_in(System);
+// Here, `s` and `x` can be modified independently.
1.40.0 · Source

pub fn repeat(&self, n: usize) -> Vec<T>
where + T: Copy,

Creates a vector by copying a slice n times.

+
§Panics
+

This function will panic if the capacity would overflow.

+
§Examples
+
assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]);
+

A panic upon overflow:

+ +
// this will panic at runtime
+b"0123456789abcdef".repeat(usize::MAX);
1.0.0 · Source

pub fn concat<Item>(&self) -> <[T] as Concat<Item>>::Output
where + [T]: Concat<Item>, + Item: ?Sized,

Flattens a slice of T into a single value Self::Output.

+
§Examples
+
assert_eq!(["hello", "world"].concat(), "helloworld");
+assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
1.3.0 · Source

pub fn join<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].join(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);
+assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);
1.0.0 · Source

pub fn connect<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

👎Deprecated since 1.3.0: renamed to join

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].connect(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]);
1.23.0 · Source

pub fn to_ascii_uppercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII upper case equivalent.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To uppercase the value in-place, use make_ascii_uppercase.

+
1.23.0 · Source

pub fn to_ascii_lowercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII lower case equivalent.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To lowercase the value in-place, use make_ascii_lowercase.

+

Trait Implementations§

Source§

impl Clone for ConnectionId

Source§

fn clone(&self) -> ConnectionId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Deref for ConnectionId

Source§

type Target = [u8]

The resulting type after dereferencing.
Source§

fn deref(&self) -> &[u8]

Dereferences the value.
Source§

impl DerefMut for ConnectionId

Source§

fn deref_mut(&mut self) -> &mut [u8]

Mutably dereferences the value.
Source§

impl Display for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Hash for ConnectionId

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ConnectionId

Source§

fn cmp(&self, other: &ConnectionId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionId

Source§

fn eq(&self, other: &ConnectionId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionId

Source§

fn partial_cmp(&self, other: &ConnectionId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionId

Source§

impl Eq for ConnectionId

Source§

impl StructuralPartialEq for ConnectionId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<P, T> Receiver for P
where + P: Deref<Target = T> + ?Sized, + T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ConnectionStats.html b/pr/234/docs/iroh_quinn/struct.ConnectionStats.html new file mode 100644 index 000000000..21834c226 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ConnectionStats.html @@ -0,0 +1,29 @@ +ConnectionStats in iroh_quinn - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, +}
Expand description

Connection statistics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§udp_tx: UdpStats

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Endpoint.html b/pr/234/docs/iroh_quinn/struct.Endpoint.html new file mode 100644 index 000000000..61fd9af6a --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Endpoint.html @@ -0,0 +1,96 @@ +Endpoint in iroh_quinn - Rust

Endpoint

Struct Endpoint 

Source
pub struct Endpoint { /* private fields */ }
Expand description

A QUIC endpoint.

+

An endpoint corresponds to a single UDP socket, may host many connections, and may act as both +client and server for different connections.

+

May be cloned to obtain another handle to the same endpoint.

+

Implementations§

Source§

impl Endpoint

Source

pub fn client(addr: SocketAddr) -> Result<Self>

Helper to construct an endpoint for use with outgoing connections only

+

Note that addr is the local address to bind to, which should usually be a wildcard +address like 0.0.0.0:0 or [::]:0, which allow communication with any reachable IPv4 or +IPv6 address respectively from an OS-assigned port.

+

If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow +communication with both IPv4 and IPv6 addresses. As such, calling Endpoint::client with +the address [::]:0 is a reasonable default to maximize the ability to connect to other +address. For example:

+ +
iroh_quinn::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+

Some environments may not allow creation of dual-stack sockets, in which case an IPv6 +client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.

+
Source

pub fn stats(&self) -> EndpointStats

Returns relevant stats from this Endpoint

+
Source

pub fn server(config: ServerConfig, addr: SocketAddr) -> Result<Self>

Helper to construct an endpoint for use with both incoming and outgoing connections

+

Platform defaults for dual-stack sockets vary. For example, any socket bound to a wildcard +IPv6 address on Windows will not by default be able to communicate with IPv4 +addresses. Portable applications should bind an address that matches the family they wish to +communicate within.

+
Source

pub fn new( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: UdpSocket, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and socket

+
Source

pub fn new_with_abstract_socket( + config: EndpointConfig, + server_config: Option<ServerConfig>, + socket: Box<dyn AsyncUdpSocket>, + runtime: Arc<dyn Runtime>, +) -> Result<Self>

Construct an endpoint with arbitrary configuration and pre-constructed abstract socket

+

Useful when socket has additional state (e.g. sidechannels) attached for which shared +ownership is needed.

+
Source

pub fn accept(&self) -> Accept<'_>

Get the next incoming connection attempt from a client

+

Yields Incomings, or None if the endpoint is closed. Incoming +can be awaited to obtain the final Connection, or used to e.g. +filter connection attempts or force address validation, or converted into an intermediate +Connecting future which can be used to e.g. send 0.5-RTT data.

+
Source

pub fn set_default_client_config(&mut self, config: ClientConfig)

Set the client configuration used by connect

+
Source

pub fn connect( + &self, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint

+

server_name must be covered by the certificate presented by the server. This prevents a +connection from being intercepted by an attacker with a valid certificate for some other +server.

+

May fail immediately due to configuration errors, or in the future if the connection could +not be established.

+
Source

pub fn connect_with( + &self, + config: ClientConfig, + addr: SocketAddr, + server_name: &str, +) -> Result<Connecting, ConnectError>

Connect to a remote endpoint using a custom configuration.

+

See connect() for details.

+
Source

pub fn rebind(&self, socket: UdpSocket) -> Result<()>

Switch to a new UDP socket

+

See Endpoint::rebind_abstract() for details.

+
Source

pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> Result<()>

Switch to a new UDP socket

+

Allows the endpoint’s address to be updated live, affecting all active connections. Incoming +connections and connections to servers unreachable from the new address will be lost.

+

On error, the old UDP socket is retained.

+
Source

pub fn set_server_config(&self, server_config: Option<ServerConfig>)

Replace the server configuration, affecting new incoming connections only

+

Useful for e.g. refreshing TLS certificates without disrupting existing connections.

+
Source

pub fn local_addr(&self) -> Result<SocketAddr>

Get the local SocketAddr the underlying socket is bound to

+
Source

pub fn open_connections(&self) -> usize

Get the number of connections that are currently open

+
Source

pub fn close(&self, error_code: VarInt, reason: &[u8])

Close all of this endpoint’s connections immediately and cease accepting new connections.

+

See Connection::close() for details.

+
Source

pub async fn wait_idle(&self)

Wait for all connections on the endpoint to be cleanly shut down

+

Waiting for this condition before exiting ensures that a good-faith effort is made to notify +peers of recent connection closes, whereas exiting immediately could force them to wait out +the idle timeout period.

+

Does not proactively close existing connections or cause incoming connections to be +rejected. Consider calling close() if that is desired.

+

Trait Implementations§

Source§

impl Clone for Endpoint

Source§

fn clone(&self) -> Endpoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Endpoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.EndpointConfig.html b/pr/234/docs/iroh_quinn/struct.EndpointConfig.html new file mode 100644 index 000000000..344e746ca --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.EndpointConfig.html @@ -0,0 +1,60 @@ +EndpointConfig in iroh_quinn - Rust

EndpointConfig

Struct EndpointConfig 

Source
pub struct EndpointConfig { /* private fields */ }
Expand description

Global configuration for the endpoint, affecting all connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl EndpointConfig

Source

pub fn new(reset_key: Arc<dyn HmacKey>) -> EndpointConfig

Create a default config with a particular reset_key

+
Source

pub fn cid_generator<F>(&mut self, factory: F) -> &mut EndpointConfig
where + F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static,

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut EndpointConfig

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut EndpointConfig, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions( + &mut self, + supported_versions: Vec<u32>, +) -> &mut EndpointConfig

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut EndpointConfig

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut EndpointConfig

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut EndpointConfig

Optional seed to be used internally for random number generation

+

By default, quinn will initialize an endpoint’s rng using a platform entropy source. +However, you can seed the rng yourself through this method (e.g. if you need to run quinn +deterministically or if you are using quinn in an environment that doesn’t have a source of +entropy available).

+

Trait Implementations§

Source§

impl Clone for EndpointConfig

Source§

fn clone(&self) -> EndpointConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointConfig

Source§

fn default() -> EndpointConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.EndpointStats.html b/pr/234/docs/iroh_quinn/struct.EndpointStats.html new file mode 100644 index 000000000..b317e2b37 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.EndpointStats.html @@ -0,0 +1,29 @@ +EndpointStats in iroh_quinn - Rust

EndpointStats

Struct EndpointStats 

Source
#[non_exhaustive]
pub struct EndpointStats { + pub accepted_handshakes: u64, + pub outgoing_handshakes: u64, + pub refused_handshakes: u64, + pub ignored_handshakes: u64, +}
Expand description

Statistics on Endpoint activity

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§accepted_handshakes: u64

Cumulative number of Quic handshakes accepted by this Endpoint

+
§outgoing_handshakes: u64

Cumulative number of Quic handshakes sent from this Endpoint

+
§refused_handshakes: u64

Cumulative number of Quic handshakes refused on this Endpoint

+
§ignored_handshakes: u64

Cumulative number of Quic handshakes ignored on this Endpoint

+

Trait Implementations§

Source§

impl Clone for EndpointStats

Source§

fn clone(&self) -> EndpointStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointStats

Source§

fn default() -> EndpointStats

Returns the “default value” for a type. Read more
Source§

impl Copy for EndpointStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.FrameStats.html b/pr/234/docs/iroh_quinn/struct.FrameStats.html new file mode 100644 index 000000000..f0444eb9f --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.FrameStats.html @@ -0,0 +1,58 @@ +FrameStats in iroh_quinn - Rust

FrameStats

Struct FrameStats 

Source
#[non_exhaustive]
pub struct FrameStats {
Show 37 fields + pub acks: u64, + pub path_acks: u64, + pub ack_frequency: u64, + pub crypto: u64, + pub connection_close: u64, + pub data_blocked: u64, + pub datagram: u64, + pub handshake_done: u8, + pub immediate_ack: u64, + pub max_data: u64, + pub max_stream_data: u64, + pub max_streams_bidi: u64, + pub max_streams_uni: u64, + pub new_connection_id: u64, + pub path_new_connection_id: u64, + pub new_token: u64, + pub path_challenge: u64, + pub path_response: u64, + pub ping: u64, + pub reset_stream: u64, + pub retire_connection_id: u64, + pub path_retire_connection_id: u64, + pub stream_data_blocked: u64, + pub streams_blocked_bidi: u64, + pub streams_blocked_uni: u64, + pub stop_sending: u64, + pub stream: u64, + pub observed_addr: u64, + pub path_abandon: u64, + pub path_status_available: u64, + pub path_status_backup: u64, + pub max_path_id: u64, + pub paths_blocked: u64, + pub path_cids_blocked: u64, + pub add_address: u64, + pub reach_out: u64, + pub remove_address: u64, +
}
Expand description

Number of frames transmitted or received of each frame type

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§acks: u64§path_acks: u64§ack_frequency: u64§crypto: u64§connection_close: u64§data_blocked: u64§datagram: u64§handshake_done: u8§immediate_ack: u64§max_data: u64§max_stream_data: u64§max_streams_bidi: u64§max_streams_uni: u64§new_connection_id: u64§path_new_connection_id: u64§new_token: u64§path_challenge: u64§path_response: u64§ping: u64§reset_stream: u64§retire_connection_id: u64§path_retire_connection_id: u64§stream_data_blocked: u64§streams_blocked_bidi: u64§streams_blocked_uni: u64§stop_sending: u64§stream: u64§observed_addr: u64§path_abandon: u64§path_status_available: u64§path_status_backup: u64§max_path_id: u64§paths_blocked: u64§path_cids_blocked: u64§add_address: u64§reach_out: u64§remove_address: u64

Trait Implementations§

Source§

impl Clone for FrameStats

Source§

fn clone(&self) -> FrameStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for FrameStats

Source§

fn default() -> FrameStats

Returns the “default value” for a type. Read more
Source§

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.FrameType.html b/pr/234/docs/iroh_quinn/struct.FrameType.html new file mode 100644 index 000000000..51a96fb98 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.FrameType.html @@ -0,0 +1,28 @@ +FrameType in iroh_quinn - Rust

FrameType

Struct FrameType 

Source
pub struct FrameType(/* private fields */);
Expand description

A QUIC frame type

+

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 Codec 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§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer
Source§

impl Debug for FrameType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for FrameType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
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 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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.IdleTimeout.html b/pr/234/docs/iroh_quinn/struct.IdleTimeout.html new file mode 100644 index 000000000..1e3072237 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.IdleTimeout.html @@ -0,0 +1,45 @@ +IdleTimeout in iroh_quinn - Rust

IdleTimeout

Struct IdleTimeout 

Source
pub struct IdleTimeout(/* private fields */);
Expand description

Maximum duration of inactivity to accept before timing out the connection

+

This wraps an underlying VarInt, representing the duration in milliseconds. Values can be +constructed by converting directly from VarInt, or using TryFrom<Duration>.

+ +
// A `VarInt`-encoded value in milliseconds
+let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+
+// Try to convert a `Duration` into a `VarInt`-encoded timeout
+let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;

Trait Implementations§

Source§

impl Clone for IdleTimeout

Source§

fn clone(&self) -> IdleTimeout

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IdleTimeout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for IdleTimeout

Source§

fn default() -> IdleTimeout

Returns the “default value” for a type. Read more
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> IdleTimeout

Converts to this type from the input type.
Source§

impl Hash for IdleTimeout

Source§

fn hash<__H>(&self, state: &mut __H)
where + __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for IdleTimeout

Source§

fn cmp(&self, other: &IdleTimeout) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for IdleTimeout

Source§

fn eq(&self, other: &IdleTimeout) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for IdleTimeout

Source§

fn partial_cmp(&self, other: &IdleTimeout) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<Duration> for IdleTimeout

Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

fn try_from( + timeout: Duration, +) -> Result<IdleTimeout, <IdleTimeout as TryFrom<Duration>>::Error>

Performs the conversion.
Source§

impl Copy for IdleTimeout

Source§

impl Eq for IdleTimeout

Source§

impl StructuralPartialEq for IdleTimeout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Incoming.html b/pr/234/docs/iroh_quinn/struct.Incoming.html new file mode 100644 index 000000000..ce1c4a544 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Incoming.html @@ -0,0 +1,39 @@ +Incoming in iroh_quinn - Rust

Incoming

Struct Incoming 

Source
pub struct Incoming(/* private fields */);
Expand description

An incoming connection for which the server has not yet begun its part of the handshake

+

Implementations§

Source§

impl Incoming

Source

pub fn accept(self) -> Result<Connecting, ConnectionError>

Attempt to accept this incoming connection (an error may still occur)

+
Source

pub fn accept_with( + self, + server_config: Arc<ServerConfig>, +) -> Result<Connecting, ConnectionError>

Accept this incoming connection using a custom configuration

+

See accept() for more details.

+
Source

pub fn refuse(self)

Reject this incoming connection attempt

+
Source

pub fn retry(self) -> Result<(), RetryError>

Respond with a retry packet, requiring the client to retry with address validation

+

Errors if may_retry() is false.

+
Source

pub fn ignore(self)

Ignore this incoming connection attempt, not sending any packet in response

+
Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established the connection

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+
Source

pub fn remote_address_validated(&self) -> bool

Whether the socket address that is initiating this connection has been validated

+

This means that the sender of the initial packet has proved that they can receive traffic +sent to self.remote_address().

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn may_retry(&self) -> bool

Whether it is legal to respond with a retry packet

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn orig_dst_cid(&self) -> ConnectionId

The original destination CID when initiating the connection

+

Trait Implementations§

Source§

impl Debug for Incoming

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for Incoming

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl IntoFuture for Incoming

Source§

type Output = Result<Connection, ConnectionError>

The output that the future will produce on completion.
Source§

type IntoFuture = IncomingFuture

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.IncomingFuture.html b/pr/234/docs/iroh_quinn/struct.IncomingFuture.html new file mode 100644 index 000000000..560c29243 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.IncomingFuture.html @@ -0,0 +1,46 @@ +IncomingFuture in iroh_quinn - Rust

IncomingFuture

Struct IncomingFuture 

Source
pub struct IncomingFuture(/* private fields */);
Expand description

Basic adapter to let Incoming be await-ed like a Connecting

+

Trait Implementations§

Source§

impl Debug for IncomingFuture

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Future for IncomingFuture

Source§

type Output = Result<Connection, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.MtuDiscoveryConfig.html b/pr/234/docs/iroh_quinn/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..fef4942bc --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.MtuDiscoveryConfig.html @@ -0,0 +1,81 @@ +MtuDiscoveryConfig in iroh_quinn - Rust

MtuDiscoveryConfig

Struct MtuDiscoveryConfig 

Source
pub struct MtuDiscoveryConfig { /* private fields */ }
Expand description

Parameters governing MTU discovery.

+

§The why of MTU discovery

+

By design, QUIC ensures during the handshake that the network path between the client and the +server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words, +once the connection is established, we know that the network path’s maximum transmission unit +(MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can +split outgoing data in packets of 1200 bytes, with confidence that the network will be able to +deliver them (if the endpoint were to send bigger packets, they could prove too big and end up +being dropped).

+

There is, however, a significant overhead associated to sending a packet. If the same +information can be sent in fewer packets, that results in higher throughput. The amount of +packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the +bigger the packets that can be sent, and the fewer packets that are needed to transmit a given +amount of bytes.

+

Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the +path’s MTU and, if it turns out to be higher, start sending bigger packets.

+

§MTU discovery internals

+

Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU +Discovery), described in section 14.3 of RFC +9000. This method consists of sending +QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote +peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote +peer, which in turn means that the network path’s MTU is of at least the packet’s size. If the +probe is lost, it is sent another 2 times before concluding that the MTU is lower than the +packet’s size.

+

MTU discovery runs on a schedule (e.g. every 600 seconds) specified through +MtuDiscoveryConfig::interval. The first run happens right after the handshake, and +subsequent discoveries are scheduled to run when the interval has elapsed, starting from the +last time when MTU discovery completed.

+

Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest +is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The +lower bound of the search is equal to TransportConfig::initial_mtu in the +initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The +upper bound is determined by the minimum of MtuDiscoveryConfig::upper_bound and the +max_udp_payload_size transport parameter received from the peer during the handshake.

+

§Black hole detection

+

If, at some point, the network path no longer accepts packets of the detected size, packet loss +will eventually trigger black hole detection and reset the detected MTU to 1200. In that case, +MTU discovery will be triggered after MtuDiscoveryConfig::black_hole_cooldown (ignoring the +timer that was set based on MtuDiscoveryConfig::interval).

+

§Interaction between peers

+

There is no guarantee that the MTU on the path between A and B is the same as the MTU of the +path between B and A. Therefore, each peer in the connection needs to run MTU discovery +independently in order to discover the path’s MTU.

+

Implementations§

Source§

impl MtuDiscoveryConfig

Source

pub fn interval(&mut self, value: Duration) -> &mut MtuDiscoveryConfig

Specifies the time to wait after completing MTU discovery before starting a new MTU +discovery run.

+

Defaults to 600 seconds, as recommended by RFC +8899.

+
Source

pub fn upper_bound(&mut self, value: u16) -> &mut MtuDiscoveryConfig

Specifies the upper bound to the max UDP payload size that MTU discovery will search for.

+

Defaults to 1452, to stay within Ethernet’s MTU when using IPv4 and IPv6. The highest +allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.

+

It is safe to use an arbitrarily high upper bound, regardless of the network path’s MTU. The +only drawback is that MTU discovery might take more time to finish.

+
Source

pub fn black_hole_cooldown( + &mut self, + value: Duration, +) -> &mut MtuDiscoveryConfig

Specifies the amount of time that MTU discovery should wait after a black hole was detected +before running again. Defaults to one minute.

+

Black hole detection can be spuriously triggered in case of congestion, so it makes sense to +try MTU discovery again after a short period of time.

+
Source

pub fn minimum_change(&mut self, value: u16) -> &mut MtuDiscoveryConfig

Specifies the minimum MTU change to stop the MTU discovery phase. +Defaults to 20.

+

Trait Implementations§

Source§

impl Clone for MtuDiscoveryConfig

Source§

fn clone(&self) -> MtuDiscoveryConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MtuDiscoveryConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for MtuDiscoveryConfig

Source§

fn default() -> MtuDiscoveryConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.NoneTokenLog.html b/pr/234/docs/iroh_quinn/struct.NoneTokenLog.html new file mode 100644 index 000000000..5a25ae41b --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.NoneTokenLog.html @@ -0,0 +1,24 @@ +NoneTokenLog in iroh_quinn - Rust

NoneTokenLog

Struct NoneTokenLog 

Source
pub struct NoneTokenLog;
Expand description

Null implementation of TokenLog, which never accepts tokens

+

Trait Implementations§

Source§

impl TokenLog for NoneTokenLog

Source§

fn check_and_insert( + &self, + _: u128, + _: SystemTime, + _: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.NoneTokenStore.html b/pr/234/docs/iroh_quinn/struct.NoneTokenStore.html new file mode 100644 index 000000000..650b73b0e --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.NoneTokenStore.html @@ -0,0 +1,18 @@ +NoneTokenStore in iroh_quinn - Rust

NoneTokenStore

Struct NoneTokenStore 

Source
pub struct NoneTokenStore;
Expand description

Null implementation of TokenStore, which does not store any tokens

+

Trait Implementations§

Source§

impl TokenStore for NoneTokenStore

Source§

fn insert(&self, _: &str, _: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, _: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.OnClosed.html b/pr/234/docs/iroh_quinn/struct.OnClosed.html new file mode 100644 index 000000000..c2cf57a3c --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.OnClosed.html @@ -0,0 +1,43 @@ +OnClosed in iroh_quinn - Rust

OnClosed

Struct OnClosed 

Source
pub struct OnClosed { /* private fields */ }
Expand description

Future returned by Connection::on_closed

+

Resolves to a tuple of (ConnectionError, ConnectionStats).

+

Trait Implementations§

Source§

impl Drop for OnClosed

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Future for OnClosed

Source§

type Output = (ConnectionError, ConnectionStats)

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.OpenBi.html b/pr/234/docs/iroh_quinn/struct.OpenBi.html new file mode 100644 index 000000000..730ea158e --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.OpenBi.html @@ -0,0 +1,47 @@ +OpenBi in iroh_quinn - Rust

OpenBi

Struct OpenBi 

Source
pub struct OpenBi<'a> { /* private fields */ }
Expand description

Future produced by Connection::open_bi

+

Trait Implementations§

Source§

impl Future for OpenBi<'_>

Source§

type Output = Result<(SendStream, RecvStream), ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for OpenBi<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for OpenBi<'a>

§

impl<'a> !RefUnwindSafe for OpenBi<'a>

§

impl<'a> Send for OpenBi<'a>

§

impl<'a> Sync for OpenBi<'a>

§

impl<'a> !UnwindSafe for OpenBi<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.OpenPath.html b/pr/234/docs/iroh_quinn/struct.OpenPath.html new file mode 100644 index 000000000..ff1588650 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.OpenPath.html @@ -0,0 +1,50 @@ +OpenPath in iroh_quinn - Rust

OpenPath

Struct OpenPath 

Source
pub struct OpenPath(/* private fields */);
Expand description

Future produced by crate::Connection::open_path

+

Implementations§

Source§

impl OpenPath

Source

pub fn path_id(&self) -> Option<PathId>

Returns the path ID of the new path being opened.

+

If an error occurred before a path ID was allocated, None is returned. In this +case the future is ready and polling it will immediately yield the error.

+

The returned value remains the same for the entire lifetime of this future.

+

Trait Implementations§

Source§

impl Future for OpenPath

Source§

type Output = Result<Path, PathError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.OpenUni.html b/pr/234/docs/iroh_quinn/struct.OpenUni.html new file mode 100644 index 000000000..e6bfe35ea --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.OpenUni.html @@ -0,0 +1,47 @@ +OpenUni in iroh_quinn - Rust

OpenUni

Struct OpenUni 

Source
pub struct OpenUni<'a> { /* private fields */ }
Expand description

Future produced by Connection::open_uni

+

Trait Implementations§

Source§

impl Future for OpenUni<'_>

Source§

type Output = Result<SendStream, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for OpenUni<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for OpenUni<'a>

§

impl<'a> !RefUnwindSafe for OpenUni<'a>

§

impl<'a> Send for OpenUni<'a>

§

impl<'a> Sync for OpenUni<'a>

§

impl<'a> !UnwindSafe for OpenUni<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Path.html b/pr/234/docs/iroh_quinn/struct.Path.html new file mode 100644 index 000000000..17d8ee5f4 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Path.html @@ -0,0 +1,40 @@ +Path in iroh_quinn - Rust

Path

Struct Path 

Source
pub struct Path { /* private fields */ }
Expand description

An open (Multi)Path

+

Implementations§

Source§

impl Path

Source

pub fn id(&self) -> PathId

The PathId of this path.

+
Source

pub fn status(&self) -> Result<PathStatus, ClosedPath>

The current local PathStatus of this path.

+
Source

pub fn set_status(&self, status: PathStatus) -> Result<(), SetPathStatusError>

Sets the PathStatus of this path.

+
Source

pub fn close(&self) -> Result<ClosePath, ClosePathError>

Closes this path

+

The future will resolve when all the path state is dropped. This only happens after +the remote has confirmed the path as closed and after an additional timeout to +give any in-flight packets the time to arrive.

+
Source

pub fn set_max_idle_timeout( + &self, + timeout: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn set_keep_alive_interval( + &self, + interval: Option<Duration>, +) -> Result<Option<Duration>, ClosedPath>

Sets the keep_alive_interval for a specific path

+

See TransportConfig::default_path_keep_alive_interval for details.

+

Returns the previous value of the setting.

+
Source

pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath>

Track changes on our external address as reported by the peer.

+

If the address-discovery extension is not negotiated, the stream will never return.

+
Source

pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath>

The peer’s UDP address for this path.

+

Trait Implementations§

Source§

impl Debug for Path

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Path

§

impl RefUnwindSafe for Path

§

impl Send for Path

§

impl Sync for Path

§

impl Unpin for Path

§

impl UnwindSafe for Path

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.PathId.html b/pr/234/docs/iroh_quinn/struct.PathId.html new file mode 100644 index 000000000..fb5d2614a --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.PathId.html @@ -0,0 +1,46 @@ +PathId in iroh_quinn - Rust

PathId

Struct PathId 

Source
pub struct PathId(/* private fields */);
Expand description

Id representing different paths when using multipath extension

+

Implementations§

Source§

impl PathId

Source

pub const MAX: PathId

The maximum path ID allowed.

+
Source

pub const ZERO: PathId

The 0 path id.

+
Source

pub fn saturating_add(self, rhs: impl Into<PathId>) -> PathId

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+
Source

pub fn saturating_sub(self, rhs: impl Into<PathId>) -> PathId

Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds +instead of overflowing.

+

Trait Implementations§

Source§

impl Clone for PathId

Source§

fn clone(&self) -> PathId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B>(&self, w: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer
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 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<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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.PathStats.html b/pr/234/docs/iroh_quinn/struct.PathStats.html new file mode 100644 index 000000000..b8135a8a1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.PathStats.html @@ -0,0 +1,47 @@ +PathStats in iroh_quinn - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_packets: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time)

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_packets: u64

The amount of packets sent on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by sent_packets)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for PathStats

Source§

fn default() -> PathStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStats

Source§

fn eq(&self, other: &PathStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.QlogConfig.html b/pr/234/docs/iroh_quinn/struct.QlogConfig.html new file mode 100644 index 000000000..28a5ec8d5 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.QlogConfig.html @@ -0,0 +1,27 @@ +QlogConfig in iroh_quinn - Rust

QlogConfig

Struct QlogConfig 

Source
pub struct QlogConfig { /* private fields */ }
Expand description

Configuration for qlog trace logging.

+

This struct is returned from QlogFactory::for_connection if qlog logging should +be enabled for a connection. It allows to set metadata for the qlog trace.

+

The trace will be written to the provided writer in the [JSON-SEQ format] defined in the qlog spec.

+

JSON-SEQ format

+

Implementations§

Source§

impl QlogConfig

Source

pub fn new(writer: Box<dyn Write + Send + Sync>) -> QlogConfig

Creates a new QlogConfig that writes a qlog trace to the specified writer.

+
Source

pub fn title(&mut self, title: Option<String>) -> &mut QlogConfig

Title to record in the qlog capture

+
Source

pub fn description(&mut self, description: Option<String>) -> &mut QlogConfig

Description to record in the qlog capture

+
Source

pub fn start_time(&mut self, start_time: Instant) -> &mut QlogConfig

Epoch qlog event times are recorded relative to

+

If unset, the start of the connection is used.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.QlogFileFactory.html b/pr/234/docs/iroh_quinn/struct.QlogFileFactory.html new file mode 100644 index 000000000..8204d77c3 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.QlogFileFactory.html @@ -0,0 +1,32 @@ +QlogFileFactory in iroh_quinn - Rust

QlogFileFactory

Struct QlogFileFactory 

Source
pub struct QlogFileFactory { /* private fields */ }
Expand description

Enables writing qlog traces to a directory.

+

Implementations§

Source§

impl QlogFileFactory

Source

pub fn new(dir: PathBuf) -> QlogFileFactory

Creates a new qlog factory that writes files into the specified directory.

+
Source

pub fn from_env() -> QlogFileFactory

Creates a new qlog factory that writes files into QLOGDIR, if set.

+

If the environment variable QLOGDIR is set, qlog traces for all connections handled +by this endpoint will be written into that directory. +If the directory doesn’t exist it will be created.

+
Source

pub fn with_prefix(self, prefix: impl ToString) -> QlogFileFactory

Sets a prefix to the filename of the generated files.

+
Source

pub fn with_start_instant(self, start: Instant) -> QlogFileFactory

Override the instant relative to which all events are recorded.

+

If not set, events will be recorded relative to the start of the connection.

+

Trait Implementations§

Source§

impl Debug for QlogFileFactory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl QlogFactory for QlogFileFactory

Source§

fn for_connection( + &self, + side: Side, + _remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ReadDatagram.html b/pr/234/docs/iroh_quinn/struct.ReadDatagram.html new file mode 100644 index 000000000..2dbfda6d9 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ReadDatagram.html @@ -0,0 +1,47 @@ +ReadDatagram in iroh_quinn - Rust

ReadDatagram

Struct ReadDatagram 

Source
pub struct ReadDatagram<'a> { /* private fields */ }
Expand description

Future produced by Connection::read_datagram

+

Trait Implementations§

Source§

impl Future for ReadDatagram<'_>

Source§

type Output = Result<Bytes, ConnectionError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for ReadDatagram<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for ReadDatagram<'a>

§

impl<'a> !RefUnwindSafe for ReadDatagram<'a>

§

impl<'a> Send for ReadDatagram<'a>

§

impl<'a> Sync for ReadDatagram<'a>

§

impl<'a> !UnwindSafe for ReadDatagram<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.RecvStream.html b/pr/234/docs/iroh_quinn/struct.RecvStream.html new file mode 100644 index 000000000..d262db807 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.RecvStream.html @@ -0,0 +1,205 @@ +RecvStream in iroh_quinn - Rust

RecvStream

Struct RecvStream 

Source
pub struct RecvStream { /* private fields */ }
Expand description

A stream that can only be used to receive data

+

stop(0) is implicitly called on drop unless:

+
    +
  • A variant of ReadError has been yielded by a read call
  • +
  • stop() was called explicitly
  • +
+

§Cancellation

+

A read method is said to be cancel-safe when dropping its future before the future becomes +ready cannot lead to loss of stream data. This is true of methods which succeed immediately when +any progress is made, and is not true of methods which might need to perform multiple reads +internally before succeeding. Each read method documents whether it is cancel-safe.

+

§Common issues

§Data never received on a locally-opened stream

+

Peers are not notified of streams until they or a later-numbered stream are used to send +data. If a bidirectional stream is locally opened but never used to send, then the peer may +never see it. Application protocols should always arrange for the endpoint which will first +transmit on a stream to be the endpoint responsible for opening it.

+

§Data never received on a remotely-opened stream

+

Verify that the stream you are receiving is the same one that the server is sending on, e.g. by +logging the id of each. Streams are always accepted in the same order as they are created, +i.e. ascending order by StreamId. For example, even if a sender first transmits on +bidirectional stream 1, the first stream yielded by Connection::accept_bi on the receiver +will be bidirectional stream 0.

+

Implementations§

Source§

impl RecvStream

Source

pub async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, ReadError>

Read data contiguously from the stream.

+

Yields the number of bytes read into buf on success, or None if the stream was finished.

+

This operation is cancel-safe.

+
Source

pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError>

Read an exact number of bytes contiguously from the stream.

+

See read() for details. This operation is not cancel-safe.

+
Source

pub fn poll_read( + &mut self, + cx: &mut Context<'_>, + buf: &mut [u8], +) -> Poll<Result<usize, ReadError>>

Attempts to read from the stream into the provided buffer

+

On success, returns Poll::Ready(Ok(num_bytes_read)) and places data into buf. If this +returns zero bytes read (and buf has a non-zero length), that indicates that the remote +side has finished the stream and the local side has already read all bytes.

+

If no data is available for reading, this returns Poll::Pending and arranges for the +current task (via cx.waker()) to be notified when the stream becomes readable or is +closed.

+
Source

pub fn poll_read_buf( + &mut self, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, +) -> Poll<Result<(), ReadError>>

Attempts to read from the stream into the provided buffer, which may be uninitialized

+

On success, returns Poll::Ready(Ok(())) and places data into the unfilled portion of +buf. If this does not write any bytes to buf (and buf.remaining() is non-zero), that +indicates that the remote side has finished the stream and the local side has already +read all bytes.

+

If no data is available for reading, this returns Poll::Pending and arranges for the +current task (via cx.waker()) to be notified when the stream becomes readable or is +closed.

+
Source

pub async fn read_chunk( + &mut self, + max_length: usize, + ordered: bool, +) -> 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. If ordered is true, the chunk’s offset will be immediately after +the last data yielded by read() or read_chunk(). 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. Unordered reads are less prone to head-of-line blocking within a +stream, but require the application to manage reassembling the original data.

+

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_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.

+

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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.RetryError.html b/pr/234/docs/iroh_quinn/struct.RetryError.html new file mode 100644 index 000000000..9165edb76 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.RetryError.html @@ -0,0 +1,20 @@ +RetryError in iroh_quinn - Rust

RetryError

Struct RetryError 

Source
pub struct RetryError(/* private fields */);
Expand description

Error for attempting to retry an Incoming which already bears a token from a previous retry

+

Implementations§

Trait Implementations§

Source§

impl Debug for RetryError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for RetryError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for RetryError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> 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/234/docs/iroh_quinn/struct.SendDatagram.html b/pr/234/docs/iroh_quinn/struct.SendDatagram.html new file mode 100644 index 000000000..912d7343e --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.SendDatagram.html @@ -0,0 +1,47 @@ +SendDatagram in iroh_quinn - Rust

SendDatagram

Struct SendDatagram 

Source
pub struct SendDatagram<'a> { /* private fields */ }
Expand description

Future produced by Connection::send_datagram_wait

+

Trait Implementations§

Source§

impl Future for SendDatagram<'_>

Source§

type Output = Result<(), SendDatagramError>

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more
Source§

impl<'__pin, 'a> Unpin for SendDatagram<'a>
where + PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,

Auto Trait Implementations§

§

impl<'a> !Freeze for SendDatagram<'a>

§

impl<'a> !RefUnwindSafe for SendDatagram<'a>

§

impl<'a> Send for SendDatagram<'a>

§

impl<'a> Sync for SendDatagram<'a>

§

impl<'a> !UnwindSafe for SendDatagram<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<F, T, E> TryFuture for F
where + F: Future<Output = Result<T, E>> + ?Sized,

§

type Ok = T

The type of successful values yielded by this future
§

type Error = E

The type of failures yielded by this future
§

fn try_poll( + self: Pin<&mut F>, + cx: &mut Context<'_>, +) -> Poll<<F as Future>::Output>

Poll this TryFuture as if it were a Future. Read more
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.SendStream.html b/pr/234/docs/iroh_quinn/struct.SendStream.html new file mode 100644 index 000000000..adc35337b --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.SendStream.html @@ -0,0 +1,215 @@ +SendStream in iroh_quinn - Rust

SendStream

Struct SendStream 

Source
pub struct SendStream { /* private fields */ }
Expand description

A stream that can only be used to send data

+

If dropped, streams that haven’t been explicitly reset() will be implicitly finish()ed, +continuing to (re)transmit previously written data until it has been fully acknowledged or the +connection is closed.

+

§Cancellation

+

A write method is said to be cancel-safe when dropping its future before the future becomes +ready will always result in no data being written to the stream. This is true of methods which +succeed immediately when any progress is made, and is not true of methods which might need to +perform multiple writes internally before succeeding. Each write method documents whether it is +cancel-safe.

+

Implementations§

Source§

impl SendStream

Source

pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError>

Write a buffer into this stream, returning how many bytes were written

+

Unless this method errors, it waits until some amount of buf can be written into this +stream, and then writes as much as it can without waiting again. Due to congestion and flow +control, this may be shorter than buf.len(). On success this yields the length of the +prefix that was written.

+
§Cancel safety
+

This method is cancellation safe. If this does not resolve, no bytes were written.

+
Source

pub async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteError>

Write a buffer into this stream in its entirety

+

This method repeatedly calls write until all bytes are written, or an +error occurs.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some prefix of buf +may have been written when previously polled.

+
Source

pub async fn write_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<Written, WriteError>

Write a slice of [Bytes] into this stream, returning how much was written

+

Bytes to try to write are provided to this method as an array of cheaply cloneable chunks. +Unless this method errors, it waits until some amount of those bytes can be written into +this stream, and then writes as much as it can without waiting again. Due to congestion and +flow control, this may be less than the total number of bytes.

+

On success, this method both mutates bufs and yields an informative Written struct +indicating how much was written:

+
    +
  • [Bytes] chunks that were fully written are mutated to be empty.
  • +
  • If a [Bytes] chunk was partially written, it is split to contain +only the suffix of bytes that were not written.
  • +
  • The yielded Written struct indicates how many chunks were fully written as well as +how many bytes were written.
  • +
+
§Cancel safety
+

This method is cancellation safe. If this does not resolve, no bytes were written.

+
Source

pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError>

Write a single [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an single cheaply cloneable chunk. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub async fn write_all_chunks( + &mut self, + bufs: &mut [Bytes], +) -> Result<(), WriteError>

Write a slice of [Bytes] into this stream in its entirety

+

Bytes to write are provided to this method as an array of cheaply cloneable chunks. This +method repeatedly calls write_chunks until all bytes are written, +or an error occurs. This method mutates bufs by mutating all chunks to be +empty.

+
§Cancel safety
+

This method is not cancellation safe. Even if this does not resolve, some bytes may have +been written when previously polled.

+
Source

pub fn finish(&mut self) -> Result<(), ClosedStream>

Notify the peer that no more data will ever be written to this stream

+

It is an error to write to a SendStream after finish()ing it. reset() +may still be called after finish to abandon transmission of any stream data that might +still be buffered.

+

To wait for the peer to receive all buffered stream data, see stopped().

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Close the send stream immediately.

+

No new data can be written after calling this method. Locally buffered data is dropped, and +previously transmitted data will no longer be retransmitted if lost. If an attempt has +already been made to finish the stream, the peer may still receive all written data.

+

May fail if finish() or reset() was previously +called. This error is harmless and serves only to indicate that the caller may have +incorrect assumptions about the stream’s state.

+
Source

pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream>

Set the priority of the send stream

+

Every send stream has an initial priority of 0. Locally buffered data from streams with +higher priority will be transmitted before data from streams with lower priority. Changing +the priority of a stream with pending data may only take effect after that data has been +transmitted. Using many different priority levels per connection may have a negative +impact on performance.

+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of the send stream

+
Source

pub fn stopped( + &self, +) -> impl Future<Output = Result<Option<VarInt>, StoppedError>> + Send + Sync + 'static

Completes when the peer stops the stream or reads the stream to completion

+

Yields Some with the stop error code if the peer stops the stream. Yields None if the +local side finish()es the stream and then the peer acknowledges receipt +of all stream data (although not necessarily the processing of it), after which the peer +closing the stream is no longer meaningful.

+

For a variety of reasons, the peer may not send acknowledgements immediately upon receiving +data. As such, relying on stopped to know when the peer has read a stream to completion +may introduce more latency than using an application-level response of some sort.

+
Source

pub fn id(&self) -> StreamId

Get the identity of this stream

+
Source

pub fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize, WriteError>>

Attempt to write bytes from buf into the stream.

+

On success, returns Poll::Ready(Ok(num_bytes_written)).

+

If the stream is not ready for writing, the method returns Poll::Pending and arranges +for the current task (via cx.waker().wake_by_ref()) to receive a notification when the +stream becomes writable or is closed.

+

Trait Implementations§

Source§

impl AsyncWrite for SendStream

Available on crate feature futures-io only.
Source§

fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize>>

Attempt to write bytes from buf into the object. Read more
Source§

fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempt to flush the object, ensuring that any buffered data reach +their destination. Read more
Source§

fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempt to close the object. Read more
§

fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], +) -> Poll<Result<usize, Error>>

Attempt to write bytes from bufs into the object using vectored +IO operations. Read more
Source§

impl AsyncWrite for SendStream

Source§

fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], +) -> Poll<Result<usize>>

Attempt to write bytes from buf into the object. Read more
Source§

fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>>

Attempts to flush the object, ensuring that any buffered data reach +their destination. Read more
Source§

fn poll_shutdown( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, +) -> Poll<Result<()>>

Initiates or attempts to shut down this writer, returning success when +the I/O connection has completely shut down. Read more
§

fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>], +) -> Poll<Result<usize, Error>>

Like poll_write, except that it writes from a slice of buffers. Read more
§

fn is_write_vectored(&self) -> bool

Determines if this writer has an efficient poll_write_vectored +implementation. Read more
Source§

impl Debug for SendStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for SendStream

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<W> AsyncWriteExt for W
where + W: AsyncWrite + ?Sized,

§

fn write<'a>(&'a mut self, src: &'a [u8]) -> Write<'a, Self>
where + Self: Unpin,

Writes a buffer into this writer, returning how many bytes were +written. Read more
§

fn write_vectored<'a, 'b>( + &'a mut self, + bufs: &'a [IoSlice<'b>], +) -> WriteVectored<'a, 'b, Self>
where + Self: Unpin,

Like write, except that it writes from a slice of buffers. Read more
§

fn write_buf<'a, B>(&'a mut self, src: &'a mut B) -> WriteBuf<'a, Self, B>
where + Self: Sized + Unpin, + B: Buf,

Writes a buffer into this writer, advancing the buffer’s internal +cursor. Read more
§

fn write_all_buf<'a, B>( + &'a mut self, + src: &'a mut B, +) -> WriteAllBuf<'a, Self, B>
where + Self: Sized + Unpin, + B: Buf,

Attempts to write an entire buffer into this writer. Read more
§

fn write_all<'a>(&'a mut self, src: &'a [u8]) -> WriteAll<'a, Self>
where + Self: Unpin,

Attempts to write an entire buffer into this writer. Read more
§

fn write_u8(&mut self, n: u8) -> WriteU8<&mut Self>
where + Self: Unpin,

Writes an unsigned 8-bit integer to the underlying writer. Read more
§

fn write_i8(&mut self, n: i8) -> WriteI8<&mut Self>
where + Self: Unpin,

Writes a signed 8-bit integer to the underlying writer. Read more
§

fn write_u16(&mut self, n: u16) -> WriteU16<&mut Self>
where + Self: Unpin,

Writes an unsigned 16-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i16(&mut self, n: i16) -> WriteI16<&mut Self>
where + Self: Unpin,

Writes a signed 16-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u32(&mut self, n: u32) -> WriteU32<&mut Self>
where + Self: Unpin,

Writes an unsigned 32-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i32(&mut self, n: i32) -> WriteI32<&mut Self>
where + Self: Unpin,

Writes a signed 32-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u64(&mut self, n: u64) -> WriteU64<&mut Self>
where + Self: Unpin,

Writes an unsigned 64-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i64(&mut self, n: i64) -> WriteI64<&mut Self>
where + Self: Unpin,

Writes an signed 64-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_u128(&mut self, n: u128) -> WriteU128<&mut Self>
where + Self: Unpin,

Writes an unsigned 128-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_i128(&mut self, n: i128) -> WriteI128<&mut Self>
where + Self: Unpin,

Writes an signed 128-bit integer in big-endian order to the +underlying writer. Read more
§

fn write_f32(&mut self, n: f32) -> WriteF32<&mut Self>
where + Self: Unpin,

Writes an 32-bit floating point type in big-endian order to the +underlying writer. Read more
§

fn write_f64(&mut self, n: f64) -> WriteF64<&mut Self>
where + Self: Unpin,

Writes an 64-bit floating point type in big-endian order to the +underlying writer. Read more
§

fn write_u16_le(&mut self, n: u16) -> WriteU16Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 16-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i16_le(&mut self, n: i16) -> WriteI16Le<&mut Self>
where + Self: Unpin,

Writes a signed 16-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u32_le(&mut self, n: u32) -> WriteU32Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 32-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i32_le(&mut self, n: i32) -> WriteI32Le<&mut Self>
where + Self: Unpin,

Writes a signed 32-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u64_le(&mut self, n: u64) -> WriteU64Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 64-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i64_le(&mut self, n: i64) -> WriteI64Le<&mut Self>
where + Self: Unpin,

Writes an signed 64-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_u128_le(&mut self, n: u128) -> WriteU128Le<&mut Self>
where + Self: Unpin,

Writes an unsigned 128-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_i128_le(&mut self, n: i128) -> WriteI128Le<&mut Self>
where + Self: Unpin,

Writes an signed 128-bit integer in little-endian order to the +underlying writer. Read more
§

fn write_f32_le(&mut self, n: f32) -> WriteF32Le<&mut Self>
where + Self: Unpin,

Writes an 32-bit floating point type in little-endian order to the +underlying writer. Read more
§

fn write_f64_le(&mut self, n: f64) -> WriteF64Le<&mut Self>
where + Self: Unpin,

Writes an 64-bit floating point type in little-endian order to the +underlying writer. Read more
§

fn flush(&mut self) -> Flush<'_, Self>
where + Self: Unpin,

Flushes this output stream, ensuring that all intermediately buffered +contents reach their destination. Read more
§

fn shutdown(&mut self) -> Shutdown<'_, Self>
where + Self: Unpin,

Shuts down the output stream, ensuring that the value can be dropped +cleanly. Read more
§

impl<W> AsyncWriteExt for W
where + W: AsyncWrite + ?Sized,

§

fn write<'a>(&'a mut self, buf: &'a [u8]) -> WriteFuture<'a, Self>
where + Self: Unpin,

Writes some bytes into the byte stream. Read more
§

fn write_vectored<'a>( + &'a mut self, + bufs: &'a [IoSlice<'a>], +) -> WriteVectoredFuture<'a, Self>
where + Self: Unpin,

Like [write()][AsyncWriteExt::write()], except that it writes a slice of buffers. Read more
§

fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAllFuture<'a, Self>
where + Self: Unpin,

Writes an entire buffer into the byte stream. Read more
§

fn flush(&mut self) -> FlushFuture<'_, Self>
where + Self: Unpin,

Flushes the stream to ensure that all buffered contents reach their destination. Read more
§

fn close(&mut self) -> CloseFuture<'_, Self>
where + Self: Unpin,

Closes the writer. Read more
§

fn boxed_writer<'a>(self) -> Pin<Box<dyn AsyncWrite + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the writer and changes its type to dyn AsyncWrite + Send + 'a. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ServerConfig.html b/pr/234/docs/iroh_quinn/struct.ServerConfig.html new file mode 100644 index 000000000..1fc6217c9 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ServerConfig.html @@ -0,0 +1,107 @@ +ServerConfig in iroh_quinn - Rust

ServerConfig

Struct ServerConfig 

Source
pub struct ServerConfig {
+    pub transport: Arc<TransportConfig>,
+    pub crypto: Arc<dyn ServerConfig>,
+    pub validation_token: ValidationTokenConfig,
+    /* private fields */
+}
Expand description

Parameters governing incoming connections

+

Default values should be suitable for most internet applications.

+

Fields§

§transport: Arc<TransportConfig>

Transport configuration to use for incoming connections

+
§crypto: Arc<dyn ServerConfig>

TLS configuration used for incoming connections

+

Must be set to use TLS 1.3 only.

+
§validation_token: ValidationTokenConfig

Configuration for sending and handling validation tokens

+

Implementations§

Source§

impl ServerConfig

Source

pub fn new( + crypto: Arc<dyn ServerConfig>, + token_key: Arc<dyn HandshakeTokenKey>, +) -> ServerConfig

Create a default config with a particular handshake token key

+
Source

pub fn transport_config( + &mut self, + transport: Arc<TransportConfig>, +) -> &mut ServerConfig

Set a custom TransportConfig

+
Source

pub fn validation_token_config( + &mut self, + validation_token: ValidationTokenConfig, +) -> &mut ServerConfig

Set a custom ValidationTokenConfig

+
Source

pub fn token_key( + &mut self, + value: Arc<dyn HandshakeTokenKey>, +) -> &mut ServerConfig

Private key used to authenticate data included in handshake tokens

+
Source

pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut ServerConfig

Duration after a retry token was issued for which it’s considered valid

+

Defaults to 15 seconds.

+
Source

pub fn migration(&mut self, value: bool) -> &mut ServerConfig

Whether to allow clients to migrate to new addresses

+

Improves behavior for clients that move between different internet connections or suffer NAT +rebinding. Enabled by default.

+
Source

pub fn preferred_address_v4( + &mut self, + address: Option<SocketAddrV4>, +) -> &mut ServerConfig

The preferred IPv4 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn preferred_address_v6( + &mut self, + address: Option<SocketAddrV6>, +) -> &mut ServerConfig

The preferred IPv6 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn max_incoming(&mut self, max_incoming: usize) -> &mut ServerConfig

Maximum number of Incoming to allow to exist at a time

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. While this limit is reached, new incoming connection attempts are immediately +refused. Larger values have greater worst-case memory consumption, but accommodate greater +application latency in handling incoming connection attempts.

+

The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits +memory consumption from this to under 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn incoming_buffer_size( + &mut self, + incoming_buffer_size: u64, +) -> &mut ServerConfig

Maximum number of received bytes to buffer for each Incoming

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 10 MiB–an amount such that in most situations a client would +not transmit that much 0-RTT data faster than the server handles the corresponding +Incoming.

+
Source

pub fn incoming_buffer_size_total( + &mut self, + incoming_buffer_size_total: u64, +) -> &mut ServerConfig

Maximum number of received bytes to buffer for all Incoming +collectively

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn time_source( + &mut self, + time_source: Arc<dyn TimeSource>, +) -> &mut ServerConfig

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Defaults to StdSystemTime, which simply calls SystemTime::now().

+
Source§

impl ServerConfig

Source

pub fn with_single_cert( + cert_chain: Vec<CertificateDer<'static>>, + key: PrivateKeyDer<'static>, +) -> Result<ServerConfig, Error>

Create a server config with the given certificate chain to be presented to clients

+

Uses a randomized handshake token key.

+
Source§

impl ServerConfig

Source

pub fn with_crypto(crypto: Arc<dyn ServerConfig>) -> ServerConfig

Create a server config with the given crypto::ServerConfig

+

Uses a randomized handshake token key.

+

Trait Implementations§

Source§

impl Clone for ServerConfig

Source§

fn clone(&self) -> ServerConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ServerConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.SmolRuntime.html b/pr/234/docs/iroh_quinn/struct.SmolRuntime.html new file mode 100644 index 000000000..2340dd796 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.SmolRuntime.html @@ -0,0 +1,18 @@ +SmolRuntime in iroh_quinn - Rust

SmolRuntime

Struct SmolRuntime 

Source
pub struct SmolRuntime;
Expand description

A Quinn runtime for smol

+

Trait Implementations§

Source§

impl Debug for SmolRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Runtime for SmolRuntime

Source§

fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i
Source§

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background
Source§

fn wrap_udp_socket(&self, sock: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime
Source§

fn now(&self) -> Instant

Look up the current time Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.StdSystemTime.html b/pr/234/docs/iroh_quinn/struct.StdSystemTime.html new file mode 100644 index 000000000..aa80f5fdf --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.StdSystemTime.html @@ -0,0 +1,19 @@ +StdSystemTime in iroh_quinn - Rust

StdSystemTime

Struct StdSystemTime 

Source
pub struct StdSystemTime;
Expand description

Default implementation of TimeSource

+

Implements now by calling SystemTime::now().

+

Trait Implementations§

Source§

impl TimeSource for StdSystemTime

Source§

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.StreamId.html b/pr/234/docs/iroh_quinn/struct.StreamId.html new file mode 100644 index 000000000..77e96b59e --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.StreamId.html @@ -0,0 +1,48 @@ +StreamId in iroh_quinn - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> StreamId

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<StreamId, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<StreamId, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer
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 Display for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.TokenMemoryCache.html b/pr/234/docs/iroh_quinn/struct.TokenMemoryCache.html new file mode 100644 index 000000000..b4556adf1 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.TokenMemoryCache.html @@ -0,0 +1,24 @@ +TokenMemoryCache in iroh_quinn - Rust

TokenMemoryCache

Struct TokenMemoryCache 

Source
pub struct TokenMemoryCache(/* private fields */);
Expand description

TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory

+

Implementations§

Source§

impl TokenMemoryCache

Source

pub fn new( + max_server_names: u32, + max_tokens_per_server: usize, +) -> TokenMemoryCache

Construct empty

+

Trait Implementations§

Source§

impl Debug for TokenMemoryCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for TokenMemoryCache

Defaults to a maximum of 256 servers and 2 tokens per server

+
Source§

fn default() -> TokenMemoryCache

Returns the “default value” for a type. Read more
Source§

impl TokenStore for TokenMemoryCache

Source§

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.TokenReuseError.html b/pr/234/docs/iroh_quinn/struct.TokenReuseError.html new file mode 100644 index 000000000..4fe70cc4a --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.TokenReuseError.html @@ -0,0 +1,18 @@ +TokenReuseError in iroh_quinn - Rust

TokenReuseError

Struct TokenReuseError 

Source
pub struct TokenReuseError;
Expand description

Error for when a validation token may have been reused

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.TokioRuntime.html b/pr/234/docs/iroh_quinn/struct.TokioRuntime.html new file mode 100644 index 000000000..cfada6913 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.TokioRuntime.html @@ -0,0 +1,18 @@ +TokioRuntime in iroh_quinn - Rust

TokioRuntime

Struct TokioRuntime 

Source
pub struct TokioRuntime;
Expand description

A Quinn runtime for Tokio

+

Trait Implementations§

Source§

impl Debug for TokioRuntime

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Runtime for TokioRuntime

Source§

fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i
Source§

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background
Source§

fn wrap_udp_socket(&self, sock: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime
Source§

fn now(&self) -> Instant

Look up the current time Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Transmit.html b/pr/234/docs/iroh_quinn/struct.Transmit.html new file mode 100644 index 000000000..fbf8754a6 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Transmit.html @@ -0,0 +1,30 @@ +Transmit in iroh_quinn - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub size: usize,
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§size: usize

Amount of data written to the caller-supplied buffer

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl Debug for Transmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.TransportConfig.html b/pr/234/docs/iroh_quinn/struct.TransportConfig.html new file mode 100644 index 000000000..45bb77fd3 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.TransportConfig.html @@ -0,0 +1,245 @@ +TransportConfig in iroh_quinn - Rust

TransportConfig

Struct TransportConfig 

Source
pub struct TransportConfig { /* private fields */ }
Expand description

Parameters governing the core QUIC state machine

+

Default values should be suitable for most internet applications. Applications protocols which +forbid remotely-initiated streams should set max_concurrent_bidi_streams and +max_concurrent_uni_streams to zero.

+

In some cases, performance or resource requirements can be improved by tuning these values to +suit a particular application and/or network connection. In particular, data window sizes can be +tuned for a particular expected round trip time, link capacity, and memory availability. Tuning +for higher bandwidths and latencies increases worst-case memory consumption, but does not impair +performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps +link with a 100ms round trip time.

+

Implementations§

Source§

impl TransportConfig

Source

pub fn max_concurrent_bidi_streams( + &mut self, + value: VarInt, +) -> &mut TransportConfig

Maximum number of incoming bidirectional streams that may be open concurrently

+

Must be nonzero for the peer to open any bidirectional streams.

+

Worst-case memory use is directly proportional to max_concurrent_bidi_streams * stream_receive_window, with an upper bound proportional to receive_window.

+
Source

pub fn max_concurrent_uni_streams( + &mut self, + value: VarInt, +) -> &mut TransportConfig

Variant of max_concurrent_bidi_streams affecting unidirectional streams

+
Source

pub fn max_idle_timeout( + &mut self, + value: Option<IdleTimeout>, +) -> &mut TransportConfig

Maximum duration of inactivity to accept before timing out the connection.

+

The true idle timeout is the minimum of this and the peer’s own max idle timeout. None +represents an infinite timeout. Defaults to 30 seconds.

+

WARNING: If a peer or its network path malfunctions or acts maliciously, an infinite +idle timeout can result in permanently hung futures!

+ +
let mut config = TransportConfig::default();
+
+// Set the idle timeout as `VarInt`-encoded milliseconds
+config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+
+// Set the idle timeout as a `Duration`
+config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
Source

pub fn stream_receive_window(&mut self, value: VarInt) -> &mut TransportConfig

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Setting this smaller than receive_window helps ensure that a single +stream doesn’t monopolize receive buffers, which may otherwise occur if the application +chooses not to read from a large stream for a time while still requiring data on other +streams.

+
Source

pub fn receive_window(&mut self, value: VarInt) -> &mut TransportConfig

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Larger values can be useful to allow maximum throughput within a +stream while another is blocked.

+
Source

pub fn send_window(&mut self, value: u64) -> &mut TransportConfig

Maximum number of bytes to transmit to a peer without acknowledgment

+

Provides an upper bound on memory when communicating with peers that issue large amounts of +flow control credit. Endpoints that wish to handle large numbers of connections robustly +should take care to set this low enough to guarantee memory exhaustion does not occur if +every connection uses the entire window.

+
Source

pub fn send_fairness(&mut self, value: bool) -> &mut TransportConfig

Whether to implement fair queuing for send streams having the same priority.

+

When enabled, connections schedule data from outgoing streams having the same priority in a +round-robin fashion. When disabled, streams are scheduled in the order they are written to.

+

Note that this only affects streams with the same priority. Higher priority streams always +take precedence over lower priority streams.

+

Disabling fairness can reduce fragmentation and protocol overhead for workloads that use +many small streams.

+
Source

pub fn packet_threshold(&mut self, value: u32) -> &mut TransportConfig

Maximum reordering in packet number space before FACK style loss detection considers a +packet lost. Should not be less than 3, per RFC5681.

+
Source

pub fn time_threshold(&mut self, value: f32) -> &mut TransportConfig

Maximum reordering in time space before time based loss detection considers a packet lost, +as a factor of RTT

+
Source

pub fn initial_rtt(&mut self, value: Duration) -> &mut TransportConfig

The RTT used before an RTT sample is taken

+
Source

pub fn initial_mtu(&mut self, value: u16) -> &mut TransportConfig

The initial value to be used as the maximum UDP payload size before running MTU discovery +(see TransportConfig::mtu_discovery_config).

+

Must be at least 1200, which is the default, and known to be safe for typical internet +applications. Larger values are more efficient, but increase the risk of packet loss due to +exceeding the network path’s IP MTU. If the provided value is higher than what the network +path actually supports, packet loss will eventually trigger black hole detection and bring +it down to TransportConfig::min_mtu.

+
Source

pub fn min_mtu(&mut self, value: u16) -> &mut TransportConfig

The maximum UDP payload size guaranteed to be supported by the network.

+

Must be at least 1200, which is the default, and lower than or equal to +TransportConfig::initial_mtu.

+

Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links +outside of either endpoint’s control. Extreme care should be used when raising this value +outside of private networks where these factors are fully controlled. If the provided value +is higher than what the network path actually supports, the result will be unpredictable and +catastrophic packet loss, without a possibility of repair. Prefer +TransportConfig::initial_mtu together with +TransportConfig::mtu_discovery_config to set a maximum UDP payload size that robustly +adapts to the network.

+
Source

pub fn mtu_discovery_config( + &mut self, + value: Option<MtuDiscoveryConfig>, +) -> &mut TransportConfig

Specifies the MTU discovery config (see MtuDiscoveryConfig for details).

+

Enabled by default.

+
Source

pub fn pad_to_mtu(&mut self, value: bool) -> &mut TransportConfig

Pad UDP datagrams carrying application data to current maximum UDP payload size

+

Disabled by default. UDP datagrams containing loss probes are exempt from padding.

+

Enabling this helps mitigate traffic analysis by network observers, but it increases +bandwidth usage. Without this mitigation precise plain text size of application datagrams as +well as the total size of stream write bursts can be inferred by observers under certain +conditions. This analysis requires either an uncongested connection or application datagrams +too large to be coalesced.

+
Source

pub fn ack_frequency_config( + &mut self, + value: Option<AckFrequencyConfig>, +) -> &mut TransportConfig

Specifies the ACK frequency config (see AckFrequencyConfig for details)

+

The provided configuration will be ignored if the peer does not support the acknowledgement +frequency QUIC extension.

+

Defaults to None, which disables controlling the peer’s acknowledgement frequency. Even +if set to None, the local side still supports the acknowledgement frequency QUIC +extension and may use it in other ways.

+
Source

pub fn persistent_congestion_threshold( + &mut self, + value: u32, +) -> &mut TransportConfig

Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.

+
Source

pub fn keep_alive_interval( + &mut self, + value: Option<Duration>, +) -> &mut TransportConfig

Period of inactivity before sending a keep-alive packet

+

Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.

+

None to disable, which is the default. Only one side of any given connection needs keep-alive +enabled for the connection to be preserved. Must be set lower than the idle_timeout of both +peers to be effective.

+
Source

pub fn crypto_buffer_size(&mut self, value: usize) -> &mut TransportConfig

Maximum quantity of out-of-order crypto layer data to buffer

+
Source

pub fn allow_spin(&mut self, value: bool) -> &mut TransportConfig

Whether the implementation is permitted to set the spin bit on this connection

+

This allows passive observers to easily judge the round trip time of a connection, which can +be useful for network administration but sacrifices a small amount of privacy.

+
Source

pub fn datagram_receive_buffer_size( + &mut self, + value: Option<usize>, +) -> &mut TransportConfig

Maximum number of incoming application datagram bytes to buffer, or None to disable +incoming datagrams

+

The peer is forbidden to send single datagrams larger than this size. If the aggregate size +of all datagrams that have been received from the peer but not consumed by the application +exceeds this value, old datagrams are dropped until it is no longer exceeded.

+
Source

pub fn datagram_send_buffer_size( + &mut self, + value: usize, +) -> &mut TransportConfig

Maximum number of outgoing application datagram bytes to buffer

+

While datagrams are sent ASAP, it is possible for an application to generate data faster +than the link, or even the underlying hardware, can transmit them. This limits the amount of +memory that may be consumed in that case. When the send buffer is full and a new datagram is +sent, older datagrams are dropped until sufficient space is available.

+
Source

pub fn congestion_controller_factory( + &mut self, + factory: Arc<dyn ControllerFactory + Send + Sync>, +) -> &mut TransportConfig

How to construct new congestion::Controllers

+

Typically the refcounted configuration of a congestion::Controller, +e.g. a congestion::NewRenoConfig.

+
§Example
+
let mut config = TransportConfig::default();
+config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
Source

pub fn enable_segmentation_offload( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to use “Generic Segmentation Offload” to accelerate transmits, when supported by the +environment

+

Defaults to true.

+

GSO dramatically reduces CPU consumption when sending large numbers of packets with the same +headers, such as when transmitting bulk data on a connection. However, it is not supported +by all network interface drivers or packet inspection tools. quinn-udp will attempt to +disable GSO automatically when unavailable, but this can lead to spurious packet loss at +startup, temporarily degrading performance.

+
Source

pub fn send_observed_address_reports( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to send observed address reports to peers.

+

This will aid peers in inferring their reachable address, which in most NATd networks +will not be easily available to them.

+
Source

pub fn receive_observed_address_reports( + &mut self, + enabled: bool, +) -> &mut TransportConfig

Whether to receive observed address reports from other peers.

+

Peers with the address discovery extension enabled that are willing to provide observed +address reports will do so if this transport parameter is set. In general, observed address +reports cannot be trusted. This, however, can aid the current endpoint in inferring its +reachable address, which in most NATd networks will not be easily available.

+
Source

pub fn max_concurrent_multipath_paths( + &mut self, + max_concurrent: u32, +) -> &mut TransportConfig

Enables the Multipath Extension for QUIC.

+

Setting this to any nonzero value will enable the Multipath Extension for QUIC, +https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/.

+

The value provided specifies the number maximum number of paths this endpoint may open +concurrently when multipath is negotiated. For any path to be opened, the remote must +enable multipath as well.

+
Source

pub fn default_path_max_idle_timeout( + &mut self, + timeout: Option<Duration>, +) -> &mut TransportConfig

Sets a default per-path maximum idle timeout

+

If the path is idle for this long the path will be abandoned. Bear in mind this will +interact with the TransportConfig::max_idle_timeout, if the last path is +abandoned the entire connection will be closed.

+

You can also change this using Connection::set_path_max_idle_timeout for +existing paths.

+
Source

pub fn default_path_keep_alive_interval( + &mut self, + interval: Option<Duration>, +) -> &mut TransportConfig

Sets a default per-path keep alive interval

+

Note that this does not interact with the connection-wide +TransportConfig::keep_alive_interval. This setting will keep this path active, +TransportConfig::keep_alive_interval will keep the connection active, with no +control over which path is used for this.

+

You can also change this using Connection::set_path_keep_alive_interval for +existing path.

+
Source

pub fn set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut TransportConfig

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable Iroh’s holepunching, loosely based in the Nat +Traversal Extension for QUIC, see +https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html

+

This implementation expects the multipath extension to be enabled as well. if not yet +enabled via Self::max_concurrent_multipath_paths, a default value of +[DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED] will be used.

+
Source

pub fn qlog_factory( + &mut self, + factory: Arc<dyn QlogFactory>, +) -> &mut TransportConfig

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut TransportConfig

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut TransportConfig

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for TransportConfig

Source§

fn default() -> TransportConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.TransportErrorCode.html b/pr/234/docs/iroh_quinn/struct.TransportErrorCode.html new file mode 100644 index 000000000..1d53beae6 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.TransportErrorCode.html @@ -0,0 +1,50 @@ +TransportErrorCode in iroh_quinn - Rust

TransportErrorCode

Struct TransportErrorCode 

Source
pub struct TransportErrorCode(/* private fields */);
Expand description

Transport-level error code

+

Implementations§

Source§

impl Code

Source

pub fn crypto(code: u8) -> Code

Create QUIC error code from TLS alert code

+
Source§

impl Code

Source

pub const NO_ERROR: Code

the connection is being closed abruptly in the absence of any error

+
Source

pub const INTERNAL_ERROR: Code

the endpoint encountered an internal error and cannot continue with the connection

+
Source

pub const CONNECTION_REFUSED: Code

the server refused to accept a new connection

+
Source

pub const FLOW_CONTROL_ERROR: Code

received more data than permitted in advertised data limits

+
Source

pub const STREAM_LIMIT_ERROR: Code

received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type

+
Source

pub const STREAM_STATE_ERROR: Code

received a frame for a stream that was not in a state that permitted that frame

+
Source

pub const FINAL_SIZE_ERROR: Code

received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established

+
Source

pub const FRAME_ENCODING_ERROR: Code

received a frame that was badly formatted

+
Source

pub const TRANSPORT_PARAMETER_ERROR: Code

received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error

+
Source

pub const CONNECTION_ID_LIMIT_ERROR: Code

the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit

+
Source

pub const PROTOCOL_VIOLATION: Code

detected an error with protocol compliance that was not covered by more specific error codes

+
Source

pub const INVALID_TOKEN: Code

received an invalid Retry Token in a client Initial

+
Source

pub const APPLICATION_ERROR: Code

the application or application protocol caused the connection to be closed during the handshake

+
Source

pub const CRYPTO_BUFFER_EXCEEDED: Code

received more data in CRYPTO frames than can be buffered

+
Source

pub const KEY_UPDATE_ERROR: Code

key update error

+
Source

pub const AEAD_LIMIT_REACHED: Code

the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm

+
Source

pub const NO_VIABLE_PATH: Code

no viable network path exists

+
Source

pub const APPLICATION_ABANDON_PATH: Code

Path abandoned at the application’s request

+
Source

pub const PATH_RESOURCE_LIMIT_REACHED: Code

Path abandoned due to resource limitations in the transport

+
Source

pub const PATH_UNSTABLE_OR_POOR: Code

Path abandoned due to unstable interfaces

+
Source

pub const NO_CID_AVAILABLE_FOR_PATH: Code

Path abandoned due to no available connection IDs for the path

+

Trait Implementations§

Source§

impl Clone for Code

Source§

fn clone(&self) -> Code

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer
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 Display for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.UdpStats.html b/pr/234/docs/iroh_quinn/struct.UdpStats.html new file mode 100644 index 000000000..a1d066618 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.UdpStats.html @@ -0,0 +1,29 @@ +UdpStats in iroh_quinn - Rust

UdpStats

Struct UdpStats 

Source
#[non_exhaustive]
pub struct UdpStats { + pub datagrams: u64, + pub bytes: u64, + pub ios: u64, +}
Expand description

Statistics about UDP datagrams transmitted or received on a connection

+

All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§datagrams: u64

The amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl Copy for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ValidationTokenConfig.html b/pr/234/docs/iroh_quinn/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..dfe7353e2 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ValidationTokenConfig.html @@ -0,0 +1,67 @@ +ValidationTokenConfig in iroh_quinn - Rust

ValidationTokenConfig

Struct ValidationTokenConfig 

Source
pub struct ValidationTokenConfig { /* private fields */ }
Expand description

Configuration for sending and handling validation tokens in incoming connections

+

Default values should be suitable for most internet applications.

+

§QUIC Tokens

+

The QUIC protocol defines a concept of “address validation”. Essentially, one side of a +QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address, +but it will only consider that remote address “validated” once it has convincing evidence that +the address is not being spoofed.

+

Validation is important primarily because of QUIC’s “anti-amplification limit.” This limit +prevents a QUIC server from sending a client more than three times the number of bytes it has +received from the client on a given address until that address is validated. This is designed +to mitigate the ability of attackers to use QUIC-based servers as reflectors in amplification +attacks.

+

A path may become validated in several ways. The server is always considered validated by the +client. The client usually begins in an unvalidated state upon first connecting or migrating, +but then becomes validated through various mechanisms that usually take one network round trip. +However, in some cases, a client which has previously attempted to connect to a server may have +been given a one-time use cryptographically secured “token” that it can send in a subsequent +connection attempt to be validated immediately.

+

There are two ways these tokens can originate:

+
    +
  • If the server responds to an incoming connection with retry, a “retry token” is minted and +sent to the client, which the client immediately uses to attempt to connect again. Retry +tokens operate on short timescales, such as 15 seconds.
  • +
  • If a client’s path within an active connection is validated, the server may send the client +one or more “validation tokens,” which the client may store for use in later connections to +the same server. Validation tokens may be valid for much longer lifetimes than retry token.
  • +
+

The usage of validation tokens is most impactful in situations where 0-RTT data is also being +used–in particular, in situations where the server sends the client more than three times more +0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection +handshake implicitly causes the client’s address to be validated, transmission of 0.5-RTT data +is the main situation where a server might be sending application data to an address that could +be validated by token usage earlier than it would become validated without token usage.

+

These tokens should not be confused with “stateless reset tokens,” which are similarly named +but entirely unrelated.

+

Implementations§

Source§

impl ValidationTokenConfig

Source

pub fn lifetime(&mut self, value: Duration) -> &mut ValidationTokenConfig

Duration after an address validation token was issued for which it’s considered valid

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

Defaults to 2 weeks.

+
Source

pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut ValidationTokenConfig

Set a custom TokenLog

+

If the bloom feature is enabled (which it is by default), defaults to a default +BloomTokenLog, which is suitable for most internet applications.

+

If the bloom feature is disabled, defaults to NoneTokenLog, +which makes the server ignore all address validation tokens (that is, tokens originating +from NEW_TOKEN frames–retry tokens are not affected).

+
Source

pub fn sent(&mut self, value: u32) -> &mut ValidationTokenConfig

Number of address validation tokens sent to a client when its path is validated

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

If the bloom feature is enabled (which it is by default), defaults to 2. Otherwise, +defaults to 0.

+

Trait Implementations§

Source§

impl Clone for ValidationTokenConfig

Source§

fn clone(&self) -> ValidationTokenConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ValidationTokenConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for ValidationTokenConfig

Source§

fn default() -> ValidationTokenConfig

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.VarInt.html b/pr/234/docs/iroh_quinn/struct.VarInt.html new file mode 100644 index 000000000..42258114d --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.VarInt.html @@ -0,0 +1,58 @@ +VarInt in iroh_quinn - Rust

VarInt

Struct VarInt 

Source
pub struct VarInt(/* private fields */);
Expand description

An integer less than 2^62

+

Values of this type are suitable for encoding as QUIC variable-length integer.

+

Implementations§

Source§

impl VarInt

Source

pub const MAX: VarInt

The largest representable value

+
Source

pub const MAX_SIZE: usize = 8usize

The largest encoded value length

+
Source

pub const fn from_u32(x: u32) -> VarInt

Construct a VarInt infallibly

+
Source

pub fn from_u64(x: u64) -> Result<VarInt, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source

pub const unsafe fn from_u64_unchecked(x: u64) -> VarInt

Create a VarInt without ensuring it’s in range

+
§Safety
+

x must be less than 2^62.

+
Source

pub const fn into_inner(self) -> u64

Extract the integer value

+
Source

pub fn saturating_add(self, rhs: impl Into<VarInt>) -> VarInt

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for VarInt

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<VarInt, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for VarInt

Source§

fn clone(&self) -> VarInt

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B>(&self, w: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer
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 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 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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.VarIntBoundsExceeded.html b/pr/234/docs/iroh_quinn/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..d86f3535d --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.VarIntBoundsExceeded.html @@ -0,0 +1,26 @@ +VarIntBoundsExceeded in iroh_quinn - Rust

VarIntBoundsExceeded

Struct VarIntBoundsExceeded 

Source
pub struct VarIntBoundsExceeded;
Expand description

Error returned when constructing a VarInt from a value >= 2^62

+

Trait Implementations§

Source§

impl Clone for VarIntBoundsExceeded

Source§

fn clone(&self) -> VarIntBoundsExceeded

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarIntBoundsExceeded

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Display for VarIntBoundsExceeded

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Error for VarIntBoundsExceeded

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> ConfigError

Converts to this type from the input type.
Source§

impl PartialEq for VarIntBoundsExceeded

Source§

fn eq(&self, other: &VarIntBoundsExceeded) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for VarIntBoundsExceeded

Source§

impl Eq for VarIntBoundsExceeded

Source§

impl StructuralPartialEq for VarIntBoundsExceeded

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn/struct.WeakConnectionHandle.html b/pr/234/docs/iroh_quinn/struct.WeakConnectionHandle.html new file mode 100644 index 000000000..0eb49bad9 --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.WeakConnectionHandle.html @@ -0,0 +1,24 @@ +WeakConnectionHandle in iroh_quinn - Rust

WeakConnectionHandle

Struct WeakConnectionHandle 

Source
pub struct WeakConnectionHandle(/* private fields */);
Expand description

A handle to some connection internals, use with care.

+

This contains a weak reference to the connection so will not itself keep the connection +alive.

+

Implementations§

Source§

impl WeakConnectionHandle

Source

pub fn is_alive(&self) -> bool

Returns true if the Connection associated with this handle is still alive.

+
Source

pub fn upgrade(&self) -> Option<Connection>

Upgrade the handle to a full Connection

+

Trait Implementations§

Source§

impl Clone for WeakConnectionHandle

Source§

fn clone(&self) -> WeakConnectionHandle

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WeakConnectionHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.Written.html b/pr/234/docs/iroh_quinn/struct.Written.html new file mode 100644 index 000000000..fd6b6473f --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.Written.html @@ -0,0 +1,31 @@ +Written in iroh_quinn - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/struct.ZeroRttAccepted.html b/pr/234/docs/iroh_quinn/struct.ZeroRttAccepted.html new file mode 100644 index 000000000..4282df6aa --- /dev/null +++ b/pr/234/docs/iroh_quinn/struct.ZeroRttAccepted.html @@ -0,0 +1,44 @@ +ZeroRttAccepted in iroh_quinn - Rust

ZeroRttAccepted

Struct ZeroRttAccepted 

Source
pub struct ZeroRttAccepted(/* private fields */);
Expand description

Future that completes when a connection is fully established

+

For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting +value is meaningless.

+

Trait Implementations§

Source§

impl Future for ZeroRttAccepted

Source§

type Output = bool

The type of value produced on completion.
Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

Attempts to resolve the future to a final value, registering +the current task for wakeup if the value is not yet available. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<F> FutureExt for F
where + F: Future + ?Sized,

§

fn poll(&mut self, cx: &mut Context<'_>) -> Poll<Self::Output>
where + Self: Unpin,

A convenience for calling Future::poll() on !Unpin types.
§

fn or<F>(self, other: F) -> Or<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, preferring self if both are ready. Read more
§

fn race<F>(self, other: F) -> Race<Self, F>
where + Self: Sized, + F: Future<Output = Self::Output>,

Returns the result of self or other future, with no preference if both are ready. Read more
§

fn catch_unwind(self) -> CatchUnwind<Self>
where + Self: Sized + UnwindSafe,

Catches panics while polling the future. Read more
§

fn boxed<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>
where + Self: Sized + Send + 'a,

Boxes the future and changes its type to dyn Future + Send + 'a. Read more
§

fn boxed_local<'a>(self) -> Pin<Box<dyn Future<Output = Self::Output> + 'a>>
where + Self: Sized + 'a,

Boxes the future and changes its type to dyn Future + 'a. Read more
§

impl<T> FutureExt for T
where + T: Future + ?Sized,

§

fn with_cancellation_token( + self, + cancellation_token: &CancellationToken, +) -> WithCancellationTokenFuture<'_, Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

fn with_cancellation_token_owned( + self, + cancellation_token: CancellationToken, +) -> WithCancellationTokenFutureOwned<Self>
where + Self: Sized,

Similar to [CancellationToken::run_until_cancelled_owned], +but with the advantage that it is easier to write fluent call chains, +and biased towards waiting for [CancellationToken] to complete. Read more
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<F> IntoFuture for F
where + F: Future,

Source§

type Output = <F as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = F

Which kind of future are we turning this into?
Source§

fn into_future(self) -> <F as IntoFuture>::IntoFuture

Creates a future from a value. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.AsyncTimer.html b/pr/234/docs/iroh_quinn/trait.AsyncTimer.html new file mode 100644 index 000000000..929ac841e --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.AsyncTimer.html @@ -0,0 +1,11 @@ +AsyncTimer in iroh_quinn - Rust

AsyncTimer

Trait AsyncTimer 

Source
pub trait AsyncTimer:
+    Send
+    + Debug
+    + 'static {
+    // Required methods
+    fn reset(self: Pin<&mut Self>, i: Instant);
+    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>;
+}
Expand description

Abstract implementation of an async timer for runtime independence

+

Required Methods§

Source

fn reset(self: Pin<&mut Self>, i: Instant)

Update the timer to expire at i

+
Source

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Check whether the timer has expired, and register to be woken if not

+

Implementations on Foreign Types§

Source§

impl AsyncTimer for Sleep

Source§

fn reset(self: Pin<&mut Self>, t: Instant)

Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Source§

impl AsyncTimer for Timer

Source§

fn reset(self: Pin<&mut Self>, t: Instant)

Source§

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.AsyncUdpSocket.html b/pr/234/docs/iroh_quinn/trait.AsyncUdpSocket.html new file mode 100644 index 000000000..56c423932 --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.AsyncUdpSocket.html @@ -0,0 +1,38 @@ +AsyncUdpSocket in iroh_quinn - Rust

AsyncUdpSocket

Trait AsyncUdpSocket 

Source
pub trait AsyncUdpSocket:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required methods
+    fn create_sender(&self) -> Pin<Box<dyn UdpSender>>;
+    fn poll_recv(
+        &mut self,
+        cx: &mut Context<'_>,
+        bufs: &mut [IoSliceMut<'_>],
+        meta: &mut [RecvMeta],
+    ) -> Poll<Result<usize>>;
+    fn local_addr(&self) -> Result<SocketAddr>;
+
+    // Provided methods
+    fn max_receive_segments(&self) -> usize { ... }
+    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) -> usize

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/234/docs/iroh_quinn/trait.ConnectionIdGenerator.html b/pr/234/docs/iroh_quinn/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..e7e811495 --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in iroh_quinn - Rust

ConnectionIdGenerator

Trait ConnectionIdGenerator 

Source
pub trait ConnectionIdGenerator: Send + Sync {
+    // Required methods
+    fn generate_cid(&mut self) -> ConnectionId;
+    fn cid_len(&self) -> usize;
+    fn cid_lifetime(&self) -> Option<Duration>;
+
+    // Provided method
+    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> { ... }
+}
Expand description

Generates connection IDs for incoming connections

+

Required Methods§

Source

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID

+

Connection IDs MUST NOT contain any information that can be used by +an external observer (that is, one that does not cooperate with the +issuer) to correlate them with other connection IDs for the same +connection. They MUST have high entropy, e.g. due to encrypted data +or cryptographic-grade random data.

+
Source

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator

+
Source

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs

+

Connection IDs will be retired after the returned Duration, if any. Assumed to be constant.

+

Provided Methods§

Source

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator

+

False positives are permitted, but increase the cost of handling invalid packets.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.QlogFactory.html b/pr/234/docs/iroh_quinn/trait.QlogFactory.html new file mode 100644 index 000000000..b9f402e30 --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in iroh_quinn - Rust

QlogFactory

Trait QlogFactory 

Source
pub trait QlogFactory:
+    Send
+    + Sync
+    + 'static {
+    // Required method
+    fn for_connection(
+        &self,
+        side: Side,
+        remote: SocketAddr,
+        initial_dst_cid: ConnectionId,
+        now: Instant,
+    ) -> Option<QlogConfig>;
+}
Expand description

Constructs a QlogConfig for individual connections.

+

This is set via [TransportConfig::qlog_factory].

+

Required Methods§

Source

fn for_connection( + &self, + side: Side, + remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled.

+

If None is returned, qlog capture is disabled for the connection.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.Runtime.html b/pr/234/docs/iroh_quinn/trait.Runtime.html new file mode 100644 index 000000000..6086e6da3 --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.Runtime.html @@ -0,0 +1,19 @@ +Runtime in iroh_quinn - Rust

Runtime

Trait Runtime 

Source
pub trait Runtime:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required methods
+    fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>;
+    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
+    fn wrap_udp_socket(&self, t: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>;
+
+    // Provided method
+    fn now(&self) -> Instant { ... }
+}
Expand description

Abstracts I/O and timer operations for runtime independence

+

Required Methods§

Source

fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>

Construct a timer that will expire at i

+
Source

fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>)

Drive future to completion in the background

+
Source

fn wrap_udp_socket(&self, t: UdpSocket) -> Result<Box<dyn AsyncUdpSocket>>

Convert t into the socket type used by this runtime

+

Provided Methods§

Source

fn now(&self) -> Instant

Look up the current time

+

Allows simulating the flow of time for testing.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.TimeSource.html b/pr/234/docs/iroh_quinn/trait.TimeSource.html new file mode 100644 index 000000000..f902865de --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in iroh_quinn - Rust

TimeSource

Trait TimeSource 

Source
pub trait TimeSource: Send + Sync {
+    // Required method
+    fn now(&self) -> SystemTime;
+}
Expand description

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Required Methods§

Source

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.TokenLog.html b/pr/234/docs/iroh_quinn/trait.TokenLog.html new file mode 100644 index 000000000..0b58ab6ec --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in iroh_quinn - Rust

TokenLog

Trait TokenLog 

Source
pub trait TokenLog: Send + Sync {
+    // Required method
+    fn check_and_insert(
+        &self,
+        nonce: u128,
+        issued: SystemTime,
+        lifetime: Duration,
+    ) -> Result<(), TokenReuseError>;
+}
Expand description

Responsible for limiting clients’ ability to reuse validation tokens

+

RFC 9000 § 8.1.4:

+
+

Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect +against such attacks, servers MUST ensure that replay of tokens is prevented or limited. +Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time, +as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames +(Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times. +Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include +additional information about clients to further narrow applicability or reuse.

+
+

TokenLog pertains only to tokens provided in NEW_TOKEN frames.

+

Required Methods§

Source

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously

+

False negatives and false positives are both permissible. Called when a client uses an +address validation token.

+

Parameters:

+
    +
  • nonce: A server-generated random unique value for the token.
  • +
  • issued: The time the server issued the token.
  • +
  • lifetime: The expiration time of address validation tokens sent via NEW_TOKEN frames, +as configured by ServerValidationTokenConfig::lifetime.
  • +
+
§Security & Performance
+

To the extent that it is possible to repeatedly trigger false negatives (returning Ok for +a token which has been reused), an attacker could use the server to perform amplification +attacks. The QUIC specification requires that this be limited, if not prevented fully.

+

A false positive (returning Err for a token which has never been used) is not a security +vulnerability; it is permissible for a TokenLog to always return Err. A false positive +causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be +delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.TokenStore.html b/pr/234/docs/iroh_quinn/trait.TokenStore.html new file mode 100644 index 000000000..b39308556 --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in iroh_quinn - Rust

TokenStore

Trait TokenStore 

Source
pub trait TokenStore: Send + Sync {
+    // Required methods
+    fn insert(&self, server_name: &str, token: Bytes);
+    fn take(&self, server_name: &str) -> Option<Bytes>;
+}
Expand description

Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections

+

Required Methods§

Source

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use

+

Called when a NEW_TOKEN frame is received from the server.

+
Source

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name

+

The same token must never be returned from take twice, as doing so can be used to +de-anonymize a client’s traffic.

+

Called when trying to connect to a server. It is always ok for this to return None.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn/trait.UdpSender.html b/pr/234/docs/iroh_quinn/trait.UdpSender.html new file mode 100644 index 000000000..32e52b60f --- /dev/null +++ b/pr/234/docs/iroh_quinn/trait.UdpSender.html @@ -0,0 +1,30 @@ +UdpSender in iroh_quinn - Rust

UdpSender

Trait UdpSender 

Source
pub trait UdpSender:
+    Send
+    + Sync
+    + Debug
+    + 'static {
+    // Required method
+    fn poll_send(
+        self: Pin<&mut Self>,
+        transmit: &Transmit<'_>,
+        cx: &mut Context<'_>,
+    ) -> Poll<Result<()>>;
+
+    // Provided method
+    fn max_transmit_segments(&self) -> usize { ... }
+}
Expand description

An object for asynchronously writing to an associated AsyncUdpSocket.

+

Any number of UdpSenders may exist for a single AsyncUdpSocket. Each UdpSender is +responsible for notifying at most one task for send readiness.

+

Required Methods§

Source

fn poll_send( + self: Pin<&mut Self>, + transmit: &Transmit<'_>, + cx: &mut Context<'_>, +) -> Poll<Result<()>>

Send a UDP datagram, or register to be woken if sending may succeed in the future.

+

Usually implementations of this will poll the socket for writability before trying to +write to them, and retry both if writing fails.

+

Quinn will create multiple UdpSenders, one for each task it’s using it from. Thus it’s +important to poll the underlying socket in a way that doesn’t overwrite wakers.

+

A single UdpSender will be reused, even if poll_send returns Poll::Ready once, +unlike Future::poll, so calling it again after readiness should not panic.

+

Provided Methods§

Source

fn max_transmit_segments(&self) -> usize

Maximum number of datagrams that a Transmit may encode.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/all.html b/pr/234/docs/iroh_quinn_proto/all.html new file mode 100644 index 000000000..bfaad14d6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Enums

Traits

Type Aliases

Constants

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html b/pr/234/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html new file mode 100644 index 000000000..b31125b10 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/bloom_token_log/struct.BloomTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.BloomTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..cf0d92970 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.HashedConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html new file mode 100644 index 000000000..3cc694461 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.InvalidCid.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.InvalidCid.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..a85350b7b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/cid_generator/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.RandomConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..ce1c7d62c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.ConnectionIdGenerator.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/coding/index.html b/pr/234/docs/iroh_quinn_proto/coding/index.html new file mode 100644 index 000000000..def698753 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/coding/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::coding - Rust

Module coding

Module coding 

Source
Expand description

Coding related traits.

+

Structs§

UnexpectedEnd
Error indicating that the provided buffer was too small

Traits§

Codec
Infallible encoding and decoding of QUIC primitives

Type Aliases§

Result
Coding result type
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/coding/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/coding/sidebar-items.js new file mode 100644 index 000000000..bf0be516a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/coding/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["UnexpectedEnd"],"trait":["Codec"],"type":["Result"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html b/pr/234/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html new file mode 100644 index 000000000..a0f6a5de9 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html @@ -0,0 +1,26 @@ +UnexpectedEnd in iroh_quinn_proto::coding - Rust

UnexpectedEnd

Struct UnexpectedEnd 

Source
pub struct UnexpectedEnd;
Expand description

Error indicating that the provided buffer was too small

+

Trait Implementations§

Source§

impl Clone for UnexpectedEnd

Source§

fn clone(&self) -> UnexpectedEnd

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UnexpectedEnd

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for UnexpectedEnd

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for UnexpectedEnd

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnexpectedEnd> for Error

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl From<UnexpectedEnd> for PacketDecodeError

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for UnexpectedEnd

Source§

fn eq(&self, other: &UnexpectedEnd) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for UnexpectedEnd

Source§

impl Eq for UnexpectedEnd

Source§

impl StructuralPartialEq for UnexpectedEnd

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/coding/trait.Codec.html b/pr/234/docs/iroh_quinn_proto/coding/trait.Codec.html new file mode 100644 index 000000000..04854a824 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/coding/trait.Codec.html @@ -0,0 +1,8 @@ +Codec in iroh_quinn_proto::coding - Rust

Codec

Trait Codec 

Source
pub trait Codec: Sized {
+    // Required methods
+    fn decode<B: Buf>(buf: &mut B) -> Result<Self>;
+    fn encode<B: BufMut>(&self, buf: &mut B);
+}
Expand description

Infallible encoding and 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

+
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 Codec for u8

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Codec for u16

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Codec for u32

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Codec for u64

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Codec for Ipv4Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Codec for Ipv6Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/coding/type.Result.html b/pr/234/docs/iroh_quinn_proto/coding/type.Result.html new file mode 100644 index 000000000..185875b69 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/coding/type.Result.html @@ -0,0 +1,7 @@ +Result in iroh_quinn_proto::coding - Rust

Result

Type Alias Result 

Source
pub type Result<T> = Result<T, UnexpectedEnd>;
Expand description

Coding result type

+

Aliased Type§

pub enum Result<T> {
+    Ok(T),
+    Err(UnexpectedEnd),
+}

Variants§

§1.0.0

Ok(T)

Contains the success value

+
§1.0.0

Err(UnexpectedEnd)

Contains the error value

+
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/enum.ConfigError.html b/pr/234/docs/iroh_quinn_proto/config/enum.ConfigError.html new file mode 100644 index 000000000..a2318c2f1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/enum.ConfigError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConfigError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html b/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html new file mode 100644 index 000000000..1e9d560a8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.QlogConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html b/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html new file mode 100644 index 000000000..6a7bb652e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/qlog/struct.QlogFileFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.QlogFileFactory.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html b/pr/234/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html new file mode 100644 index 000000000..e58263549 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/qlog/trait.QlogFactory.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/trait.QlogFactory.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/struct.ClientConfig.html b/pr/234/docs/iroh_quinn_proto/config/struct.ClientConfig.html new file mode 100644 index 000000000..72ad29942 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/struct.ClientConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ClientConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/struct.EndpointConfig.html b/pr/234/docs/iroh_quinn_proto/config/struct.EndpointConfig.html new file mode 100644 index 000000000..f787522dd --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/struct.EndpointConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.EndpointConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/struct.ServerConfig.html b/pr/234/docs/iroh_quinn_proto/config/struct.ServerConfig.html new file mode 100644 index 000000000..54e34e58b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/struct.ServerConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ServerConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/struct.StdSystemTime.html b/pr/234/docs/iroh_quinn_proto/config/struct.StdSystemTime.html new file mode 100644 index 000000000..a036660a0 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/struct.StdSystemTime.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.StdSystemTime.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html b/pr/234/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..71c171cbb --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/struct.ValidationTokenConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ValidationTokenConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/trait.TimeSource.html b/pr/234/docs/iroh_quinn_proto/config/trait.TimeSource.html new file mode 100644 index 000000000..531e889ff --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/trait.TimeSource.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TimeSource.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/234/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..b07cdbacc --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/transport/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html b/pr/234/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..f1cfe6e06 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/transport/struct.AckFrequencyConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.AckFrequencyConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html b/pr/234/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html new file mode 100644 index 000000000..d3b3774c0 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/transport/struct.IdleTimeout.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.IdleTimeout.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html b/pr/234/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..8f76f4934 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/transport/struct.MtuDiscoveryConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.MtuDiscoveryConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html b/pr/234/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html new file mode 100644 index 000000000..b849996c3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/config/transport/struct.TransportConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.TransportConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html b/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html new file mode 100644 index 000000000..ab7174506 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.Bbr.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.Bbr.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html new file mode 100644 index 000000000..0d5073995 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/bbr/struct.BbrConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.BbrConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html b/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html new file mode 100644 index 000000000..687e48b93 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.Cubic.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.Cubic.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html new file mode 100644 index 000000000..5c78ca8d6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/cubic/struct.CubicConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.CubicConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/index.html b/pr/234/docs/iroh_quinn_proto/congestion/index.html new file mode 100644 index 000000000..591662f1f --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::congestion - Rust

Module congestion

Module congestion 

Source
Expand description

Logic for controlling the rate at which data is sent

+

Structs§

Bbr
Experimental! Use at your own risk.
BbrConfig
Configuration for the Bbr congestion controller
ControllerMetrics
Common congestion controller metrics
Cubic
The RFC8312 congestion controller, as widely used for TCP
CubicConfig
Configuration for the Cubic congestion controller
NewReno
A simple, standard congestion controller
NewRenoConfig
Configuration for the NewReno congestion controller

Traits§

Controller
Common interface for different congestion controllers
ControllerFactory
Constructs controllers on demand
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html b/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html new file mode 100644 index 000000000..2722e12a4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewReno.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.NewReno.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html new file mode 100644 index 000000000..50d3f5805 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/new_reno/struct.NewRenoConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/congestion/struct.NewRenoConfig.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/congestion/sidebar-items.js new file mode 100644 index 000000000..888f550d4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Bbr","BbrConfig","ControllerMetrics","Cubic","CubicConfig","NewReno","NewRenoConfig"],"trait":["Controller","ControllerFactory"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.Bbr.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.Bbr.html new file mode 100644 index 000000000..e97a0da30 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.Bbr.html @@ -0,0 +1,45 @@ +Bbr in iroh_quinn_proto::congestion - Rust

Bbr

Struct Bbr 

Source
pub struct Bbr { /* private fields */ }
Expand description

Experimental! Use at your own risk.

+

Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks. +Based on google’s quiche implementation https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc +of BBR https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control. +More discussion and links at https://groups.google.com/g/bbr-dev.

+

Implementations§

Source§

impl Bbr

Source

pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Bbr

Source§

fn clone(&self) -> Bbr

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Bbr

Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_congestion_event( + &mut self, + _now: Instant, + _sent: Instant, + _is_persistent_congestion: bool, + _is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for Bbr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Bbr

§

impl RefUnwindSafe for Bbr

§

impl Send for Bbr

§

impl Sync for Bbr

§

impl Unpin for Bbr

§

impl UnwindSafe for Bbr

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html new file mode 100644 index 000000000..75ebb60f2 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.BbrConfig.html @@ -0,0 +1,26 @@ +BbrConfig in iroh_quinn_proto::congestion - Rust

BbrConfig

Struct BbrConfig 

Source
pub struct BbrConfig { /* private fields */ }
Expand description

Configuration for the Bbr congestion controller

+

Implementations§

Source§

impl BbrConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for BbrConfig

Source§

fn clone(&self) -> BbrConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for BbrConfig

Source§

fn build( + self: Arc<Self>, + _now: Instant, + current_mtu: u16, +) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for BbrConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for BbrConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html new file mode 100644 index 000000000..cdd37486e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.ControllerMetrics.html @@ -0,0 +1,25 @@ +ControllerMetrics in iroh_quinn_proto::congestion - Rust

ControllerMetrics

Struct ControllerMetrics 

Source
#[non_exhaustive]
pub struct ControllerMetrics { + pub congestion_window: u64, + pub ssthresh: Option<u64>, + pub pacing_rate: Option<u64>, +}
Expand description

Common congestion controller metrics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§congestion_window: u64

Congestion window (bytes)

+
§ssthresh: Option<u64>

Slow start threshold (bytes)

+
§pacing_rate: Option<u64>

Pacing rate (bits/s)

+

Trait Implementations§

Source§

impl Debug for ControllerMetrics

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ControllerMetrics

Source§

fn default() -> ControllerMetrics

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.Cubic.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.Cubic.html new file mode 100644 index 000000000..ae113dee2 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.Cubic.html @@ -0,0 +1,41 @@ +Cubic in iroh_quinn_proto::congestion - Rust

Cubic

Struct Cubic 

Source
pub struct Cubic { /* private fields */ }
Expand description

The RFC8312 congestion controller, as widely used for TCP

+

Implementations§

Source§

impl Cubic

Source

pub fn new(config: Arc<CubicConfig>, _now: Instant, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for Cubic

Source§

fn clone(&self) -> Cubic

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for Cubic

Source§

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

impl Debug for Cubic

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Cubic

§

impl RefUnwindSafe for Cubic

§

impl Send for Cubic

§

impl Sync for Cubic

§

impl Unpin for Cubic

§

impl UnwindSafe for Cubic

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html new file mode 100644 index 000000000..d6c784ac1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.CubicConfig.html @@ -0,0 +1,22 @@ +CubicConfig in iroh_quinn_proto::congestion - Rust

CubicConfig

Struct CubicConfig 

Source
pub struct CubicConfig { /* private fields */ }
Expand description

Configuration for the Cubic congestion controller

+

Implementations§

Source§

impl CubicConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+

Trait Implementations§

Source§

impl Clone for CubicConfig

Source§

fn clone(&self) -> CubicConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for CubicConfig

Source§

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for CubicConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for CubicConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.NewReno.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.NewReno.html new file mode 100644 index 000000000..4ff2fb0db --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.NewReno.html @@ -0,0 +1,41 @@ +NewReno in iroh_quinn_proto::congestion - Rust

NewReno

Struct NewReno 

Source
pub struct NewReno { /* private fields */ }
Expand description

A simple, standard congestion controller

+

Implementations§

Source§

impl NewReno

Source

pub fn new(config: Arc<NewRenoConfig>, now: Instant, current_mtu: u16) -> Self

Construct a state using the given config and current time now

+

Trait Implementations§

Source§

impl Clone for NewReno

Source§

fn clone(&self) -> NewReno

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Controller for NewReno

Source§

fn on_ack( + &mut self, + _now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + _rtt: &RttEstimator, +)

Packet deliveries were confirmed Read more
Source§

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + _is_ecn: bool, + _lost_bytes: u64, +)

Packets were deemed lost or marked congested Read more
Source§

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated
Source§

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight
Source§

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled
Source§

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state
Source§

fn initial_window(&self) -> u64

Initial congestion window
Source§

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details
Source§

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent
Source§

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.
Source§

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost Read more
Source§

impl Debug for NewReno

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html b/pr/234/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html new file mode 100644 index 000000000..c1e57143b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/struct.NewRenoConfig.html @@ -0,0 +1,23 @@ +NewRenoConfig in iroh_quinn_proto::congestion - Rust

NewRenoConfig

Struct NewRenoConfig 

Source
pub struct NewRenoConfig { /* private fields */ }
Expand description

Configuration for the NewReno congestion controller

+

Implementations§

Source§

impl NewRenoConfig

Source

pub fn initial_window(&mut self, value: u64) -> &mut Self

Default limit on the amount of outstanding data in bytes.

+

Recommended value: min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))

+
Source

pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self

Reduction in congestion window when a new loss event is detected.

+

Trait Implementations§

Source§

impl Clone for NewRenoConfig

Source§

fn clone(&self) -> NewRenoConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ControllerFactory for NewRenoConfig

Source§

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller
Source§

impl Debug for NewRenoConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for NewRenoConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/trait.Controller.html b/pr/234/docs/iroh_quinn_proto/congestion/trait.Controller.html new file mode 100644 index 000000000..b57591df3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/trait.Controller.html @@ -0,0 +1,79 @@ +Controller in iroh_quinn_proto::congestion - Rust

Controller

Trait Controller 

Source
pub trait Controller:
+    Send
+    + Sync
+    + Debug {
+    // Required methods
+    fn on_congestion_event(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        is_persistent_congestion: bool,
+        is_ecn: bool,
+        lost_bytes: u64,
+    );
+    fn on_mtu_update(&mut self, new_mtu: u16);
+    fn window(&self) -> u64;
+    fn clone_box(&self) -> Box<dyn Controller>;
+    fn initial_window(&self) -> u64;
+    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+
+    // Provided methods
+    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) { ... }
+    fn on_ack(
+        &mut self,
+        now: Instant,
+        sent: Instant,
+        bytes: u64,
+        app_limited: bool,
+        rtt: &RttEstimator,
+    ) { ... }
+    fn on_end_acks(
+        &mut self,
+        now: Instant,
+        in_flight: u64,
+        app_limited: bool,
+        largest_packet_num_acked: Option<u64>,
+    ) { ... }
+    fn on_spurious_congestion_event(&mut self) { ... }
+    fn metrics(&self) -> ControllerMetrics { ... }
+}
Expand description

Common interface for different congestion controllers

+

Required Methods§

Source

fn on_congestion_event( + &mut self, + now: Instant, + sent: Instant, + is_persistent_congestion: bool, + is_ecn: bool, + lost_bytes: u64, +)

Packets were deemed lost or marked congested

+

in_persistent_congestion indicates whether all packets sent within the persistent +congestion threshold period ending when the most recent packet in this batch was sent were +lost. +lost_bytes indicates how many bytes were lost. This value will be 0 for ECN triggers.

+
Source

fn on_mtu_update(&mut self, new_mtu: u16)

The known MTU for the current network path has been updated

+
Source

fn window(&self) -> u64

Number of ack-eliciting bytes that may be in flight

+
Source

fn clone_box(&self) -> Box<dyn Controller>

Duplicate the controller’s state

+
Source

fn initial_window(&self) -> u64

Initial congestion window

+
Source

fn into_any(self: Box<Self>) -> Box<dyn Any>

Returns Self for use in down-casting to extract implementation details

+

Provided Methods§

Source

fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64)

One or more packets were just sent

+
Source

fn on_ack( + &mut self, + now: Instant, + sent: Instant, + bytes: u64, + app_limited: bool, + rtt: &RttEstimator, +)

Packet deliveries were confirmed

+

app_limited indicates whether the connection was blocked on outgoing +application data prior to receiving these acknowledgements.

+
Source

fn on_end_acks( + &mut self, + now: Instant, + in_flight: u64, + app_limited: bool, + largest_packet_num_acked: Option<u64>, +)

Packets are acked in batches, all with the same now argument. This indicates one of those batches has completed.

+
Source

fn on_spurious_congestion_event(&mut self)

Packets were incorrectly deemed lost

+

This function is called when all packets that were deemed lost (for instance because +of packet reordering) are acknowledged after the congestion event was raised.

+
Source

fn metrics(&self) -> ControllerMetrics

Retrieve implementation-specific metrics used to populate qlog traces when they are enabled

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html b/pr/234/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html new file mode 100644 index 000000000..8e98dac9d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/congestion/trait.ControllerFactory.html @@ -0,0 +1,10 @@ +ControllerFactory in iroh_quinn_proto::congestion - Rust

ControllerFactory

Trait ControllerFactory 

Source
pub trait ControllerFactory {
+    // Required method
+    fn build(
+        self: Arc<Self>,
+        now: Instant,
+        current_mtu: u16,
+    ) -> Box<dyn Controller>;
+}
Expand description

Constructs controllers on demand

+

Required Methods§

Source

fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>

Construct a fresh Controller

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html b/pr/234/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html new file mode 100644 index 000000000..f7ac7a578 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/assembler/struct.Chunk.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Chunk.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html b/pr/234/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html new file mode 100644 index 000000000..addce8553 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/datagrams/enum.SendDatagramError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.SendDatagramError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html b/pr/234/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html new file mode 100644 index 000000000..240a4cef6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/datagrams/struct.Datagrams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Datagrams.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/enum.ClosePathError.html b/pr/234/docs/iroh_quinn_proto/connection/enum.ClosePathError.html new file mode 100644 index 000000000..73bb84ef9 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/enum.ClosePathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ClosePathError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/enum.ConnectionError.html b/pr/234/docs/iroh_quinn_proto/connection/enum.ConnectionError.html new file mode 100644 index 000000000..abbb38eab --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/enum.ConnectionError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConnectionError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/enum.Event.html b/pr/234/docs/iroh_quinn_proto/connection/enum.Event.html new file mode 100644 index 000000000..d1dc43357 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/enum.Event.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.Event.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/enum.PathError.html b/pr/234/docs/iroh_quinn_proto/connection/enum.PathError.html new file mode 100644 index 000000000..c45b49c08 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/enum.PathError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.PathError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html new file mode 100644 index 000000000..5002bde8e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.PathEvent.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html new file mode 100644 index 000000000..a886eee7b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.PathStatus.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.PathStatus.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html new file mode 100644 index 000000000..60829b195 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/enum.SetPathStatusError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.SetPathStatusError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html new file mode 100644 index 000000000..63b45525e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.ClosedPath.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ClosedPath.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/struct.PathId.html b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.PathId.html new file mode 100644 index 000000000..2fd79824a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.PathId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.PathId.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html new file mode 100644 index 000000000..21f95bb18 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/paths/struct.RttEstimator.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.RttEstimator.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html new file mode 100644 index 000000000..2102eceea --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.ConnectionStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ConnectionStats.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html new file mode 100644 index 000000000..9385ca7df --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.FrameStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.FrameStats.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html new file mode 100644 index 000000000..e561e9dcf --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.PathStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.PathStats.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html new file mode 100644 index 000000000..f606e4f17 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/stats/struct.UdpStats.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.UdpStats.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html b/pr/234/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html new file mode 100644 index 000000000..03531d047 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/enum.StreamEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/enum.StreamEvent.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html new file mode 100644 index 000000000..ebeb73d2e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.ReadError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html new file mode 100644 index 000000000..41f875c3e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/enum.ReadableError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.ReadableError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html new file mode 100644 index 000000000..2c3a1e2c2 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/recv/struct.Chunks.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/struct.Chunks.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html b/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html new file mode 100644 index 000000000..404821c56 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.FinishError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.FinishError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html b/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html new file mode 100644 index 000000000..a3a2799d8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/send/enum.WriteError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/enum.WriteError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html b/pr/234/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html new file mode 100644 index 000000000..886ef15f4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/send/struct.Written.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../iroh_quinn_proto/struct.Written.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html new file mode 100644 index 000000000..c276926d2 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ClosedStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ClosedStream.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html new file mode 100644 index 000000000..db77de12a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.RecvStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.RecvStream.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html new file mode 100644 index 000000000..84c2e122d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.SendStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.SendStream.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html new file mode 100644 index 000000000..8023ab946 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.ShouldTransmit.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.ShouldTransmit.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/streams/struct.Streams.html b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.Streams.html new file mode 100644 index 000000000..b3a078714 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/streams/struct.Streams.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../iroh_quinn_proto/struct.Streams.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/connection/struct.Connection.html b/pr/234/docs/iroh_quinn_proto/connection/struct.Connection.html new file mode 100644 index 000000000..6e3774403 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/connection/struct.Connection.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Connection.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html b/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html new file mode 100644 index 000000000..20952ccea --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED.html @@ -0,0 +1,3 @@ +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED in iroh_quinn_proto - Rust

DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED

Constant DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED 

Source
pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32;
Expand description

When multipath is required and has not been explicitly enabled, this value will be used for

+

TransportConfig::max_concurrent_multipath_paths.

+
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html b/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html new file mode 100644 index 000000000..44a5e72a5 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/constant.DEFAULT_SUPPORTED_VERSIONS.html @@ -0,0 +1,2 @@ +DEFAULT_SUPPORTED_VERSIONS in iroh_quinn_proto - Rust

DEFAULT_SUPPORTED_VERSIONS

Constant DEFAULT_SUPPORTED_VERSIONS 

Source
pub const DEFAULT_SUPPORTED_VERSIONS: &[u32];
Expand description

The QUIC protocol version implemented.

+
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/index.html b/pr/234/docs/iroh_quinn_proto/crypto/index.html new file mode 100644 index 000000000..f315f347c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/index.html @@ -0,0 +1,8 @@ +iroh_quinn_proto::crypto - Rust

Module crypto

Module crypto 

Source
Expand description

Traits and implementations for the QUIC cryptography protocol

+

The protocol logic in Quinn is contained in types that abstract over the actual +cryptographic protocol used. This module contains the traits used for this +abstraction layer as well as a single implementation of these traits that uses +ring and rustls to implement the TLS protocol support.

+

Note that usage of any protocol (version) other than TLS 1.3 does not conform to any +published versions of the specification, and will not be supported in QUIC v1.

+

Modules§

rustls
TLS interface based on rustls

Structs§

CryptoError
Generic crypto errors
ExportKeyingMaterialError
Error returned by Session::export_keying_material.
KeyPair
A pair of keys for bidirectional communication
Keys
A complete set of keys for a certain packet space
UnsupportedVersion
Error indicating that the specified QUIC version is not supported

Traits§

AeadKey
A key for sealing data with AEAD-based algorithms
ClientConfig
Client-side configuration for the crypto protocol
HandshakeTokenKey
A pseudo random key for HKDF
HeaderKey
Keys used to protect packet headers
HmacKey
A key for signing with HMAC-based algorithms
PacketKey
Keys used to protect packet payloads
ServerConfig
Server-side configuration for the crypto protocol
Session
A cryptographic session (commonly TLS)
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html new file mode 100644 index 000000000..f5adb2fed --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/enum.Error.html @@ -0,0 +1,94 @@ +Error in iroh_quinn_proto::crypto::rustls - Rust

Error

Enum Error 

#[non_exhaustive]
pub enum Error { +
Show 22 variants InappropriateMessage { + expect_types: Vec<ContentType>, + got_type: ContentType, + }, + InappropriateHandshakeMessage { + expect_types: Vec<HandshakeType>, + got_type: HandshakeType, + }, + InvalidEncryptedClientHello(EncryptedClientHelloError), + InvalidMessage(InvalidMessage), + NoCertificatesPresented, + UnsupportedNameType, + DecryptError, + EncryptError, + PeerIncompatible(PeerIncompatible), + PeerMisbehaved(PeerMisbehaved), + AlertReceived(AlertDescription), + InvalidCertificate(CertificateError), + InvalidCertRevocationList(CertRevocationListError), + General(String), + FailedToGetCurrentTime, + FailedToGetRandomBytes, + HandshakeNotComplete, + PeerSentOversizedRecord, + NoApplicationProtocol, + BadMaxFragmentSize, + InconsistentKeys(InconsistentKeys), + Other(OtherError), +
}
Expand description

rustls reports protocol errors using this type.

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

InappropriateMessage

We received a TLS message that isn’t valid right now. +expect_types lists the message types we can expect right now. +got_type is the type we found. This error is typically +caused by a buggy TLS stack (the peer or this one), a broken +network, or an attack.

+

Fields

§expect_types: Vec<ContentType>

Which types we expected

+
§got_type: ContentType

What type we received

+
§

InappropriateHandshakeMessage

We received a TLS handshake message that isn’t valid right now. +expect_types lists the handshake message types we can expect +right now. got_type is the type we found.

+

Fields

§expect_types: Vec<HandshakeType>

Which handshake type we expected

+
§got_type: HandshakeType

What handshake type we received

+
§

InvalidEncryptedClientHello(EncryptedClientHelloError)

An error occurred while handling Encrypted Client Hello (ECH).

+
§

InvalidMessage(InvalidMessage)

The peer sent us a TLS message with invalid contents.

+
§

NoCertificatesPresented

The peer didn’t give us any certificates.

+
§

UnsupportedNameType

The certificate verifier doesn’t support the given type of name.

+
§

DecryptError

We couldn’t decrypt a message. This is invariably fatal.

+
§

EncryptError

We couldn’t encrypt a message because it was larger than the allowed message size. +This should never happen if the application is using valid record sizes.

+
§

PeerIncompatible(PeerIncompatible)

The peer doesn’t support a protocol version/feature we require. +The parameter gives a hint as to what version/feature it is.

+
§

PeerMisbehaved(PeerMisbehaved)

The peer deviated from the standard TLS protocol. +The parameter gives a hint where.

+
§

AlertReceived(AlertDescription)

We received a fatal alert. This means the peer is unhappy.

+
§

InvalidCertificate(CertificateError)

We saw an invalid certificate.

+

The contained error is from the certificate validation trait +implementation.

+
§

InvalidCertRevocationList(CertRevocationListError)

A provided certificate revocation list (CRL) was invalid.

+
§

General(String)

A catch-all error for unlikely errors.

+
§

FailedToGetCurrentTime

We failed to figure out what time it currently is.

+
§

FailedToGetRandomBytes

We failed to acquire random bytes from the system.

+
§

HandshakeNotComplete

This function doesn’t work until the TLS handshake +is complete.

+
§

PeerSentOversizedRecord

The peer sent an oversized record/fragment.

+
§

NoApplicationProtocol

An incoming connection did not support any known application protocol.

+
§

BadMaxFragmentSize

The max_fragment_size value supplied in configuration was too small, +or too large.

+
§

InconsistentKeys(InconsistentKeys)

Specific failure cases from keys_match or a [crate::crypto::signer::SigningKey] that cannot produce a corresponding public key.

+
§

Other(OtherError)

Any other error.

+

This variant should only be used when the error is not better described by a more +specific variant. For example, if a custom crypto provider returns a +provider specific error.

+

Enums holding this variant will never compare equal to each other.

+

Trait Implementations§

§

impl Clone for Error

§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Debug for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Display for Error

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
§

impl From<CertRevocationListError> for Error

§

fn from(e: CertRevocationListError) -> Error

Converts to this type from the input type.
§

impl From<CertificateError> for Error

§

fn from(e: CertificateError) -> Error

Converts to this type from the input type.
§

impl From<EncryptedClientHelloError> for Error

§

fn from(e: EncryptedClientHelloError) -> Error

Converts to this type from the input type.
§

impl From<GetRandomFailed> for Error

§

fn from(_: GetRandomFailed) -> Error

Converts to this type from the input type.
§

impl From<InconsistentKeys> for Error

§

fn from(e: InconsistentKeys) -> Error

Converts to this type from the input type.
§

impl From<InvalidMessage> for Error

§

fn from(e: InvalidMessage) -> Error

Converts to this type from the input type.
§

impl From<OtherError> for Error

§

fn from(value: OtherError) -> Error

Converts to this type from the input type.
§

impl From<PeerIncompatible> for Error

§

fn from(e: PeerIncompatible) -> Error

Converts to this type from the input type.
§

impl From<PeerMisbehaved> for Error

§

fn from(e: PeerMisbehaved) -> Error

Converts to this type from the input type.
§

impl From<SystemTimeError> for Error

§

fn from(_: SystemTimeError) -> Error

Converts to this type from the input type.
§

impl From<UnsupportedOperationError> for Error

§

fn from(value: UnsupportedOperationError) -> Error

Converts to this type from the input type.
§

impl PartialEq for Error

§

fn eq(&self, other: &Error) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl !RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl !UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/crypto/rustls/index.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/index.html new file mode 100644 index 000000000..96919ee18 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::crypto::rustls - Rust

Module rustls

Module rustls 

Source
Expand description

TLS interface based on rustls

+

Structs§

HandshakeData
Authentication data for (rustls) TLS session
NoInitialCipherSuite
The initial cipher suite (AES-128-GCM-SHA256) is not available
QuicClientConfig
A QUIC-compatible TLS client configuration
QuicServerConfig
A QUIC-compatible TLS server configuration
TlsSession
A rustls TLS session

Enums§

Error
rustls reports protocol errors using this type.
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js new file mode 100644 index 000000000..65f98d4d4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["HandshakeData","NoInitialCipherSuite","QuicClientConfig","QuicServerConfig","TlsSession"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html new file mode 100644 index 000000000..587cb6b50 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.HandshakeData.html @@ -0,0 +1,27 @@ +HandshakeData in iroh_quinn_proto::crypto::rustls - Rust

HandshakeData

Struct HandshakeData 

Source
pub struct HandshakeData {
+    pub protocol: Option<Vec<u8>>,
+    pub server_name: Option<String>,
+    pub negotiated_key_exchange_group: NamedGroup,
+}
Expand description

Authentication data for (rustls) TLS session

+

Fields§

§protocol: Option<Vec<u8>>

The negotiated application protocol, if ALPN is in use

+

Guaranteed to be set if a nonempty list of protocols was specified for this connection.

+
§server_name: Option<String>

The server name specified by the client, if any

+

Always None for outgoing connections

+
§negotiated_key_exchange_group: NamedGroup

The key exchange group negotiated with the peer

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html new file mode 100644 index 000000000..eb44f7208 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.NoInitialCipherSuite.html @@ -0,0 +1,24 @@ +NoInitialCipherSuite in iroh_quinn_proto::crypto::rustls - Rust

NoInitialCipherSuite

Struct NoInitialCipherSuite 

Source
pub struct NoInitialCipherSuite { /* private fields */ }
Expand description

The initial cipher suite (AES-128-GCM-SHA256) is not available

+

When the cipher suite is supplied with_initial(), it must be +[CipherSuite::TLS13_AES_128_GCM_SHA256]. When the cipher suite is derived from a config’s +CryptoProvider, that provider must reference a cipher suite with the same ID.

+

Trait Implementations§

Source§

impl Clone for NoInitialCipherSuite

Source§

fn clone(&self) -> NoInitialCipherSuite

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for NoInitialCipherSuite

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for NoInitialCipherSuite

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html new file mode 100644 index 000000000..52f9217b8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicClientConfig.html @@ -0,0 +1,41 @@ +QuicClientConfig in iroh_quinn_proto::crypto::rustls - Rust

QuicClientConfig

Struct QuicClientConfig 

Source
pub struct QuicClientConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS client configuration

+

Quinn implicitly constructs a QuicClientConfig with reasonable defaults within +ClientConfig::with_root_certificates() and ClientConfig::with_platform_verifier(). +Alternatively, QuicClientConfig’s TryFrom implementation can be used to wrap around a +custom [rustls::ClientConfig], in which case care should be taken around certain points:

+
    +
  • If enable_early_data is not set to true, then sending 0-RTT data will not be possible on +outgoing connections.
  • +
  • The [rustls::ClientConfig] must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

The object in the resumption field of the inner [rustls::ClientConfig] determines whether +calling into_0rtt on outgoing connections returns Ok or Err. It typically allows +into_0rtt to proceed if it recognizes the server name, and defaults to an in-memory cache of +256 server names.

+

Implementations§

Source§

impl QuicClientConfig

Source

pub fn with_initial( + inner: Arc<ClientConfig>, + initial: Suite, +) -> Result<Self, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ClientConfig for QuicClientConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration
Source§

impl TryFrom<Arc<ClientConfig>> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: Arc<ClientConfig>) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<ClientConfig> for QuicClientConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: ClientConfig) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html new file mode 100644 index 000000000..c0e779f59 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.QuicServerConfig.html @@ -0,0 +1,45 @@ +QuicServerConfig in iroh_quinn_proto::crypto::rustls - Rust

QuicServerConfig

Struct QuicServerConfig 

Source
pub struct QuicServerConfig { /* private fields */ }
Expand description

A QUIC-compatible TLS server configuration

+

Quinn implicitly constructs a QuicServerConfig with reasonable defaults within +ServerConfig::with_single_cert(). Alternatively, QuicServerConfig’s TryFrom +implementation or with_initial method can be used to wrap around a custom +[rustls::ServerConfig], in which case care should be taken around certain points:

+
    +
  • If max_early_data_size is not set to u32::MAX, the server will not be able to accept +incoming 0-RTT data. QUIC prohibits max_early_data_size values other than 0 or u32::MAX.
  • +
  • The rustls::ServerConfig must have TLS 1.3 support enabled for conversion to succeed.
  • +
+

Implementations§

Source§

impl QuicServerConfig

Source

pub fn with_initial( + inner: Arc<ServerConfig>, + initial: Suite, +) -> Result<Self, NoInitialCipherSuite>

Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite

+

This is useful if you want to avoid the initial cipher suite for traffic encryption.

+

Trait Implementations§

Source§

impl ServerConfig for QuicServerConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more
Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

impl TryFrom<Arc<ServerConfig>> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: Arc<ServerConfig>) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<ServerConfig> for QuicServerConfig

Source§

type Error = NoInitialCipherSuite

The type returned in the event of a conversion error.
Source§

fn try_from(inner: ServerConfig) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html new file mode 100644 index 000000000..bae5019d5 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/rustls/struct.TlsSession.html @@ -0,0 +1,33 @@ +TlsSession in iroh_quinn_proto::crypto::rustls - Rust

TlsSession

Struct TlsSession 

Source
pub struct TlsSession { /* private fields */ }
Expand description

A rustls TLS session

+

Trait Implementations§

Source§

impl Session for TlsSession

Source§

fn peer_identity(&self) -> Option<Box<dyn Any>>

For the rustls TlsSession, the Any type is Vec<rustls::pki_types::CertificateDer>

+
Source§

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available Read more
Source§

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only) Read more
Source§

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer
Source§

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.
Source§

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>

Read bytes of handshake data Read more
Source§

fn transport_parameters( + &self, +) -> Result<Option<TransportParameters>, TransportError>

The peer’s QUIC transport parameters Read more
Source§

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys Read more
Source§

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update
Source§

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet
Source§

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/crypto/sidebar-items.js new file mode 100644 index 000000000..375413e92 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["rustls"],"struct":["CryptoError","ExportKeyingMaterialError","KeyPair","Keys","UnsupportedVersion"],"trait":["AeadKey","ClientConfig","HandshakeTokenKey","HeaderKey","HmacKey","PacketKey","ServerConfig","Session"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/struct.CryptoError.html b/pr/234/docs/iroh_quinn_proto/crypto/struct.CryptoError.html new file mode 100644 index 000000000..b13379875 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/struct.CryptoError.html @@ -0,0 +1,18 @@ +CryptoError in iroh_quinn_proto::crypto - Rust

CryptoError

Struct CryptoError 

Source
pub struct CryptoError;
Expand description

Generic crypto errors

+

Trait Implementations§

Source§

impl Debug for CryptoError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Unspecified> for CryptoError

Source§

fn from(_: Unspecified) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html b/pr/234/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html new file mode 100644 index 000000000..bf8d49eef --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/struct.ExportKeyingMaterialError.html @@ -0,0 +1,24 @@ +ExportKeyingMaterialError in iroh_quinn_proto::crypto - Rust

ExportKeyingMaterialError

Struct ExportKeyingMaterialError 

Source
pub struct ExportKeyingMaterialError;
Expand description

Error returned by Session::export_keying_material.

+

This error occurs if the requested output length is too large.

+

Trait Implementations§

Source§

impl Debug for ExportKeyingMaterialError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ExportKeyingMaterialError

Source§

fn eq(&self, other: &ExportKeyingMaterialError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ExportKeyingMaterialError

Source§

impl StructuralPartialEq for ExportKeyingMaterialError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/struct.KeyPair.html b/pr/234/docs/iroh_quinn_proto/crypto/struct.KeyPair.html new file mode 100644 index 000000000..94106d6fa --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/struct.KeyPair.html @@ -0,0 +1,29 @@ +KeyPair in iroh_quinn_proto::crypto - Rust

KeyPair

Struct KeyPair 

Source
pub struct KeyPair<T> {
+    pub local: T,
+    pub remote: T,
+}
Expand description

A pair of keys for bidirectional communication

+

Fields§

§local: T

Key for encrypting data

+
§remote: T

Key for decrypting data

+

Auto Trait Implementations§

§

impl<T> Freeze for KeyPair<T>
where + T: Freeze,

§

impl<T> RefUnwindSafe for KeyPair<T>
where + T: RefUnwindSafe,

§

impl<T> Send for KeyPair<T>
where + T: Send,

§

impl<T> Sync for KeyPair<T>
where + T: Sync,

§

impl<T> Unpin for KeyPair<T>
where + T: Unpin,

§

impl<T> UnwindSafe for KeyPair<T>
where + T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/struct.Keys.html b/pr/234/docs/iroh_quinn_proto/crypto/struct.Keys.html new file mode 100644 index 000000000..26349130c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/struct.Keys.html @@ -0,0 +1,23 @@ +Keys in iroh_quinn_proto::crypto - Rust

Keys

Struct Keys 

Source
pub struct Keys {
+    pub header: KeyPair<Box<dyn HeaderKey>>,
+    pub packet: KeyPair<Box<dyn PacketKey>>,
+}
Expand description

A complete set of keys for a certain packet space

+

Fields§

§header: KeyPair<Box<dyn HeaderKey>>

Header protection keys

+
§packet: KeyPair<Box<dyn PacketKey>>

Packet protection keys

+

Auto Trait Implementations§

§

impl Freeze for Keys

§

impl !RefUnwindSafe for Keys

§

impl Send for Keys

§

impl Sync for Keys

§

impl Unpin for Keys

§

impl !UnwindSafe for Keys

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html b/pr/234/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html new file mode 100644 index 000000000..8ff84fb86 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/struct.UnsupportedVersion.html @@ -0,0 +1,18 @@ +UnsupportedVersion in iroh_quinn_proto::crypto - Rust

UnsupportedVersion

Struct UnsupportedVersion 

Source
pub struct UnsupportedVersion;
Expand description

Error indicating that the specified QUIC version is not supported

+

Trait Implementations§

Source§

impl Debug for UnsupportedVersion

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.AeadKey.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.AeadKey.html new file mode 100644 index 000000000..e1abdf405 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.AeadKey.html @@ -0,0 +1,32 @@ +AeadKey in iroh_quinn_proto::crypto - Rust

AeadKey

Trait AeadKey 

Source
pub trait AeadKey {
+    // Required methods
+    fn seal(
+        &self,
+        data: &mut Vec<u8>,
+        additional_data: &[u8],
+    ) -> Result<(), CryptoError>;
+    fn open<'a>(
+        &self,
+        data: &'a mut [u8],
+        additional_data: &[u8],
+    ) -> Result<&'a mut [u8], CryptoError>;
+}
Expand description

A key for sealing data with AEAD-based algorithms

+

Required Methods§

Source

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Method for sealing message data

+
Source

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Method for opening a sealed message data

+

Implementations on Foreign Types§

Source§

impl AeadKey for LessSafeKey

Source§

fn seal( + &self, + data: &mut Vec<u8>, + additional_data: &[u8], +) -> Result<(), CryptoError>

Source§

fn open<'a>( + &self, + data: &'a mut [u8], + additional_data: &[u8], +) -> Result<&'a mut [u8], CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html new file mode 100644 index 000000000..b45349488 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.ClientConfig.html @@ -0,0 +1,16 @@ +ClientConfig in iroh_quinn_proto::crypto - Rust

ClientConfig

Trait ClientConfig 

Source
pub trait ClientConfig: Send + Sync {
+    // Required method
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        server_name: &str,
+        params: &TransportParameters,
+    ) -> Result<Box<dyn Session>, ConnectError>;
+}
Expand description

Client-side configuration for the crypto protocol

+

Required Methods§

Source

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html new file mode 100644 index 000000000..ca4a39808 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.html @@ -0,0 +1,6 @@ +HandshakeTokenKey in iroh_quinn_proto::crypto - Rust

HandshakeTokenKey

Trait HandshakeTokenKey 

Source
pub trait HandshakeTokenKey: Send + Sync {
+    // Required method
+    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+}
Expand description

A pseudo random key for HKDF

+

Required Methods§

Source

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Derive AEAD using hkdf

+

Implementations on Foreign Types§

Source§

impl HandshakeTokenKey for Prk

Source§

fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html new file mode 100644 index 000000000..cc36c1859 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.HeaderKey.html @@ -0,0 +1,10 @@ +HeaderKey in iroh_quinn_proto::crypto - Rust

HeaderKey

Trait HeaderKey 

Source
pub trait HeaderKey: Send + Sync {
+    // Required methods
+    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+    fn sample_size(&self) -> usize;
+}
Expand description

Keys used to protect packet headers

+

Required Methods§

Source

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Decrypt the given packet’s header

+
Source

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Encrypt the given packet’s header

+
Source

fn sample_size(&self) -> usize

The sample size used for this key’s algorithm

+

Implementations on Foreign Types§

Source§

impl HeaderKey for Box<dyn HeaderProtectionKey>

Source§

fn decrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn encrypt(&self, pn_offset: usize, packet: &mut [u8])

Source§

fn sample_size(&self) -> usize

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.HmacKey.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.HmacKey.html new file mode 100644 index 000000000..26cf8079b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.HmacKey.html @@ -0,0 +1,10 @@ +HmacKey in iroh_quinn_proto::crypto - Rust

HmacKey

Trait HmacKey 

Source
pub trait HmacKey: Send + Sync {
+    // Required methods
+    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+    fn signature_len(&self) -> usize;
+    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+}
Expand description

A key for signing with HMAC-based algorithms

+

Required Methods§

Source

fn sign(&self, data: &[u8], signature_out: &mut [u8])

Method for signing a message

+
Source

fn signature_len(&self) -> usize

Length of sign’s output

+
Source

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Method for verifying a message

+

Implementations on Foreign Types§

Source§

impl HmacKey for Key

Source§

fn sign(&self, data: &[u8], out: &mut [u8])

Source§

fn signature_len(&self) -> usize

Source§

fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.PacketKey.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.PacketKey.html new file mode 100644 index 000000000..737f4ef77 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.PacketKey.html @@ -0,0 +1,51 @@ +PacketKey in iroh_quinn_proto::crypto - Rust

PacketKey

Trait PacketKey 

Source
pub trait PacketKey: Send + Sync {
+    // Required methods
+    fn encrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        buf: &mut [u8],
+        header_len: usize,
+    );
+    fn decrypt(
+        &self,
+        path_id: PathId,
+        packet: u64,
+        header: &[u8],
+        payload: &mut BytesMut,
+    ) -> Result<(), CryptoError>;
+    fn tag_len(&self) -> usize;
+    fn confidentiality_limit(&self) -> u64;
+    fn integrity_limit(&self) -> u64;
+}
Expand description

Keys used to protect packet payloads

+

Required Methods§

Source

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Encrypt the packet payload with the given packet number

+
Source

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Decrypt the packet payload with the given packet number

+
Source

fn tag_len(&self) -> usize

The length of the AEAD tag appended to packets on encryption

+
Source

fn confidentiality_limit(&self) -> u64

Maximum number of packets that may be sent using a single key

+
Source

fn integrity_limit(&self) -> u64

Maximum number of incoming packets that may fail decryption before the connection must be +abandoned

+

Implementations on Foreign Types§

Source§

impl PacketKey for Box<dyn PacketKey>

Source§

fn encrypt( + &self, + path_id: PathId, + packet: u64, + buf: &mut [u8], + header_len: usize, +)

Source§

fn decrypt( + &self, + path_id: PathId, + packet: u64, + header: &[u8], + payload: &mut BytesMut, +) -> Result<(), CryptoError>

Source§

fn tag_len(&self) -> usize

Source§

fn confidentiality_limit(&self) -> u64

Source§

fn integrity_limit(&self) -> u64

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html new file mode 100644 index 000000000..d540cbd8e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.ServerConfig.html @@ -0,0 +1,38 @@ +ServerConfig in iroh_quinn_proto::crypto - Rust

ServerConfig

Trait ServerConfig 

Source
pub trait ServerConfig: Send + Sync {
+    // Required methods
+    fn initial_keys(
+        &self,
+        version: u32,
+        dst_cid: ConnectionId,
+    ) -> Result<Keys, UnsupportedVersion>;
+    fn retry_tag(
+        &self,
+        version: u32,
+        orig_dst_cid: ConnectionId,
+        packet: &[u8],
+    ) -> [u8; 16];
+    fn start_session(
+        self: Arc<Self>,
+        version: u32,
+        params: &TransportParameters,
+    ) -> Box<dyn Session>;
+}
Expand description

Server-side configuration for the crypto protocol

+

Required Methods§

Source

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet

+

Never called if initial_keys rejected version.

+
Source

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration

+

Never called if initial_keys rejected version.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/crypto/trait.Session.html b/pr/234/docs/iroh_quinn_proto/crypto/trait.Session.html new file mode 100644 index 000000000..cb10cecaa --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/crypto/trait.Session.html @@ -0,0 +1,72 @@ +Session in iroh_quinn_proto::crypto - Rust

Session

Trait Session 

Source
pub trait Session:
+    Send
+    + Sync
+    + 'static {
+    // Required methods
+    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+    fn early_data_accepted(&self) -> Option<bool>;
+    fn is_handshaking(&self) -> bool;
+    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
+    fn transport_parameters(
+        &self,
+    ) -> Result<Option<TransportParameters>, TransportError>;
+    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+    fn is_valid_retry(
+        &self,
+        orig_dst_cid: ConnectionId,
+        header: &[u8],
+        payload: &[u8],
+    ) -> bool;
+    fn export_keying_material(
+        &self,
+        output: &mut [u8],
+        label: &[u8],
+        context: &[u8],
+    ) -> Result<(), ExportKeyingMaterialError>;
+}
Expand description

A cryptographic session (commonly TLS)

+

Required Methods§

Source

fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys

Create the initial set of keys given the client’s initial destination ConnectionId

+
Source

fn handshake_data(&self) -> Option<Box<dyn Any>>

Get data negotiated during the handshake, if available

+

Returns None until the connection emits HandshakeDataReady.

+
Source

fn peer_identity(&self) -> Option<Box<dyn Any>>

Get the peer’s identity, if available

+
Source

fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>

Get the 0-RTT keys if available (clients only)

+

On the client side, this method can be used to see if 0-RTT key material is available +to start sending data before the protocol handshake has completed.

+

Returns None if the key material is not available. This might happen if you have +not connected to this server before.

+
Source

fn early_data_accepted(&self) -> Option<bool>

If the 0-RTT-encrypted data has been accepted by the peer

+
Source

fn is_handshaking(&self) -> bool

Returns true until the connection is fully established.

+
Source

fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>

Read bytes of handshake data

+

This should be called with the contents of CRYPTO frames. If it returns Ok, the +caller should call write_handshake() to check if the crypto protocol has anything +to send to the peer. This method will only return true the first time that +handshake data is available. Future calls will always return false.

+

On success, returns true iff self.handshake_data() has been populated.

+
Source

fn transport_parameters( + &self, +) -> Result<Option<TransportParameters>, TransportError>

The peer’s QUIC transport parameters

+

These are only available after the first flight from the peer has been received.

+
Source

fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>

Writes handshake bytes into the given buffer and optionally returns the negotiated keys

+

When the handshake proceeds to the next phase, this method will return a new set of +keys to encrypt data with.

+
Source

fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>

Compute keys for the next key update

+
Source

fn is_valid_retry( + &self, + orig_dst_cid: ConnectionId, + header: &[u8], + payload: &[u8], +) -> bool

Verify the integrity of a retry packet

+
Source

fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: &[u8], +) -> Result<(), ExportKeyingMaterialError>

Fill output with output.len() bytes of keying material derived +from the Session’s secrets, using label and context for domain +separation.

+

This function will fail, returning ExportKeyingMaterialError, +if the requested output length is too large.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html b/pr/234/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html new file mode 100644 index 000000000..d80e75b8e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/enum.ConnectError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ConnectError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html b/pr/234/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html new file mode 100644 index 000000000..dd7b60907 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/enum.DatagramEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.DatagramEvent.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html b/pr/234/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html new file mode 100644 index 000000000..dc615b45d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/struct.AcceptError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.AcceptError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html b/pr/234/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html new file mode 100644 index 000000000..72a5a9b92 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/struct.ConnectionHandle.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionHandle.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html b/pr/234/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html new file mode 100644 index 000000000..80d25b39b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/struct.Endpoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Endpoint.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/struct.Incoming.html b/pr/234/docs/iroh_quinn_proto/endpoint/struct.Incoming.html new file mode 100644 index 000000000..4c2ebf7d4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/struct.Incoming.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Incoming.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/endpoint/struct.RetryError.html b/pr/234/docs/iroh_quinn_proto/endpoint/struct.RetryError.html new file mode 100644 index 000000000..8df786829 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/endpoint/struct.RetryError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.RetryError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/234/docs/iroh_quinn_proto/enum.ClosePathError.html new file mode 100644 index 000000000..189159922 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ClosePathError.html @@ -0,0 +1,31 @@ +ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+    ClosedPath,
+    LastOpenPath,
+}
Expand description

Errors triggered when abandoning a path

+

Variants§

§

ClosedPath

The path is already closed or was never opened

+
§

LastOpenPath

This is the last path, which can not be abandoned

+

Trait Implementations§

Source§

impl Clone for ClosePathError

Source§

fn clone(&self) -> ClosePathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosePathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ClosePathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosePathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosePathError

Source§

fn eq(&self, other: &ClosePathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosePathError

Source§

impl StructuralPartialEq for ClosePathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.ConfigError.html b/pr/234/docs/iroh_quinn_proto/enum.ConfigError.html new file mode 100644 index 000000000..28e4c31e4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ConfigError.html @@ -0,0 +1,29 @@ +ConfigError in iroh_quinn_proto - Rust

ConfigError

Enum ConfigError 

Source
#[non_exhaustive]
pub enum ConfigError { + OutOfBounds, +}
Expand description

Errors in the configuration of an endpoint

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

OutOfBounds

Value exceeds supported bounds

+

Trait Implementations§

Source§

impl Clone for ConfigError

Source§

fn clone(&self) -> ConfigError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConfigError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConfigError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConfigError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<TryFromIntError> for ConfigError

Source§

fn from(_: TryFromIntError) -> Self

Converts to this type from the input type.
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConfigError

Source§

fn eq(&self, other: &ConfigError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConfigError

Source§

impl StructuralPartialEq for ConfigError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.ConnectError.html b/pr/234/docs/iroh_quinn_proto/enum.ConnectError.html new file mode 100644 index 000000000..8b4d113ae --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ConnectError.html @@ -0,0 +1,44 @@ +ConnectError in iroh_quinn_proto - Rust

ConnectError

Enum ConnectError 

Source
pub enum ConnectError {
+    EndpointStopping,
+    CidsExhausted,
+    InvalidServerName(String),
+    InvalidRemoteAddress(SocketAddr),
+    NoDefaultClientConfig,
+    UnsupportedVersion,
+}
Expand description

Errors in the parameters being used to create a new connection

+

These arise before any I/O has been performed.

+

Variants§

§

EndpointStopping

The endpoint can no longer create new connections

+

Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs

+
§

InvalidServerName(String)

The given server name was malformed

+
§

InvalidRemoteAddress(SocketAddr)

The remote SocketAddr supplied was malformed

+

Examples include attempting to connect to port 0, or using an inappropriate address family.

+
§

NoDefaultClientConfig

No default client configuration was set up

+

Use Endpoint::connect_with to specify a client configuration.

+
§

UnsupportedVersion

The local endpoint does not support the QUIC version specified in the client configuration

+

Trait Implementations§

Source§

impl Clone for ConnectError

Source§

fn clone(&self) -> ConnectError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnsupportedVersion> for ConnectError

Source§

fn from(_: UnsupportedVersion) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectError

Source§

fn eq(&self, other: &ConnectError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectError

Source§

impl StructuralPartialEq for ConnectError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/234/docs/iroh_quinn_proto/enum.ConnectionError.html new file mode 100644 index 000000000..b8d607350 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ConnectionError.html @@ -0,0 +1,47 @@ +ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+    VersionMismatch,
+    TransportError(TransportError),
+    ConnectionClosed(ConnectionClose),
+    ApplicationClosed(ApplicationClose),
+    Reset,
+    TimedOut,
+    LocallyClosed,
+    CidsExhausted,
+}
Expand description

Reasons why a connection might be lost

+

Variants§

§

VersionMismatch

The peer doesn’t implement any supported version

+
§

TransportError(TransportError)

The peer violated the QUIC specification as understood by this implementation

+
§

ConnectionClosed(ConnectionClose)

The peer’s QUIC stack aborted the connection automatically

+
§

ApplicationClosed(ApplicationClose)

The peer closed the connection

+
§

Reset

The peer is unable to continue processing this connection, usually due to having restarted

+
§

TimedOut

Communication with the peer has lapsed for longer than the negotiated idle timeout

+

If neither side is sending keep-alives, a connection will time out after a long enough idle +period even if the peer is still reachable. See also TransportConfig::max_idle_timeout() +and TransportConfig::keep_alive_interval().

+
§

LocallyClosed

The local application closed the connection

+
§

CidsExhausted

The connection could not be created because not enough of the CID space is available

+

Try using longer connection IDs.

+

Trait Implementations§

Source§

impl Clone for ConnectionError

Source§

fn clone(&self) -> ConnectionError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ConnectionError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ConnectionError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ConnectionError> for Error

Source§

fn from(x: ConnectionError) -> Self

Converts to this type from the input type.
Source§

impl From<Error> for ConnectionError

Source§

fn from(source: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionError

Source§

fn eq(&self, other: &ConnectionError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ConnectionError

Source§

impl StructuralPartialEq for ConnectionError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.DatagramEvent.html b/pr/234/docs/iroh_quinn_proto/enum.DatagramEvent.html new file mode 100644 index 000000000..197e558ea --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.DatagramEvent.html @@ -0,0 +1,25 @@ +DatagramEvent in iroh_quinn_proto - Rust

DatagramEvent

Enum DatagramEvent 

Source
pub enum DatagramEvent {
+    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+    NewConnection(Incoming),
+    Response(Transmit),
+}
Expand description

Event resulting from processing a single datagram

+

Variants§

§

ConnectionEvent(ConnectionHandle, ConnectionEvent)

The datagram is redirected to its Connection

+
§

NewConnection(Incoming)

The datagram may result in starting a new Connection

+
§

Response(Transmit)

Response generated directly by the endpoint

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.Dir.html b/pr/234/docs/iroh_quinn_proto/enum.Dir.html new file mode 100644 index 000000000..19f97c0c8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.Dir.html @@ -0,0 +1,46 @@ +Dir in iroh_quinn_proto - Rust

Dir

Enum Dir 

Source
pub enum Dir {
+    Bi = 0,
+    Uni = 1,
+}
Expand description

Whether a stream communicates data in both directions or only from the initiator

+

Variants§

§

Bi = 0

Data flows in both directions

+
§

Uni = 1

Data flows only from the stream’s initiator

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Dir

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Dir

Source§

fn clone(&self) -> Dir

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Dir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Dir> for StreamType

Available on crate feature qlog only.
Source§

fn from(value: Dir) -> Self

Converts to this type from the input type.
Source§

impl Hash for Dir

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Dir

Source§

fn cmp(&self, other: &Dir) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Dir

Source§

fn eq(&self, other: &Dir) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Dir

Source§

fn partial_cmp(&self, other: &Dir) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Dir

Source§

impl Eq for Dir

Source§

impl StructuralPartialEq for Dir

Auto Trait Implementations§

§

impl Freeze for Dir

§

impl RefUnwindSafe for Dir

§

impl Send for Dir

§

impl Sync for Dir

§

impl Unpin for Dir

§

impl UnwindSafe for Dir

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.EcnCodepoint.html b/pr/234/docs/iroh_quinn_proto/enum.EcnCodepoint.html new file mode 100644 index 000000000..71add470d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.EcnCodepoint.html @@ -0,0 +1,34 @@ +EcnCodepoint in iroh_quinn_proto - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<Self>

Create new object from the given bits

+
Source

pub fn is_ce(self) -> bool

Returns whether the codepoint is a CE, signalling that congestion was experienced

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.Event.html b/pr/234/docs/iroh_quinn_proto/enum.Event.html new file mode 100644 index 000000000..ba3fe5e2d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.Event.html @@ -0,0 +1,41 @@ +Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+    HandshakeDataReady,
+    Connected,
+    HandshakeConfirmed,
+    ConnectionLost {
+        reason: ConnectionError,
+    },
+    Stream(StreamEvent),
+    DatagramReceived,
+    DatagramsUnblocked,
+    Path(PathEvent),
+    NatTraversal(Event),
+}
Expand description

Events of interest to the application

+

Variants§

§

HandshakeDataReady

The connection’s handshake data is ready

+
§

Connected

The connection was successfully established

+
§

HandshakeConfirmed

The TLS handshake was confirmed

+
§

ConnectionLost

The connection was lost

+

Emitted if the peer closes the connection or an error is encountered.

+

Fields

§reason: ConnectionError

Reason that the connection was closed

+
§

Stream(StreamEvent)

Stream events

+
§

DatagramReceived

One or more application datagrams have been received

+
§

DatagramsUnblocked

One or more application datagrams have been sent after blocking

+
§

Path(PathEvent)

(Multi)Path events

+
§

NatTraversal(Event)

Iroh’s nat traversal events

+

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl !RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl !UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.FinishError.html b/pr/234/docs/iroh_quinn_proto/enum.FinishError.html new file mode 100644 index 000000000..c822591f6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.FinishError.html @@ -0,0 +1,33 @@ +FinishError in iroh_quinn_proto - Rust

FinishError

Enum FinishError 

Source
pub enum FinishError {
+    Stopped(VarInt),
+    ClosedStream,
+}
Expand description

Reasons why attempting to finish a stream might fail

+

Variants§

§

Stopped(VarInt)

The peer is no longer accepting data on this stream. No +StreamEvent::Finished event will be emitted for this stream.

+

Carries an application-defined error code.

+
§

ClosedStream

The stream has not been opened or was already finished or reset

+

Trait Implementations§

Source§

impl Clone for FinishError

Source§

fn clone(&self) -> FinishError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FinishError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FinishError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for FinishError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for FinishError

Source§

fn eq(&self, other: &FinishError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for FinishError

Source§

impl StructuralPartialEq for FinishError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.LongType.html b/pr/234/docs/iroh_quinn_proto/enum.LongType.html new file mode 100644 index 000000000..c50b0d2e3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.LongType.html @@ -0,0 +1,30 @@ +LongType in iroh_quinn_proto - Rust

LongType

Enum LongType 

Source
pub enum LongType {
+    Handshake,
+    ZeroRtt,
+}
Expand description

Long packet types with uniform header structure

+

Variants§

§

Handshake

Handshake packet

+
§

ZeroRtt

0-RTT packet

+

Trait Implementations§

Source§

impl Clone for LongType

Source§

fn clone(&self) -> LongType

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for LongType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for LongType

Source§

fn eq(&self, other: &LongType) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for LongType

Source§

impl Eq for LongType

Source§

impl StructuralPartialEq for LongType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.PacketDecodeError.html b/pr/234/docs/iroh_quinn_proto/enum.PacketDecodeError.html new file mode 100644 index 000000000..584377d6e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.PacketDecodeError.html @@ -0,0 +1,48 @@ +PacketDecodeError in iroh_quinn_proto - Rust

PacketDecodeError

Enum PacketDecodeError 

Source
pub enum PacketDecodeError {
+    UnsupportedVersion {
+        src_cid: ConnectionId,
+        dst_cid: ConnectionId,
+        version: u32,
+    },
+    InvalidHeader(&'static str),
+}
Expand description

Packet decode error

+

Variants§

§

UnsupportedVersion

Packet uses a QUIC version that is not supported

+

Fields

§src_cid: ConnectionId

Source Connection ID

+
§dst_cid: ConnectionId

Destination Connection ID

+
§version: u32

The version that was unsupported

+
§

InvalidHeader(&'static str)

The packet header is invalid

+

Trait Implementations§

Source§

impl Clone for PacketDecodeError

Source§

fn clone(&self) -> PacketDecodeError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PacketDecodeError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PacketDecodeError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PacketDecodeError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<UnexpectedEnd> for PacketDecodeError

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl Hash for PacketDecodeError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for PacketDecodeError

Source§

fn cmp(&self, other: &PacketDecodeError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for PacketDecodeError

Source§

fn eq(&self, other: &PacketDecodeError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for PacketDecodeError

Source§

fn partial_cmp(&self, other: &PacketDecodeError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for PacketDecodeError

Source§

impl StructuralPartialEq for PacketDecodeError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.PathError.html b/pr/234/docs/iroh_quinn_proto/enum.PathError.html new file mode 100644 index 000000000..f216129e5 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.PathError.html @@ -0,0 +1,39 @@ +PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+    MultipathNotNegotiated,
+    ServerSideNotAllowed,
+    MaxPathIdReached,
+    RemoteCidsExhausted,
+    ValidationFailed,
+    InvalidRemoteAddress(SocketAddr),
+}
Expand description

Errors that might trigger a path being closed

+

Variants§

§

MultipathNotNegotiated

The extension was not negotiated with the peer

+
§

ServerSideNotAllowed

Paths can only be opened client-side

+
§

MaxPathIdReached

Current limits do not allow us to open more paths

+
§

RemoteCidsExhausted

No remote CIDs available to open a new path

+
§

ValidationFailed

Path could not be validated and will be abandoned

+
§

InvalidRemoteAddress(SocketAddr)

The remote address for the path is not supported by the endpoint

+

Trait Implementations§

Source§

impl Clone for PathError

Source§

fn clone(&self) -> PathError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for PathError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for PathError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for PathError

Source§

fn eq(&self, other: &PathError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathError

Source§

impl Eq for PathError

Source§

impl StructuralPartialEq for PathError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/234/docs/iroh_quinn_proto/enum.PathEvent.html new file mode 100644 index 000000000..7f80c27f1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.PathEvent.html @@ -0,0 +1,73 @@ +PathEvent in iroh_quinn_proto - Rust

PathEvent

Enum PathEvent 

Source
pub enum PathEvent {
+    Opened {
+        id: PathId,
+    },
+    Closed {
+        id: PathId,
+        error_code: VarInt,
+    },
+    Abandoned {
+        id: PathId,
+        path_stats: PathStats,
+    },
+    LocallyClosed {
+        id: PathId,
+        error: PathError,
+    },
+    RemoteStatus {
+        id: PathId,
+        status: PathStatus,
+    },
+    ObservedAddr {
+        id: PathId,
+        addr: SocketAddr,
+    },
+}
Expand description

Application events about paths

+

Variants§

§

Opened

A new path has been opened

+

Fields

§id: PathId

Which path is now open

+
§

Closed

A path has been closed

+

Fields

§id: PathId

Which path has been closed

+
§error_code: VarInt

Error code supplied by the peer +See https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-error-codes +for a list of known errors.

+
§

Abandoned

All remaining state for a path has been removed

+

The PathEvent::Closed would have been emitted for this path earlier.

+

Fields

§id: PathId

Which path had its state dropped

+
§path_stats: PathStats

The final path stats, they are no longer available via Connection::stats

+
§

LocallyClosed

Path was closed locally

+

Fields

§id: PathId

Path for which the error occurred

+
§error: PathError

The error that occurred

+
§

RemoteStatus

The remote changed the status of the path

+

The local status is not changed because of this event. It is up to the application +to update the local status, which is used for packet scheduling, when the remote +changes the status.

+

Fields

§id: PathId

Path which has changed status

+
§status: PathStatus

The new status set by the remote

+
§

ObservedAddr

Received an observation of our external address from the peer.

+

Fields

§id: PathId

Path over which the observed address was reported, PathId::ZERO when multipath is +not negotiated

+
§addr: SocketAddr

The address observed by the remote over this path

+

Trait Implementations§

Source§

impl Clone for PathEvent

Source§

fn clone(&self) -> PathEvent

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<PathEvent> for Event

Source§

fn from(source: PathEvent) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for PathEvent

Source§

fn eq(&self, other: &PathEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for PathEvent

Source§

impl StructuralPartialEq for PathEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.PathStatus.html b/pr/234/docs/iroh_quinn_proto/enum.PathStatus.html new file mode 100644 index 000000000..d9f1cdd17 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.PathStatus.html @@ -0,0 +1,36 @@ +PathStatus in iroh_quinn_proto - Rust

PathStatus

Enum PathStatus 

Source
pub enum PathStatus {
+    Available,
+    Backup,
+}
Expand description

The QUIC-MULTIPATH path status

+

See section “3.3 Path Status Management”: +https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management

+

Variants§

§

Available

Paths marked with as available will be used when scheduling packets

+

If multiple paths are available, packets will be scheduled on whichever has +capacity.

+
§

Backup

Paths marked as backup will only be used if there are no available paths

+

If the max_idle_timeout is specified the path will be kept alive so that it does not +expire.

+

Trait Implementations§

Source§

impl Clone for PathStatus

Source§

fn clone(&self) -> PathStatus

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStatus

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStatus

Source§

fn default() -> PathStatus

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStatus

Source§

fn eq(&self, other: &PathStatus) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStatus

Source§

impl Eq for PathStatus

Source§

impl StructuralPartialEq for PathStatus

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.ProtectedHeader.html b/pr/234/docs/iroh_quinn_proto/enum.ProtectedHeader.html new file mode 100644 index 000000000..80c3f47d8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ProtectedHeader.html @@ -0,0 +1,68 @@ +ProtectedHeader in iroh_quinn_proto - Rust

ProtectedHeader

Enum ProtectedHeader 

Source
pub enum ProtectedHeader {
+    Initial(ProtectedInitialHeader),
+    Long {
+        ty: LongType,
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+        len: u64,
+        version: u32,
+    },
+    Retry {
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+        version: u32,
+    },
+    Short {
+        spin: bool,
+        dst_cid: ConnectionId,
+    },
+    VersionNegotiate {
+        random: u8,
+        dst_cid: ConnectionId,
+        src_cid: ConnectionId,
+    },
+}
Expand description

Plain packet header

+

Variants§

§

Initial(ProtectedInitialHeader)

An Initial packet header

+
§

Long

A Long packet header, as used during the handshake

+

Fields

§ty: LongType

Type of the Long header packet

+
§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§len: u64

Length of the packet payload

+
§version: u32

QUIC version

+
§

Retry

A Retry packet header

+

Fields

§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§version: u32

QUIC version

+
§

Short

A short packet header, as used during the data phase

+

Fields

§spin: bool

Spin bit

+
§dst_cid: ConnectionId

Destination Connection ID

+
§

VersionNegotiate

A Version Negotiation packet header

+

Fields

§random: u8

Random value

+
§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+

Implementations§

Source§

impl ProtectedHeader

Source

pub fn dst_cid(&self) -> ConnectionId

The destination Connection ID of the packet

+
Source

pub fn decode( + buf: &mut Cursor<BytesMut>, + cid_parser: &(impl ConnectionIdParser + ?Sized), + supported_versions: &[u32], + grease_quic_bit: bool, +) -> Result<Self, PacketDecodeError>

Decode a plain header from given buffer, with given ConnectionIdParser.

+

Trait Implementations§

Source§

impl Clone for ProtectedHeader

Source§

fn clone(&self) -> ProtectedHeader

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ProtectedHeader

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.ReadError.html b/pr/234/docs/iroh_quinn_proto/enum.ReadError.html new file mode 100644 index 000000000..1280666df --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ReadError.html @@ -0,0 +1,44 @@ +ReadError in iroh_quinn_proto - Rust

ReadError

Enum ReadError 

Source
pub enum ReadError {
+    Blocked,
+    Reset(VarInt),
+}
Expand description

Errors triggered when reading from a recv stream

+

Variants§

§

Blocked

No more data is currently available on this stream.

+

If more data on this stream is received from the peer, an Event::StreamReadable will be +generated for this stream, indicating that retrying the read might succeed.

+
§

Reset(VarInt)

The peer abandoned transmitting data on this stream.

+

Carries an application-defined error code.

+

Trait Implementations§

Source§

impl Clone for ReadError

Source§

fn clone(&self) -> ReadError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for ReadError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ReadError

Source§

fn cmp(&self, other: &ReadError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ReadError

Source§

fn eq(&self, other: &ReadError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ReadError

Source§

fn partial_cmp(&self, other: &ReadError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for ReadError

Source§

impl StructuralPartialEq for ReadError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.ReadableError.html b/pr/234/docs/iroh_quinn_proto/enum.ReadableError.html new file mode 100644 index 000000000..767de7e77 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.ReadableError.html @@ -0,0 +1,43 @@ +ReadableError in iroh_quinn_proto - Rust

ReadableError

Enum ReadableError 

Source
pub enum ReadableError {
+    ClosedStream,
+    IllegalOrderedRead,
+}
Expand description

Errors triggered when opening a recv stream for reading

+

Variants§

§

ClosedStream

The stream has not been opened or was already stopped, finished, or reset

+
§

IllegalOrderedRead

Attempted an ordered read following an unordered read

+

Performing an unordered read allows discontinuities to arise in the receive buffer of a +stream which cannot be recovered, making further ordered reads impossible.

+

Trait Implementations§

Source§

impl Clone for ReadableError

Source§

fn clone(&self) -> ReadableError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ReadableError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ReadableError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ReadableError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for ReadableError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ReadableError

Source§

fn cmp(&self, other: &ReadableError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ReadableError

Source§

fn eq(&self, other: &ReadableError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ReadableError

Source§

fn partial_cmp(&self, other: &ReadableError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for ReadableError

Source§

impl StructuralPartialEq for ReadableError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.SendDatagramError.html b/pr/234/docs/iroh_quinn_proto/enum.SendDatagramError.html new file mode 100644 index 000000000..02eb87e5c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.SendDatagramError.html @@ -0,0 +1,47 @@ +SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+    UnsupportedByPeer,
+    Disabled,
+    TooLarge,
+    Blocked(Bytes),
+}
Expand description

Errors that can arise when sending a datagram

+

Variants§

§

UnsupportedByPeer

The peer does not support receiving datagram frames

+
§

Disabled

Datagram support is disabled locally

+
§

TooLarge

The datagram is larger than the connection can currently accommodate

+

Indicates that the path MTU minus overhead or the limit advertised by the peer has been +exceeded.

+
§

Blocked(Bytes)

Send would block

+

Trait Implementations§

Source§

impl Clone for SendDatagramError

Source§

fn clone(&self) -> SendDatagramError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SendDatagramError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SendDatagramError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SendDatagramError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for SendDatagramError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for SendDatagramError

Source§

fn cmp(&self, other: &SendDatagramError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for SendDatagramError

Source§

fn eq(&self, other: &SendDatagramError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for SendDatagramError

Source§

fn partial_cmp(&self, other: &SendDatagramError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for SendDatagramError

Source§

impl StructuralPartialEq for SendDatagramError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.SetPathStatusError.html b/pr/234/docs/iroh_quinn_proto/enum.SetPathStatusError.html new file mode 100644 index 000000000..04a970417 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.SetPathStatusError.html @@ -0,0 +1,31 @@ +SetPathStatusError in iroh_quinn_proto - Rust

SetPathStatusError

Enum SetPathStatusError 

Source
pub enum SetPathStatusError {
+    ClosedPath,
+    MultipathNotNegotiated,
+}
Expand description

Error from setting path status

+

Variants§

§

ClosedPath

Error indicating that a path has not been opened or has already been abandoned

+
§

MultipathNotNegotiated

Error indicating that this operation requires multipath to be negotiated whereas it hasn’t been

+

Trait Implementations§

Source§

impl Clone for SetPathStatusError

Source§

fn clone(&self) -> SetPathStatusError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SetPathStatusError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for SetPathStatusError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for SetPathStatusError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for SetPathStatusError

Source§

fn eq(&self, other: &SetPathStatusError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for SetPathStatusError

Source§

impl StructuralPartialEq for SetPathStatusError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/enum.Side.html b/pr/234/docs/iroh_quinn_proto/enum.Side.html new file mode 100644 index 000000000..6951c725c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.Side.html @@ -0,0 +1,47 @@ +Side in iroh_quinn_proto - Rust

Side

Enum Side 

Source
pub enum Side {
+    Client = 0,
+    Server = 1,
+}
Expand description

Whether an endpoint was the initiator of a connection

+

Variants§

§

Client = 0

The initiator of a connection

+
§

Server = 1

The acceptor of a connection

+

Implementations§

Source§

impl Side

Source

pub fn is_client(self) -> bool

Shorthand for self == Side::Client

+
Source

pub fn is_server(self) -> bool

Shorthand for self == Side::Server

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for Side

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for Side

Source§

fn clone(&self) -> Side

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Side

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Side> for Side

Source§

fn from(s: Side) -> Self

Converts to this type from the input type.
Source§

impl Hash for Side

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Not for Side

Source§

type Output = Side

The resulting type after applying the ! operator.
Source§

fn not(self) -> Self

Performs the unary ! operation. Read more
Source§

impl Ord for Side

Source§

fn cmp(&self, other: &Side) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Side

Source§

fn eq(&self, other: &Side) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for Side

Source§

fn partial_cmp(&self, other: &Side) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for Side

Source§

impl Eq for Side

Source§

impl StructuralPartialEq for Side

Auto Trait Implementations§

§

impl Freeze for Side

§

impl RefUnwindSafe for Side

§

impl Send for Side

§

impl Sync for Side

§

impl Unpin for Side

§

impl UnwindSafe for Side

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.StreamEvent.html b/pr/234/docs/iroh_quinn_proto/enum.StreamEvent.html new file mode 100644 index 000000000..6cdf9bbb4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.StreamEvent.html @@ -0,0 +1,57 @@ +StreamEvent in iroh_quinn_proto - Rust

StreamEvent

Enum StreamEvent 

Source
pub enum StreamEvent {
+    Opened {
+        dir: Dir,
+    },
+    Readable {
+        id: StreamId,
+    },
+    Writable {
+        id: StreamId,
+    },
+    Finished {
+        id: StreamId,
+    },
+    Stopped {
+        id: StreamId,
+        error_code: VarInt,
+    },
+    Available {
+        dir: Dir,
+    },
+}
Expand description

Application events about streams

+

Variants§

§

Opened

One or more new streams has been opened and might be readable

+

Fields

§dir: Dir

Directionality for which streams have been opened

+
§

Readable

A currently open stream likely has data or errors waiting to be read

+

Fields

§id: StreamId

Which stream is now readable

+
§

Writable

A formerly write-blocked stream might be ready for a write or have been stopped

+

Only generated for streams that are currently open.

+

Fields

§id: StreamId

Which stream is now writable

+
§

Finished

A finished stream has been fully acknowledged or stopped

+

Fields

§id: StreamId

Which stream has been finished

+
§

Stopped

The peer asked us to stop sending on an outgoing stream

+

Fields

§id: StreamId

Which stream has been stopped

+
§error_code: VarInt

Error code supplied by the peer

+
§

Available

At least one new stream of a certain directionality may be opened

+

Fields

§dir: Dir

Directionality for which streams are newly available

+

Trait Implementations§

Source§

impl Debug for StreamEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for StreamEvent

Source§

fn eq(&self, other: &StreamEvent) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for StreamEvent

Source§

impl StructuralPartialEq for StreamEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/enum.WriteError.html b/pr/234/docs/iroh_quinn_proto/enum.WriteError.html new file mode 100644 index 000000000..8a51bc744 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/enum.WriteError.html @@ -0,0 +1,47 @@ +WriteError in iroh_quinn_proto - Rust

WriteError

Enum WriteError 

Source
pub enum WriteError {
+    Blocked,
+    Stopped(VarInt),
+    ClosedStream,
+}
Expand description

Errors triggered while writing to a send stream

+

Variants§

§

Blocked

The peer is not able to accept additional data, or the connection is congested.

+

If the peer issues additional flow control credit, a StreamEvent::Writable event will +be generated, indicating that retrying the write might succeed.

+
§

Stopped(VarInt)

The peer is no longer accepting data on this stream, and it has been implicitly reset. The +stream cannot be finished or further written to.

+

Carries an application-defined error code.

+
§

ClosedStream

The stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for WriteError

Source§

fn clone(&self) -> WriteError

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WriteError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for WriteError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for WriteError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl Hash for WriteError

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for WriteError

Source§

fn cmp(&self, other: &WriteError) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for WriteError

Source§

fn eq(&self, other: &WriteError) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for WriteError

Source§

fn partial_cmp(&self, other: &WriteError) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Eq for WriteError

Source§

impl StructuralPartialEq for WriteError

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html b/pr/234/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html new file mode 100644 index 000000000..6785c96b6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/frame/struct.ApplicationClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ApplicationClose.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html b/pr/234/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html new file mode 100644 index 000000000..9839746e5 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/frame/struct.ConnectionClose.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionClose.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/frame/struct.Datagram.html b/pr/234/docs/iroh_quinn_proto/frame/struct.Datagram.html new file mode 100644 index 000000000..96b3bbb73 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/frame/struct.Datagram.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.Datagram.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/frame/struct.FrameType.html b/pr/234/docs/iroh_quinn_proto/frame/struct.FrameType.html new file mode 100644 index 000000000..1da1764a3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/frame/struct.FrameType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.FrameType.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/index.html b/pr/234/docs/iroh_quinn_proto/index.html new file mode 100644 index 000000000..23bdcc4e1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/index.html @@ -0,0 +1,14 @@ +iroh_quinn_proto - Rust

Crate iroh_quinn_proto

Crate iroh_quinn_proto 

Source
Expand description

Low-level protocol logic for the QUIC protoocol

+

quinn-proto contains a fully deterministic implementation of QUIC protocol logic. It contains +no networking code and does not get any relevant timestamps from the operating system. Most +users may want to use the futures-based quinn API instead.

+

The quinn-proto API might be of interest if you want to use it from a C or C++ project +through C bindings or if you want to use a different event loop than the one tokio provides.

+

The most important types are Endpoint, which conceptually represents the protocol state for +a single socket and mostly manages configuration and dispatches incoming datagrams to the +related Connection. Connection types contain the bulk of the protocol logic related to +managing a single connection and all the related state (such as streams).

+

Re-exports§

pub use rustls;

Modules§

coding
Coding related traits.
congestion
Logic for controlling the rate at which data is sent
crypto
Traits and implementations for the QUIC cryptography protocol
iroh_hp
iroh NAT Traversal
transport_parameters
QUIC connection transport parameters

Structs§

AcceptError
Error type for attempting to accept an Incoming
AckFrequencyConfig
Parameters for controlling the peer’s acknowledgement frequency
ApplicationClose
Reason given by an application for closing the connection
BloomTokenLog
Bloom filter-based TokenLog
Chunk
A chunk of data from the receive stream
Chunks
Chunks returned from RecvStream::read().
ClientConfig
Configuration for outgoing connections
ClosedPath
Error indicating that a path has not been opened or has already been abandoned
ClosedStream
Error indicating that a stream has not been opened or has already been finished or reset
Connection
Protocol state and logic for a single QUIC connection
ConnectionClose
Reason given by the transport for closing the connection
ConnectionEvent
Events sent from an Endpoint to a Connection
ConnectionHandle
Internal identifier for a Connection currently associated with an endpoint
ConnectionId
Protocol-level identifier for a connection.
ConnectionStats
Connection statistics
Datagram
An unreliable datagram
Datagrams
API to control datagram traffic
Endpoint
The main entry point to the library
EndpointConfig
Global configuration for the endpoint, affecting all connections
EndpointEvent
Events sent from a Connection to an Endpoint
FixedLengthConnectionIdParser
A ConnectionIdParser implementation that assumes the connection ID is of fixed length
FrameStats
Number of frames transmitted or received of each frame type
FrameType
A QUIC frame type
HashedConnectionIdGenerator
Generates 8-byte connection IDs that can be efficiently +validated
IdleTimeout
Maximum duration of inactivity to accept before timing out the connection
Incoming
An incoming connection for which the server has not yet begun its part of the handshake.
InvalidCid
The connection ID was not recognized by the ConnectionIdGenerator
MtuDiscoveryConfig
Parameters governing MTU discovery.
NoneTokenLog
Null implementation of TokenLog, which never accepts tokens
NoneTokenStore
Null implementation of TokenStore, which does not store any tokens
PartialDecode
Decodes a QUIC packet’s invariant header
PathId
Id representing different paths when using multipath extension
PathStats
Statistics related to a transmission path
ProtectedInitialHeader
Header of an Initial packet, before decryption
QlogConfig
Configuration for qlog trace logging.
QlogFileFactory
Enables writing qlog traces to a directory.
RandomConnectionIdGenerator
Generates purely random connection IDs of a specified length
RecvStream
Access to streams
RetryError
Error for attempting to retry an Incoming which already bears a token from a previous retry
RttEstimator
RTT estimation for a particular network path
SendStream
Access to streams
ServerConfig
Parameters governing incoming connections
ShouldTransmit
Indicates whether a frame needs to be transmitted
StdSystemTime
Default implementation of TimeSource
StreamId
Identifier for a stream within a particular connection
Streams
Access to streams
TokenMemoryCache
TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory
TokenReuseError
Error for when a validation token may have been reused
Transmit
An outgoing packet
TransportConfig
Parameters governing the core QUIC state machine
TransportError
Transport-level errors occur when a peer violates the protocol specification
TransportErrorCode
Transport-level error code
UdpStats
Statistics about UDP datagrams transmitted or received on a connection
ValidationTokenConfig
Configuration for sending and handling validation tokens in incoming connections
VarInt
An integer less than 2^62
VarIntBoundsExceeded
Error returned when constructing a VarInt from a value >= 2^62
Written
Indicates how many bytes and chunks had been transferred in a write operation

Enums§

ClosePathError
Errors triggered when abandoning a path
ConfigError
Errors in the configuration of an endpoint
ConnectError
Errors in the parameters being used to create a new connection
ConnectionError
Reasons why a connection might be lost
DatagramEvent
Event resulting from processing a single datagram
Dir
Whether a stream communicates data in both directions or only from the initiator
EcnCodepoint
Explicit congestion notification codepoint
Event
Events of interest to the application
FinishError
Reasons why attempting to finish a stream might fail
LongType
Long packet types with uniform header structure
PacketDecodeError
Packet decode error
PathError
Errors that might trigger a path being closed
PathEvent
Application events about paths
PathStatus
The QUIC-MULTIPATH path status
ProtectedHeader
Plain packet header
ReadError
Errors triggered when reading from a recv stream
ReadableError
Errors triggered when opening a recv stream for reading
SendDatagramError
Errors that can arise when sending a datagram
SetPathStatusError
Error from setting path status
Side
Whether an endpoint was the initiator of a connection
StreamEvent
Application events about streams
WriteError
Errors triggered while writing to a send stream

Constants§

DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED
When multipath is required and has not been explicitly enabled, this value will be used for
DEFAULT_SUPPORTED_VERSIONS
The QUIC protocol version implemented.

Traits§

ConnectionIdGenerator
Generates connection IDs for incoming connections
ConnectionIdParser
Parse connection id in short header packet
QlogFactory
Constructs a QlogConfig for individual connections.
TimeSource
Object to get current SystemTime
TokenLog
Responsible for limiting clients’ ability to reuse validation tokens
TokenStore
Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Error.html b/pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Error.html new file mode 100644 index 000000000..db215ec22 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Error.html @@ -0,0 +1,30 @@ +Error in iroh_quinn_proto::iroh_hp - Rust

Error

Enum Error 

Source
pub enum Error {
+    TooManyAddresses,
+    WrongConnectionSide,
+    ExtensionNotNegotiated,
+    NotEnoughAddresses,
+    Multipath(PathError),
+}
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

+

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> 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/234/docs/iroh_quinn_proto/iroh_hp/enum.Event.html b/pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Event.html new file mode 100644 index 000000000..8fec88452 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/iroh_hp/enum.Event.html @@ -0,0 +1,25 @@ +Event in iroh_quinn_proto::iroh_hp - Rust

Event

Enum Event 

Source
pub enum Event {
+    AddressAdded(SocketAddr),
+    AddressRemoved(SocketAddr),
+}
Expand description

Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.

+

Variants§

§

AddressAdded(SocketAddr)

An ADD_ADDRESS frame was received.

+
§

AddressRemoved(SocketAddr)

A REMOVE_ADDRESS frame was received.

+

Trait Implementations§

Source§

impl Clone for Event

Source§

fn clone(&self) -> Event

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Event

§

impl RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/iroh_hp/index.html b/pr/234/docs/iroh_quinn_proto/iroh_hp/index.html new file mode 100644 index 000000000..378677fc6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/iroh_hp/index.html @@ -0,0 +1,2 @@ +iroh_quinn_proto::iroh_hp - Rust

Module iroh_hp

Module iroh_hp 

Source
Expand description

iroh NAT Traversal

+

Enums§

Error
Errors that the nat traversal state might encounter.
Event
Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js new file mode 100644 index 000000000..e03b07ea1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/iroh_hp/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error","Event"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/enum.LongType.html b/pr/234/docs/iroh_quinn_proto/packet/enum.LongType.html new file mode 100644 index 000000000..dcf81f61f --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/enum.LongType.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.LongType.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html b/pr/234/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html new file mode 100644 index 000000000..494edb286 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/enum.PacketDecodeError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.PacketDecodeError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html b/pr/234/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html new file mode 100644 index 000000000..4e6ca3502 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/enum.ProtectedHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.ProtectedHeader.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html b/pr/234/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..63a343905 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/struct.PartialDecode.html b/pr/234/docs/iroh_quinn_proto/packet/struct.PartialDecode.html new file mode 100644 index 000000000..8ea383e01 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/struct.PartialDecode.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.PartialDecode.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html b/pr/234/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..e2c23e247 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/struct.ProtectedInitialHeader.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ProtectedInitialHeader.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html b/pr/234/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html new file mode 100644 index 000000000..2d526fcd6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/packet/trait.ConnectionIdParser.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.ConnectionIdParser.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html b/pr/234/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html new file mode 100644 index 000000000..31fd913dc --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/shared/enum.EcnCodepoint.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/enum.EcnCodepoint.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html b/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html new file mode 100644 index 000000000..18bfd544f --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionEvent.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionId.html b/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionId.html new file mode 100644 index 000000000..67aa4df61 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/shared/struct.ConnectionId.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.ConnectionId.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html b/pr/234/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html new file mode 100644 index 000000000..4b4f3e586 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/shared/struct.EndpointEvent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.EndpointEvent.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/sidebar-items.js new file mode 100644 index 000000000..a4656bb5e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED","DEFAULT_SUPPORTED_VERSIONS"],"enum":["ClosePathError","ConfigError","ConnectError","ConnectionError","DatagramEvent","Dir","EcnCodepoint","Event","FinishError","LongType","PacketDecodeError","PathError","PathEvent","PathStatus","ProtectedHeader","ReadError","ReadableError","SendDatagramError","SetPathStatusError","Side","StreamEvent","WriteError"],"mod":["coding","congestion","crypto","iroh_hp","transport_parameters"],"struct":["AcceptError","AckFrequencyConfig","ApplicationClose","BloomTokenLog","Chunk","Chunks","ClientConfig","ClosedPath","ClosedStream","Connection","ConnectionClose","ConnectionEvent","ConnectionHandle","ConnectionId","ConnectionStats","Datagram","Datagrams","Endpoint","EndpointConfig","EndpointEvent","FixedLengthConnectionIdParser","FrameStats","FrameType","HashedConnectionIdGenerator","IdleTimeout","Incoming","InvalidCid","MtuDiscoveryConfig","NoneTokenLog","NoneTokenStore","PartialDecode","PathId","PathStats","ProtectedInitialHeader","QlogConfig","QlogFileFactory","RandomConnectionIdGenerator","RecvStream","RetryError","RttEstimator","SendStream","ServerConfig","ShouldTransmit","StdSystemTime","StreamId","Streams","TokenMemoryCache","TokenReuseError","Transmit","TransportConfig","TransportError","TransportErrorCode","UdpStats","ValidationTokenConfig","VarInt","VarIntBoundsExceeded","Written"],"trait":["ConnectionIdGenerator","ConnectionIdParser","QlogFactory","TimeSource","TokenLog","TokenStore"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.AcceptError.html b/pr/234/docs/iroh_quinn_proto/struct.AcceptError.html new file mode 100644 index 000000000..43277ed74 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.AcceptError.html @@ -0,0 +1,23 @@ +AcceptError in iroh_quinn_proto - Rust

AcceptError

Struct AcceptError 

Source
pub struct AcceptError {
+    pub cause: ConnectionError,
+    pub response: Option<Transmit>,
+}
Expand description

Error type for attempting to accept an Incoming

+

Fields§

§cause: ConnectionError

Underlying error describing reason for failure

+
§response: Option<Transmit>

Optional response to transmit back

+

Trait Implementations§

Source§

impl Debug for AcceptError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html b/pr/234/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html new file mode 100644 index 000000000..aebf60f4b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.AckFrequencyConfig.html @@ -0,0 +1,52 @@ +AckFrequencyConfig in iroh_quinn_proto - Rust

AckFrequencyConfig

Struct AckFrequencyConfig 

Source
pub struct AckFrequencyConfig { /* private fields */ }
Expand description

Parameters for controlling the peer’s acknowledgement frequency

+

The parameters provided in this config will be sent to the peer at the beginning of the +connection, so it can take them into account when sending acknowledgements (see each parameter’s +description for details on how it influences acknowledgement frequency).

+

Quinn’s implementation follows the fourth draft of the +QUIC Acknowledgement Frequency extension. +The defaults produce behavior slightly different than the behavior without this extension, +because they change the way reordered packets are handled (see +AckFrequencyConfig::reordering_threshold for details).

+

Implementations§

Source§

impl AckFrequencyConfig

Source

pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self

The ack-eliciting threshold we will request the peer to use

+

This threshold represents the number of ack-eliciting packets an endpoint may receive +without immediately sending an ACK.

+

The remote peer should send at least one ACK frame when more than this number of +ack-eliciting packets have been received. A value of 0 results in a receiver immediately +acknowledging every ack-eliciting packet.

+

Defaults to 1, which sends ACK frames for every other ack-eliciting packet.

+
Source

pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self

The max_ack_delay we will request the peer to use

+

This parameter represents the maximum amount of time that an endpoint waits before sending +an ACK when the ack-eliciting threshold hasn’t been reached.

+

The effective max_ack_delay will be clamped to be at least the peer’s min_ack_delay +transport parameter, and at most the greater of the current path RTT or 25ms.

+

Defaults to None, in which case the peer’s original max_ack_delay will be used, as +obtained from its transport parameters.

+
Source

pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self

The reordering threshold we will request the peer to use

+

This threshold represents the amount of out-of-order packets that will trigger an endpoint +to send an ACK, without waiting for ack_eliciting_threshold to be exceeded or for +max_ack_delay to be elapsed.

+

A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1 +immediately acknowledges any packets that are received out of order (this is also the +behavior when the extension is disabled).

+

It is recommended to set this value to TransportConfig::packet_threshold minus one. +Since the default value for TransportConfig::packet_threshold is 3, this value defaults +to 2.

+

Trait Implementations§

Source§

impl Clone for AckFrequencyConfig

Source§

fn clone(&self) -> AckFrequencyConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AckFrequencyConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for AckFrequencyConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ApplicationClose.html b/pr/234/docs/iroh_quinn_proto/struct.ApplicationClose.html new file mode 100644 index 000000000..5187f053f --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ApplicationClose.html @@ -0,0 +1,31 @@ +ApplicationClose in iroh_quinn_proto - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+    pub error_code: VarInt,
+    pub reason: Bytes,
+}
Expand description

Reason given by an application for closing the connection

+

Fields§

§error_code: VarInt

Application-specific reason code

+
§reason: Bytes

Human-readable reason for the close

+

Trait Implementations§

Source§

impl Clone for ApplicationClose

Source§

fn clone(&self) -> ApplicationClose

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for ApplicationClose

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ApplicationClose

Source§

fn eq(&self, other: &ApplicationClose) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ApplicationClose

Source§

impl StructuralPartialEq for ApplicationClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.BloomTokenLog.html b/pr/234/docs/iroh_quinn_proto/struct.BloomTokenLog.html new file mode 100644 index 000000000..02f2030cb --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.BloomTokenLog.html @@ -0,0 +1,41 @@ +BloomTokenLog in iroh_quinn_proto - Rust

BloomTokenLog

Struct BloomTokenLog 

Source
pub struct BloomTokenLog(/* private fields */);
Expand description

Bloom filter-based TokenLog

+

Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing +used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter. +This achieves a memory profile of linear growth with an upper bound.

+

Divides time into periods based on lifetime and stores two filters at any given moment, for +each of the two periods currently non-expired tokens could expire in. As such, turns over +filters as time goes on to avoid bloom filter false positive rate increasing infinitely over +time.

+

Implementations§

Source§

impl BloomTokenLog

Source

pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> Self

Construct with an approximate maximum memory usage and expected number of validation token +usages per expiration period

+

Calculates the optimal bloom filter k number automatically.

+
Source

pub fn new(max_bytes: usize, k_num: u32) -> Self

Construct with an approximate maximum memory usage and a bloom filter k number

+

If choosing a custom k number, note that BloomTokenLog always maintains two filters +between them and divides the allocation budget of max_bytes evenly between them. As such, +each bloom filter will contain max_bytes * 4 bits.

+

Trait Implementations§

Source§

impl Default for BloomTokenLog

Default to 20 MiB max memory consumption and expected one million hits

+

With the default validation token lifetime of 2 weeks, this corresponds to one token usage per +1.21 seconds.

+
Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl TokenLog for BloomTokenLog

Source§

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Chunk.html b/pr/234/docs/iroh_quinn_proto/struct.Chunk.html new file mode 100644 index 000000000..7417842b4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Chunk.html @@ -0,0 +1,28 @@ +Chunk in iroh_quinn_proto - Rust

Chunk

Struct Chunk 

Source
pub struct Chunk {
+    pub offset: u64,
+    pub bytes: Bytes,
+}
Expand description

A chunk of data from the receive stream

+

Fields§

§offset: u64

The offset in the stream

+
§bytes: Bytes

The contents of the chunk

+

Trait Implementations§

Source§

impl Debug for Chunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Chunk

Source§

fn eq(&self, other: &Chunk) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for Chunk

Source§

impl StructuralPartialEq for Chunk

Auto Trait Implementations§

§

impl !Freeze for Chunk

§

impl RefUnwindSafe for Chunk

§

impl Send for Chunk

§

impl Sync for Chunk

§

impl Unpin for Chunk

§

impl UnwindSafe for Chunk

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Chunks.html b/pr/234/docs/iroh_quinn_proto/struct.Chunks.html new file mode 100644 index 000000000..b9cd33c0d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Chunks.html @@ -0,0 +1,34 @@ +Chunks in iroh_quinn_proto - Rust

Chunks

Struct Chunks 

Source
pub struct Chunks<'a> { /* private fields */ }
Expand description

Chunks returned from RecvStream::read().

+

§Note: Finalization Needed

+

Bytes read from the stream are not released from the congestion window until +either Self::finalize() is called, or this type is dropped.

+

It is recommended that you call Self::finalize() because it returns a flag +telling you whether reading from the stream has resulted in the need to transmit a packet.

+

If this type is leaked, the stream will remain blocked on the remote peer until +another read from the stream is done.

+

Implementations§

Source§

impl<'a> Chunks<'a>

Source

pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError>

Next

+

Should call finalize() when done calling this.

+
Source

pub fn finalize(self) -> ShouldTransmit

Mark the read data as consumed from the stream.

+

The number of read bytes will be released from the congestion window, +allowing the remote peer to send more data if it was previously blocked.

+

If ShouldTransmit::should_transmit() returns true, +a packet needs to be sent to the peer informing them that the stream is unblocked. +This means that you should call Connection::poll_transmit() +and send the returned packet as soon as is reasonable, to unblock the remote peer.

+

Trait Implementations§

Source§

impl Drop for Chunks<'_>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Chunks<'a>

§

impl<'a> RefUnwindSafe for Chunks<'a>

§

impl<'a> Send for Chunks<'a>

§

impl<'a> Sync for Chunks<'a>

§

impl<'a> Unpin for Chunks<'a>

§

impl<'a> !UnwindSafe for Chunks<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ClientConfig.html b/pr/234/docs/iroh_quinn_proto/struct.ClientConfig.html new file mode 100644 index 000000000..f979843c5 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ClientConfig.html @@ -0,0 +1,40 @@ +ClientConfig in iroh_quinn_proto - Rust

ClientConfig

Struct ClientConfig 

Source
#[non_exhaustive]
pub struct ClientConfig { /* private fields */ }
Expand description

Configuration for outgoing connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl ClientConfig

Source

pub fn new(crypto: Arc<dyn ClientConfig>) -> Self

Create a default config with a particular cryptographic config

+
Source

pub fn initial_dst_cid_provider( + &mut self, + initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>, +) -> &mut Self

Configure how to populate the destination CID of the initial packet when attempting to +establish a new connection

+

By default, it’s populated with random bytes with reasonable length, so unless you have +a good reason, you do not need to change it.

+

When prefer to override the default, please note that the generated connection ID MUST be +at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.

+
Source

pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self

Set a custom TransportConfig

+
Source

pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self

Set a custom TokenStore

+

Defaults to TokenMemoryCache, which is suitable for most internet applications.

+
Source

pub fn version(&mut self, version: u32) -> &mut Self

Set the QUIC version to use

+
Source§

impl ClientConfig

Source

pub fn with_platform_verifier() -> Self

👎Deprecated since 0.11.13: use try_with_platform_verifier() instead

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn try_with_platform_verifier() -> Result<Self, Error>

Create a client configuration that trusts the platform’s native roots

+
Source

pub fn with_root_certificates( + roots: Arc<RootCertStore>, +) -> Result<Self, VerifierBuilderError>

Create a client configuration that trusts specified trust anchors

+

Trait Implementations§

Source§

impl Clone for ClientConfig

Source§

fn clone(&self) -> ClientConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ClosedPath.html b/pr/234/docs/iroh_quinn_proto/struct.ClosedPath.html new file mode 100644 index 000000000..eac00c20d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ClosedPath.html @@ -0,0 +1,26 @@ +ClosedPath in iroh_quinn_proto - Rust

ClosedPath

Struct ClosedPath 

Source
pub struct ClosedPath { /* private fields */ }
Expand description

Error indicating that a path has not been opened or has already been abandoned

+

Trait Implementations§

Source§

impl Clone for ClosedPath

Source§

fn clone(&self) -> ClosedPath

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedPath

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedPath

Source§

fn default() -> ClosedPath

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedPath

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedPath

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl PartialEq for ClosedPath

Source§

fn eq(&self, other: &ClosedPath) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedPath

Source§

impl StructuralPartialEq for ClosedPath

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.ClosedStream.html b/pr/234/docs/iroh_quinn_proto/struct.ClosedStream.html new file mode 100644 index 000000000..2b581c329 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ClosedStream.html @@ -0,0 +1,26 @@ +ClosedStream in iroh_quinn_proto - Rust

ClosedStream

Struct ClosedStream 

Source
pub struct ClosedStream { /* private fields */ }
Expand description

Error indicating that a stream has not been opened or has already been finished or reset

+

Trait Implementations§

Source§

impl Clone for ClosedStream

Source§

fn clone(&self) -> ClosedStream

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClosedStream

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ClosedStream

Source§

fn default() -> ClosedStream

Returns the “default value” for a type. Read more
Source§

impl Display for ClosedStream

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for ClosedStream

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<ClosedStream> for Error

Source§

fn from(x: ClosedStream) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ClosedStream

Source§

fn eq(&self, other: &ClosedStream) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Eq for ClosedStream

Source§

impl StructuralPartialEq for ClosedStream

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.Connection.html b/pr/234/docs/iroh_quinn_proto/struct.Connection.html new file mode 100644 index 000000000..7ac24b8a6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Connection.html @@ -0,0 +1,244 @@ +Connection in iroh_quinn_proto - Rust

Connection

Struct Connection 

Source
pub struct Connection { /* private fields */ }
Expand description

Protocol state and logic for a single QUIC connection

+

Objects of this type receive ConnectionEvents and emit EndpointEvents and application +Events to make progress. To handle timeouts, a Connection returns timer updates and +expects timeouts through various methods. A number of simple getter methods are exposed +to allow callers to inspect some of the connection state.

+

Connection has roughly 4 types of methods:

+
    +
  • A. Simple getters, taking &self
  • +
  • B. Handlers for incoming events from the network or system, named handle_*.
  • +
  • C. State machine mutators, for incoming commands from the application. For convenience we +refer to this as “performing I/O” below, however as per the design of this library none of the +functions actually perform system-level I/O. For example, read and +write, but also things like reset.
  • +
  • D. Polling functions for outgoing events or actions for the caller to +take, named poll_*.
  • +
+

The simplest way to use this API correctly is to call (B) and (C) whenever +appropriate, then after each of those calls, as soon as feasible call all +polling methods (D) and deal with their outputs appropriately, e.g. by +passing it to the application or by making a system-level I/O call. You +should call the polling functions in this order:

+
    +
  1. poll_transmit
  2. +
  3. poll_timeout
  4. +
  5. poll_endpoint_events
  6. +
  7. poll
  8. +
+

Currently the only actual dependency is from (2) to (1), however additional +dependencies may be added in future, so the above order is recommended.

+

(A) may be called whenever desired.

+

Care should be made to ensure that the input events represent monotonically +increasing time. Specifically, calling handle_timeout +with events of the same Instant may be interleaved in any order with a +call to handle_event at that same instant; however +events or timeouts with different instants must not be interleaved.

+

Implementations§

Source§

impl Connection

Source

pub fn poll_timeout(&mut self) -> Option<Instant>

Returns the next time at which handle_timeout should be called

+

The value returned may change after:

+
    +
  • the application performed some I/O on the connection
  • +
  • a call was made to handle_event
  • +
  • a call to poll_transmit returned Some
  • +
  • a call was made to handle_timeout
  • +
+
Source

pub fn poll(&mut self) -> Option<Event>

Returns application-facing events

+

Connections should be polled for events after:

+
    +
  • a call was made to handle_event
  • +
  • a call was made to handle_timeout
  • +
+
Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

+
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

+
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

+
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

+
Source

pub fn open_path_ensure( + &mut self, + remote: SocketAddr, + initial_status: PathStatus, + now: Instant, +) -> Result<(PathId, bool), PathError>

Opens a new path only if no path to the remote address exists so far

+

See open_path. Returns (path_id, true) if the path already existed. (path_id, false) if was opened.

+
Source

pub fn open_path( + &mut self, + remote: SocketAddr, + initial_status: PathStatus, + now: Instant, +) -> Result<PathId, PathError>

Opens a new path

+

Further errors might occur and they will be emitted in PathEvent::LocallyClosed events. +When the path is opened it will be reported as an PathEvent::Opened.

+
Source

pub fn close_path( + &mut self, + now: Instant, + path_id: PathId, + error_code: VarInt, +) -> Result<(), ClosePathError>

Closes a path by sending a PATH_ABANDON frame

+

This will not allow closing the last path. It does allow closing paths which have +not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer +for a path that was never opened locally.

+
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+

There is no guarantee any of these paths are open or usable.

+
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn path_remote_address( + &self, + path_id: PathId, +) -> Result<SocketAddr, ClosedPath>

Returns the path’s remote socket address

+
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: usize, + 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 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 no local_ip was passed to +Endpoint::handle() for the datagrams establishing this +connection.

+
Source

pub fn rtt(&self, path_id: PathId) -> Option<Duration>

Current best estimate of this connection’s latency (round-trip-time)

+
Source

pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller>

Current state of this connection’s congestion controller, for debugging purposes

+
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

Modify the number of remotely initiated streams that may be concurrently open

+

No streams may be opened by the peer unless fewer than count are already open. Large +counts increase both minimum and worst-case memory consumption.

+
Source

pub fn set_max_concurrent_paths( + &mut self, + now: Instant, + count: NonZeroU32, +) -> Result<(), MultipathNotNegotiated>

Modify the number of open paths allowed when multipath is enabled

+

When reducing the number of concurrent paths this will only affect delaying sending +new MAX_PATH_ID frames until fewer than this number of paths are possible. To +actively reduce paths they must be closed using Connection::close_path, which +can also be used to close not-yet-opened paths.

+

If multipath is not negotiated (see the TransportConfig) this can not enable +multipath and will fail.

+
Source

pub fn max_concurrent_streams(&self, dir: Dir) -> u64

Current number of remotely initiated streams that may be concurrently open

+

If the target for this limit is reduced using set_max_concurrent_streams, +it will not change immediately, even if fewer streams are open. Instead, it will +decrement by one for each time a remotely initiated stream of matching directionality is closed.

+
Source

pub fn set_send_window(&mut self, send_window: u64)

Source

pub fn set_receive_window(&mut self, receive_window: VarInt)

Source

pub fn is_multipath_negotiated(&self) -> bool

Whether the Multipath for QUIC extension is enabled.

+

Multipath is only enabled after the handshake is completed and if it was enabled by both +peers.

+
Source

pub fn local_address_changed(&mut self)

Handle a change in the local address, i.e. an active migration

+
Source

pub fn current_mtu(&self) -> u16

Storage size required for the largest packet that can be transmitted on all currently +available paths

+

Buffers passed to Connection::poll_transmit should be at least this large.

+

When multipath is enabled, this value is the minimum MTU across all available paths.

+
Source

pub fn add_nat_traversal_address( + &mut self, + address: SocketAddr, +) -> Result<(), Error>

Add addresses the local endpoint considers are reachable for nat traversal

+
Source

pub fn remove_nat_traversal_address( + &mut self, + address: SocketAddr, +) -> Result<(), Error>

Removes an address the endpoing no longer considers reachable for nat traversal

+

Addresses not present in the set will be silently ignored.

+
Source

pub fn get_local_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the current local nat traversal addresses

+
Source

pub fn get_remote_nat_traversal_addresses( + &self, +) -> Result<Vec<SocketAddr>, Error>

Get the currently advertised nat traversal addresses by the server

+
Source

pub fn initiate_nat_traversal_round( + &mut self, + now: Instant, +) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

+

A nat traversal round involves advertising the client’s local addresses in REACH_OUT +frames, and initiating probing of the known remote addresses. When a new round is +initiated, the previous one is cancelled, and paths that have not been opened are closed.

+

Returns the server addresses that are now being probed.

+

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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ConnectionClose.html b/pr/234/docs/iroh_quinn_proto/struct.ConnectionClose.html new file mode 100644 index 000000000..68aed9f7b --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ConnectionClose.html @@ -0,0 +1,33 @@ +ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+    pub error_code: TransportErrorCode,
+    pub frame_type: Option<FrameType>,
+    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: Option<FrameType>

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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.ConnectionEvent.html b/pr/234/docs/iroh_quinn_proto/struct.ConnectionEvent.html new file mode 100644 index 000000000..7597724dd --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ConnectionEvent.html @@ -0,0 +1,18 @@ +ConnectionEvent in iroh_quinn_proto - Rust

ConnectionEvent

Struct ConnectionEvent 

Source
pub struct ConnectionEvent(/* private fields */);
Expand description

Events sent from an Endpoint to a Connection

+

Trait Implementations§

Source§

impl Debug for ConnectionEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ConnectionHandle.html b/pr/234/docs/iroh_quinn_proto/struct.ConnectionHandle.html new file mode 100644 index 000000000..617047484 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ConnectionHandle.html @@ -0,0 +1,35 @@ +ConnectionHandle in iroh_quinn_proto - Rust

ConnectionHandle

Struct ConnectionHandle 

Source
pub struct ConnectionHandle(pub usize);
Expand description

Internal identifier for a Connection currently associated with an endpoint

+

Tuple Fields§

§0: usize

Trait Implementations§

Source§

impl Clone for ConnectionHandle

Source§

fn clone(&self) -> ConnectionHandle

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionHandle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<ConnectionHandle> for usize

Source§

fn from(x: ConnectionHandle) -> Self

Converts to this type from the input type.
Source§

impl Hash for ConnectionHandle

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Index<ConnectionHandle> for Slab<ConnectionMeta>

Source§

type Output = ConnectionMeta

The returned type after indexing.
Source§

fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta

Performs the indexing (container[index]) operation. Read more
Source§

impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta>

Source§

fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta

Performs the mutable indexing (container[index]) operation. Read more
Source§

impl Ord for ConnectionHandle

Source§

fn cmp(&self, other: &ConnectionHandle) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionHandle

Source§

fn eq(&self, other: &ConnectionHandle) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionHandle

Source§

fn partial_cmp(&self, other: &ConnectionHandle) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionHandle

Source§

impl Eq for ConnectionHandle

Source§

impl StructuralPartialEq for ConnectionHandle

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ConnectionId.html b/pr/234/docs/iroh_quinn_proto/struct.ConnectionId.html new file mode 100644 index 000000000..9697f87e3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ConnectionId.html @@ -0,0 +1,2461 @@ +ConnectionId in iroh_quinn_proto - Rust

ConnectionId

Struct ConnectionId 

Source
pub struct ConnectionId { /* private fields */ }
Expand description

Protocol-level identifier for a connection.

+

Mainly useful for identifying this connection’s packets on the wire with tools like Wireshark.

+

Implementations§

Source§

impl ConnectionId

Source

pub fn new(bytes: &[u8]) -> Self

Construct cid from byte array

+
Source

pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self

Constructs cid by reading len bytes from a Buf

+

Callers need to assure that buf.remaining() >= len

+

Methods from Deref<Target = [u8]>§

1.23.0 · Source

pub fn is_ascii(&self) -> bool

Checks if all bytes in this slice are within the ASCII range.

+
Source

pub fn as_ascii(&self) -> Option<&[AsciiChar]>

🔬This is a nightly-only experimental API. (ascii_char)

If this slice is_ascii, returns it as a slice of +ASCII characters, otherwise returns None.

+
Source

pub unsafe fn as_ascii_unchecked(&self) -> &[AsciiChar]

🔬This is a nightly-only experimental API. (ascii_char)

Converts this slice of bytes into a slice of ASCII characters, +without checking whether they’re valid.

+
§Safety
+

Every byte in the slice must be in 0..=127, or else this is UB.

+
1.23.0 · Source

pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool

Checks that two slices are an ASCII case-insensitive match.

+

Same as to_ascii_lowercase(a) == to_ascii_lowercase(b), +but without allocating and copying temporaries.

+
1.23.0 · Source

pub fn make_ascii_uppercase(&mut self)

Converts this slice to its ASCII upper case equivalent in-place.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To return a new uppercased value without modifying the existing one, use +to_ascii_uppercase.

+
1.23.0 · Source

pub fn make_ascii_lowercase(&mut self)

Converts this slice to its ASCII lower case equivalent in-place.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To return a new lowercased value without modifying the existing one, use +to_ascii_lowercase.

+
1.60.0 · Source

pub fn escape_ascii(&self) -> EscapeAscii<'_>

Returns an iterator that produces an escaped version of this slice, +treating it as an ASCII string.

+
§Examples
+
let s = b"0\t\r\n'\"\\\x9d";
+let escaped = s.escape_ascii().to_string();
+assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
1.80.0 · Source

pub fn trim_ascii_start(&self) -> &[u8]

Returns a byte slice with leading ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n");
+assert_eq!(b"  ".trim_ascii_start(), b"");
+assert_eq!(b"".trim_ascii_start(), b"");
1.80.0 · Source

pub fn trim_ascii_end(&self) -> &[u8]

Returns a byte slice with trailing ASCII whitespace bytes removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world");
+assert_eq!(b"  ".trim_ascii_end(), b"");
+assert_eq!(b"".trim_ascii_end(), b"");
1.80.0 · Source

pub fn trim_ascii(&self) -> &[u8]

Returns a byte slice with leading and trailing ASCII whitespace bytes +removed.

+

‘Whitespace’ refers to the definition used by +u8::is_ascii_whitespace.

+
§Examples
+
assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world");
+assert_eq!(b"  ".trim_ascii(), b"");
+assert_eq!(b"".trim_ascii(), b"");
1.0.0 · Source

pub fn len(&self) -> usize

Returns the number of elements in the slice.

+
§Examples
+
let a = [1, 2, 3];
+assert_eq!(a.len(), 3);
1.0.0 · Source

pub fn is_empty(&self) -> bool

Returns true if the slice has a length of 0.

+
§Examples
+
let a = [1, 2, 3];
+assert!(!a.is_empty());
+
+let b: &[i32] = &[];
+assert!(b.is_empty());
1.0.0 · Source

pub fn first(&self) -> Option<&T>

Returns the first element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&10), v.first());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.first());
1.0.0 · Source

pub fn first_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the first element of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_mut() {
+    *first = 5;
+}
+assert_eq!(x, &[5, 1, 2]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.first_mut());
1.5.0 · Source

pub fn split_first(&self) -> Option<(&T, &[T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first() {
+    assert_eq!(first, &0);
+    assert_eq!(elements, &[1, 2]);
+}
1.5.0 · Source

pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the first and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_mut() {
+    *first = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
1.5.0 · Source

pub fn split_last(&self) -> Option<(&T, &[T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((last, elements)) = x.split_last() {
+    assert_eq!(last, &2);
+    assert_eq!(elements, &[0, 1]);
+}
1.5.0 · Source

pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])>

Returns the last and all the rest of the elements of the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((last, elements)) = x.split_last_mut() {
+    *last = 3;
+    elements[0] = 4;
+    elements[1] = 5;
+}
+assert_eq!(x, &[4, 5, 3]);
1.0.0 · Source

pub fn last(&self) -> Option<&T>

Returns the last element of the slice, or None if it is empty.

+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&30), v.last());
+
+let w: &[i32] = &[];
+assert_eq!(None, w.last());
1.0.0 · Source

pub fn last_mut(&mut self) -> Option<&mut T>

Returns a mutable reference to the last item in the slice, or None if it is empty.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_mut() {
+    *last = 10;
+}
+assert_eq!(x, &[0, 1, 10]);
+
+let y: &mut [i32] = &mut [];
+assert_eq!(None, y.last_mut());
1.77.0 · Source

pub fn first_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.first_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.first_chunk::<0>());
1.77.0 · Source

pub fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the first N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(first) = x.first_chunk_mut::<2>() {
+    first[0] = 5;
+    first[1] = 4;
+}
+assert_eq!(x, &[5, 4, 2]);
+
+assert_eq!(None, x.first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])>

Returns an array reference to the first N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk::<2>() {
+    assert_eq!(first, &[0, 1]);
+    assert_eq!(elements, &[2]);
+}
+
+assert_eq!(None, x.split_first_chunk::<4>());
1.77.0 · Source

pub fn split_first_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T; N], &mut [T])>

Returns a mutable array reference to the first N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
+    first[0] = 3;
+    first[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[3, 4, 5]);
+
+assert_eq!(None, x.split_first_chunk_mut::<4>());
1.77.0 · Source

pub fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])>

Returns an array reference to the last N items in the slice and the remaining slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &[0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk::<2>() {
+    assert_eq!(elements, &[0]);
+    assert_eq!(last, &[1, 2]);
+}
+
+assert_eq!(None, x.split_last_chunk::<4>());
1.77.0 · Source

pub fn split_last_chunk_mut<const N: usize>( + &mut self, +) -> Option<(&mut [T], &mut [T; N])>

Returns a mutable array reference to the last N items in the slice and the remaining +slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
+    last[0] = 3;
+    last[1] = 4;
+    elements[0] = 5;
+}
+assert_eq!(x, &[5, 3, 4]);
+
+assert_eq!(None, x.split_last_chunk_mut::<4>());
1.77.0 · Source

pub fn last_chunk<const N: usize>(&self) -> Option<&[T; N]>

Returns an array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let u = [10, 40, 30];
+assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
+
+let v: &[i32] = &[10];
+assert_eq!(None, v.last_chunk::<2>());
+
+let w: &[i32] = &[];
+assert_eq!(Some(&[]), w.last_chunk::<0>());
1.77.0 · Source

pub fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]>

Returns a mutable array reference to the last N items in the slice.

+

If the slice is not at least N in length, this will return None.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(last) = x.last_chunk_mut::<2>() {
+    last[0] = 10;
+    last[1] = 20;
+}
+assert_eq!(x, &[0, 10, 20]);
+
+assert_eq!(None, x.last_chunk_mut::<4>());
1.0.0 · Source

pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice depending on the type of +index.

+
    +
  • If given a position, returns a reference to the element at that +position or None if out of bounds.
  • +
  • If given a range, returns the subslice corresponding to that range, +or None if out of bounds.
  • +
+
§Examples
+
let v = [10, 40, 30];
+assert_eq!(Some(&40), v.get(1));
+assert_eq!(Some(&[10, 40][..]), v.get(0..2));
+assert_eq!(None, v.get(3));
+assert_eq!(None, v.get(0..4));
1.0.0 · Source

pub fn get_mut<I>( + &mut self, + index: I, +) -> Option<&mut <I as SliceIndex<[T]>>::Output>
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice depending on the +type of index (see get) or None if the index is out of bounds.

+
§Examples
+
let x = &mut [0, 1, 2];
+
+if let Some(elem) = x.get_mut(1) {
+    *elem = 42;
+}
+assert_eq!(x, &[0, 42, 2]);
1.0.0 · Source

pub unsafe fn get_unchecked<I>( + &self, + index: I, +) -> &<I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a reference to an element or subslice, without doing bounds +checking.

+

For a safe alternative see get.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get(index).unwrap_unchecked(). It’s UB +to call .get_unchecked(len), even if you immediately convert to a +pointer. And it’s UB to call .get_unchecked(..len + 1), +.get_unchecked(..=len), or similar.

+
§Examples
+
let x = &[1, 2, 4];
+
+unsafe {
+    assert_eq!(x.get_unchecked(1), &2);
+}
1.0.0 · Source

pub unsafe fn get_unchecked_mut<I>( + &mut self, + index: I, +) -> &mut <I as SliceIndex<[T]>>::Output
where + I: SliceIndex<[T]>,

Returns a mutable reference to an element or subslice, without doing +bounds checking.

+

For a safe alternative see get_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used.

+

You can think of this like .get_mut(index).unwrap_unchecked(). It’s +UB to call .get_unchecked_mut(len), even if you immediately convert +to a pointer. And it’s UB to call .get_unchecked_mut(..len + 1), +.get_unchecked_mut(..=len), or similar.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let elem = x.get_unchecked_mut(1);
+    *elem = 13;
+}
+assert_eq!(x, &[1, 13, 4]);
1.0.0 · Source

pub fn as_ptr(&self) -> *const T

Returns a raw pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

The caller must also ensure that the memory the pointer (non-transitively) points to +is never written to (except inside an UnsafeCell) using this pointer or any pointer +derived from it. If you need to mutate the contents of the slice, use as_mut_ptr.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &[1, 2, 4];
+let x_ptr = x.as_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        assert_eq!(x.get_unchecked(i), &*x_ptr.add(i));
+    }
+}
1.0.0 · Source

pub fn as_mut_ptr(&mut self) -> *mut T

Returns an unsafe mutable pointer to the slice’s buffer.

+

The caller must ensure that the slice outlives the pointer this +function returns, or else it will end up dangling.

+

Modifying the container referenced by this slice may cause its buffer +to be reallocated, which would also make any pointers to it invalid.

+
§Examples
+
let x = &mut [1, 2, 4];
+let x_ptr = x.as_mut_ptr();
+
+unsafe {
+    for i in 0..x.len() {
+        *x_ptr.add(i) += 2;
+    }
+}
+assert_eq!(x, &[3, 4, 6]);
1.48.0 · Source

pub fn as_ptr_range(&self) -> Range<*const T>

Returns the two raw pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_ptr for warnings on using these pointers. The end pointer +requires extra caution, as it does not point to a valid element in the +slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+

It can also be useful to check if a pointer to an element refers to an +element of this slice:

+ +
let a = [1, 2, 3];
+let x = &a[1] as *const _;
+let y = &5 as *const _;
+
+assert!(a.as_ptr_range().contains(&x));
+assert!(!a.as_ptr_range().contains(&y));
1.48.0 · Source

pub fn as_mut_ptr_range(&mut self) -> Range<*mut T>

Returns the two unsafe mutable pointers spanning the slice.

+

The returned range is half-open, which means that the end pointer +points one past the last element of the slice. This way, an empty +slice is represented by two equal pointers, and the difference between +the two pointers represents the size of the slice.

+

See as_mut_ptr for warnings on using these pointers. The end +pointer requires extra caution, as it does not point to a valid element +in the slice.

+

This function is useful for interacting with foreign interfaces which +use two pointers to refer to a range of elements in memory, as is +common in C++.

+
Source

pub fn as_array<const N: usize>(&self) -> Option<&[T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a reference to the underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
Source

pub fn as_mut_array<const N: usize>(&mut self) -> Option<&mut [T; N]>

🔬This is a nightly-only experimental API. (slice_as_array)

Gets a mutable reference to the slice’s underlying array.

+

If N is not exactly equal to the length of self, then this method returns None.

+
1.0.0 · Source

pub fn swap(&mut self, a: usize, b: usize)

Swaps two elements in the slice.

+

If a equals to b, it’s guaranteed that elements won’t change value.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Panics
+

Panics if a or b are out of bounds.

+
§Examples
+
let mut v = ["a", "b", "c", "d", "e"];
+v.swap(2, 4);
+assert!(v == ["a", "b", "e", "d", "c"]);
Source

pub unsafe fn swap_unchecked(&mut self, a: usize, b: usize)

🔬This is a nightly-only experimental API. (slice_swap_unchecked)

Swaps two elements in the slice, without doing bounds checking.

+

For a safe alternative see swap.

+
§Arguments
+
    +
  • a - The index of the first element
  • +
  • b - The index of the second element
  • +
+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior. +The caller has to ensure that a < self.len() and b < self.len().

+
§Examples
+
#![feature(slice_swap_unchecked)]
+
+let mut v = ["a", "b", "c", "d"];
+// SAFETY: we know that 1 and 3 are both indices of the slice
+unsafe { v.swap_unchecked(1, 3) };
+assert!(v == ["a", "d", "c", "b"]);
1.0.0 · Source

pub fn reverse(&mut self)

Reverses the order of elements in the slice, in place.

+
§Examples
+
let mut v = [1, 2, 3];
+v.reverse();
+assert!(v == [3, 2, 1]);
1.0.0 · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the slice.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &[1, 2, 4];
+let mut iterator = x.iter();
+
+assert_eq!(iterator.next(), Some(&1));
+assert_eq!(iterator.next(), Some(&2));
+assert_eq!(iterator.next(), Some(&4));
+assert_eq!(iterator.next(), None);
1.0.0 · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns an iterator that allows modifying each value.

+

The iterator yields all items from start to end.

+
§Examples
+
let x = &mut [1, 2, 4];
+for elem in x.iter_mut() {
+    *elem += 2;
+}
+assert_eq!(x, &[3, 4, 6]);
1.0.0 · Source

pub fn windows(&self, size: usize) -> Windows<'_, T>

Returns an iterator over all contiguous windows of length +size. The windows overlap. If the slice is shorter than +size, the iterator returns no values.

+
§Panics
+

Panics if size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.windows(3);
+assert_eq!(iter.next().unwrap(), &['l', 'o', 'r']);
+assert_eq!(iter.next().unwrap(), &['o', 'r', 'e']);
+assert_eq!(iter.next().unwrap(), &['r', 'e', 'm']);
+assert!(iter.next().is_none());
+

If the slice is shorter than size:

+ +
let slice = ['f', 'o', 'o'];
+let mut iter = slice.windows(4);
+assert!(iter.next().is_none());
+

Because the Iterator trait cannot represent the required lifetimes, +there is no windows_mut analog to windows; +[0,1,2].windows_mut(2).collect() would violate the rules of references +(though a LendingIterator analog is possible). You can sometimes use +Cell::as_slice_of_cells in +conjunction with windows instead:

+ +
use std::cell::Cell;
+
+let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+let slice = &mut array[..];
+let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+for w in slice_of_cells.windows(3) {
+    Cell::swap(&w[0], &w[2]);
+}
+assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
1.0.0 · Source

pub fn chunks(&self, chunk_size: usize) -> Chunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See chunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and rchunks for the same iterator but starting at the end of the +slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert_eq!(iter.next().unwrap(), &['m']);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See chunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and rchunks_mut for the same iterator but starting at +the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 3]);
1.31.0 · Source

pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks.

+

See chunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and rchunks_exact for the same iterator but starting at the end of the slice.

+

If your chunk_size is a constant, consider using as_chunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.chunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['l', 'o']);
+assert_eq!(iter.next().unwrap(), &['r', 'e']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['m']);
1.31.0 · Source

pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +beginning of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See chunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and rchunks_exact_mut for the same iterator but starting at the end of +the slice.

+

If your chunk_size is a constant, consider using as_chunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.chunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 0]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened.

+

As this is unsafe, consider whether you could use as_chunks or +as_rchunks instead, perhaps via something like +if let (chunks, []) = slice.as_chunks() or +let (chunks, []) = slice.as_chunks() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &[[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &[[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked() };
+assert_eq!(chunks, &[['l', 'o', 'r'], ['e', 'm', '!']]);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (chunks, remainder) = slice.as_chunks();
+assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
+assert_eq!(remainder, &['m']);
+

If you expect the slice to be an exact multiple, you can combine +let-else with an empty slice pattern:

+ +
let slice = ['R', 'u', 's', 't'];
+let (chunks, []) = slice.as_chunks::<2>() else {
+    panic!("slice didn't have even length")
+};
+assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
1.88.0 · Source

pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let (remainder, chunks) = slice.as_rchunks();
+assert_eq!(remainder, &['l']);
+assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]);
1.88.0 · Source

pub unsafe fn as_chunks_unchecked_mut<const N: usize>( + &mut self, +) -> &mut [[T; N]]

Splits the slice into a slice of N-element arrays, +assuming that there’s no remainder.

+

This is the inverse operation to as_flattened_mut.

+

As this is unsafe, consider whether you could use as_chunks_mut or +as_rchunks_mut instead, perhaps via something like +if let (chunks, []) = slice.as_chunks_mut() or +let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };.

+
§Safety
+

This may only be called when

+
    +
  • The slice splits exactly into N-element chunks (aka self.len() % N == 0).
  • +
  • N != 0.
  • +
+
§Examples
+
let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!'];
+let chunks: &mut [[char; 1]] =
+    // SAFETY: 1-element chunks never have remainder
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[0] = ['L'];
+assert_eq!(chunks, &[['L'], ['o'], ['r'], ['e'], ['m'], ['!']]);
+let chunks: &mut [[char; 3]] =
+    // SAFETY: The slice length (6) is a multiple of 3
+    unsafe { slice.as_chunks_unchecked_mut() };
+chunks[1] = ['a', 'x', '?'];
+assert_eq!(slice, &['L', 'o', 'r', 'a', 'x', '?']);
+
+// These would be unsound:
+// let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5
+// let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed
1.88.0 · Source

pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T])

Splits the slice into a slice of N-element arrays, +starting at the beginning of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (chunks, remainder) = slice.as_chunks_mut(), then:

+
    +
  • chunks.len() equals slice.len() / N,
  • +
  • remainder.len() equals slice.len() % N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (chunks, remainder) = v.as_chunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[1, 1, 2, 2, 9]);
1.88.0 · Source

pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]])

Splits the slice into a slice of N-element arrays, +starting at the end of the slice, +and a remainder slice with length strictly less than N.

+

The remainder is meaningful in the division sense. Given +let (remainder, chunks) = slice.as_rchunks_mut(), then:

+
    +
  • remainder.len() equals slice.len() % N,
  • +
  • chunks.len() equals slice.len() / N, and
  • +
  • slice.len() equals chunks.len() * N + remainder.len().
  • +
+

You can flatten the chunks back into a slice-of-T with as_flattened_mut.

+
§Panics
+

Panics if N is zero.

+

Note that this check is against a const generic parameter, not a runtime +value, and thus a particular monomorphization will either always panic +or it will never panic.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+let (remainder, chunks) = v.as_rchunks_mut();
+remainder[0] = 9;
+for chunk in chunks {
+    *chunk = [count; 2];
+    count += 1;
+}
+assert_eq!(v, &[9, 1, 1, 2, 2]);
Source

pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N>

🔬This is a nightly-only experimental API. (array_windows)

Returns an iterator over overlapping windows of N elements of a slice, +starting at the beginning of the slice.

+

This is the const generic equivalent of windows.

+

If N is greater than the size of the slice, it will return no windows.

+
§Panics
+

Panics if N is zero. This check will most probably get changed to a compile time +error before this method gets stabilized.

+
§Examples
+
#![feature(array_windows)]
+let slice = [0, 1, 2, 3];
+let mut iter = slice.array_windows();
+assert_eq!(iter.next().unwrap(), &[0, 1]);
+assert_eq!(iter.next().unwrap(), &[1, 2]);
+assert_eq!(iter.next().unwrap(), &[2, 3]);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks(&self, chunk_size: usize) -> RChunks<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact for a variant of this iterator that returns chunks of always exactly +chunk_size elements, and chunks for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert_eq!(iter.next().unwrap(), &['l']);
+assert!(iter.next().is_none());
1.31.0 · Source

pub fn rchunks_mut(&mut self, chunk_size: usize) -> RChunksMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last chunk will not have length chunk_size.

+

See rchunks_exact_mut for a variant of this iterator that returns chunks of always +exactly chunk_size elements, and chunks_mut for the same iterator but starting at the +beginning of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[3, 2, 2, 1, 1]);
1.31.0 · Source

pub fn rchunks_exact(&self, chunk_size: usize) -> RChunksExact<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the +end of the slice.

+

The chunks are slices and do not overlap. If chunk_size does not divide the length of the +slice, then the last up to chunk_size-1 elements will be omitted and can be retrieved +from the remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of rchunks.

+

See rchunks for a variant of this iterator that also returns the remainder as a smaller +chunk, and chunks_exact for the same iterator but starting at the beginning of the +slice.

+

If your chunk_size is a constant, consider using as_rchunks instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let slice = ['l', 'o', 'r', 'e', 'm'];
+let mut iter = slice.rchunks_exact(2);
+assert_eq!(iter.next().unwrap(), &['e', 'm']);
+assert_eq!(iter.next().unwrap(), &['o', 'r']);
+assert!(iter.next().is_none());
+assert_eq!(iter.remainder(), &['l']);
1.31.0 · Source

pub fn rchunks_exact_mut(&mut self, chunk_size: usize) -> RChunksExactMut<'_, T>

Returns an iterator over chunk_size elements of the slice at a time, starting at the end +of the slice.

+

The chunks are mutable slices, and do not overlap. If chunk_size does not divide the +length of the slice, then the last up to chunk_size-1 elements will be omitted and can be +retrieved from the into_remainder function of the iterator.

+

Due to each chunk having exactly chunk_size elements, the compiler can often optimize the +resulting code better than in the case of chunks_mut.

+

See rchunks_mut for a variant of this iterator that also returns the remainder as a +smaller chunk, and chunks_exact_mut for the same iterator but starting at the beginning +of the slice.

+

If your chunk_size is a constant, consider using as_rchunks_mut instead, which will +give references to arrays of exactly that length, rather than slices.

+
§Panics
+

Panics if chunk_size is zero.

+
§Examples
+
let v = &mut [0, 0, 0, 0, 0];
+let mut count = 1;
+
+for chunk in v.rchunks_exact_mut(2) {
+    for elem in chunk.iter_mut() {
+        *elem += count;
+    }
+    count += 1;
+}
+assert_eq!(v, &[0, 2, 2, 1, 1]);
1.77.0 · Source

pub fn chunk_by<F>(&self, pred: F) -> ChunkBy<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping runs +of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&[3, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &[1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&[1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3][..]));
+assert_eq!(iter.next(), Some(&[2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.77.0 · Source

pub fn chunk_by_mut<F>(&mut self, pred: F) -> ChunkByMut<'_, T, F>
where + F: FnMut(&T, &T) -> bool,

Returns an iterator over the slice producing non-overlapping mutable +runs of elements using the predicate to separate them.

+

The predicate is called for every pair of consecutive elements, +meaning that it is called on slice[0] and slice[1], +followed by slice[1] and slice[2], and so on.

+
§Examples
+
let slice = &mut [1, 1, 1, 3, 3, 2, 2, 2];
+
+let mut iter = slice.chunk_by_mut(|a, b| a == b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
+assert_eq!(iter.next(), Some(&mut [3, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
+assert_eq!(iter.next(), None);
+

This method can be used to extract the sorted subslices:

+ +
let slice = &mut [1, 1, 2, 3, 2, 3, 2, 3, 4];
+
+let mut iter = slice.chunk_by_mut(|a, b| a <= b);
+
+assert_eq!(iter.next(), Some(&mut [1, 1, 2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3][..]));
+assert_eq!(iter.next(), Some(&mut [2, 3, 4][..]));
+assert_eq!(iter.next(), None);
1.0.0 · Source

pub fn split_at(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_checked.

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+{
+   let (left, right) = v.split_at(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+{
+    let (left, right) = v.split_at(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+{
+    let (left, right) = v.split_at(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.0.0 · Source

pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+
§Panics
+

Panics if mid > len. For a non-panicking alternative see +split_at_mut_checked.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+let (left, right) = v.split_at_mut(2);
+assert_eq!(left, [1, 0]);
+assert_eq!(right, [3, 0, 5, 6]);
+left[1] = 2;
+right[1] = 4;
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.79.0 · Source

pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T])

Divides one slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let v = ['a', 'b', 'c'];
+
+unsafe {
+   let (left, right) = v.split_at_unchecked(0);
+   assert_eq!(left, []);
+   assert_eq!(right, ['a', 'b', 'c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(2);
+    assert_eq!(left, ['a', 'b']);
+    assert_eq!(right, ['c']);
+}
+
+unsafe {
+    let (left, right) = v.split_at_unchecked(3);
+    assert_eq!(left, ['a', 'b', 'c']);
+    assert_eq!(right, []);
+}
1.79.0 · Source

pub unsafe fn split_at_mut_unchecked( + &mut self, + mid: usize, +) -> (&mut [T], &mut [T])

Divides one mutable slice into two at an index, without doing bounds checking.

+

The first will contain all indices from [0, mid) (excluding +the index mid itself) and the second will contain all +indices from [mid, len) (excluding the index len itself).

+

For a safe alternative see split_at_mut.

+
§Safety
+

Calling this method with an out-of-bounds index is undefined behavior +even if the resulting reference is not used. The caller has to ensure that +0 <= mid <= self.len().

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+// scoped to restrict the lifetime of the borrows
+unsafe {
+    let (left, right) = v.split_at_mut_unchecked(2);
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
1.80.0 · Source

pub fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])>

Divides one slice into two at an index, returning None if the slice is +too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let v = [1, -2, 3, -4, 5, -6];
+
+{
+   let (left, right) = v.split_at_checked(0).unwrap();
+   assert_eq!(left, []);
+   assert_eq!(right, [1, -2, 3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(2).unwrap();
+    assert_eq!(left, [1, -2]);
+    assert_eq!(right, [3, -4, 5, -6]);
+}
+
+{
+    let (left, right) = v.split_at_checked(6).unwrap();
+    assert_eq!(left, [1, -2, 3, -4, 5, -6]);
+    assert_eq!(right, []);
+}
+
+assert_eq!(None, v.split_at_checked(7));
1.80.0 · Source

pub fn split_at_mut_checked( + &mut self, + mid: usize, +) -> Option<(&mut [T], &mut [T])>

Divides one mutable slice into two at an index, returning None if the +slice is too short.

+

If mid ≤ len returns a pair of slices where the first will contain all +indices from [0, mid) (excluding the index mid itself) and the +second will contain all indices from [mid, len) (excluding the index +len itself).

+

Otherwise, if mid > len, returns None.

+
§Examples
+
let mut v = [1, 0, 3, 0, 5, 6];
+
+if let Some((left, right)) = v.split_at_mut_checked(2) {
+    assert_eq!(left, [1, 0]);
+    assert_eq!(right, [3, 0, 5, 6]);
+    left[1] = 2;
+    right[1] = 4;
+}
+assert_eq!(v, [1, 2, 3, 4, 5, 6]);
+
+assert_eq!(None, v.split_at_mut_checked(7));
1.0.0 · Source

pub fn split<F>(&self, pred: F) -> Split<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is not contained in the subslices.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the first element is matched, an empty slice will be the first item +returned by the iterator. Similarly, if the last element in the slice +is matched, an empty slice will be the last item returned by the +iterator:

+ +
let slice = [10, 40, 33];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert!(iter.next().is_none());
+

If two matched elements are directly adjacent, an empty slice will be +present between them:

+ +
let slice = [10, 6, 33, 20];
+let mut iter = slice.split(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10]);
+assert_eq!(iter.next().unwrap(), &[]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
1.0.0 · Source

pub fn split_mut<F>(&mut self, pred: F) -> SplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_mut(|num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 1]);
1.51.0 · Source

pub fn split_inclusive<F>(&self, pred: F) -> SplitInclusive<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred. The matched element is contained in the end of the previous +subslice as a terminator.

+
§Examples
+
let slice = [10, 40, 33, 20];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert_eq!(iter.next().unwrap(), &[20]);
+assert!(iter.next().is_none());
+

If the last element of the slice is matched, +that element will be considered the terminator of the preceding slice. +That slice will be the last item returned by the iterator.

+ +
let slice = [3, 10, 40, 33];
+let mut iter = slice.split_inclusive(|num| num % 3 == 0);
+
+assert_eq!(iter.next().unwrap(), &[3]);
+assert_eq!(iter.next().unwrap(), &[10, 40, 33]);
+assert!(iter.next().is_none());
1.51.0 · Source

pub fn split_inclusive_mut<F>(&mut self, pred: F) -> SplitInclusiveMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred. The matched element is contained in the previous +subslice as a terminator.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.split_inclusive_mut(|num| *num % 3 == 0) {
+    let terminator_idx = group.len()-1;
+    group[terminator_idx] = 1;
+}
+assert_eq!(v, [10, 40, 1, 20, 1, 1]);
1.27.0 · Source

pub fn rsplit<F>(&self, pred: F) -> RSplit<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, starting at the end of the slice and working backwards. +The matched element is not contained in the subslices.

+
§Examples
+
let slice = [11, 22, 33, 0, 44, 55];
+let mut iter = slice.rsplit(|num| *num == 0);
+
+assert_eq!(iter.next().unwrap(), &[44, 55]);
+assert_eq!(iter.next().unwrap(), &[11, 22, 33]);
+assert_eq!(iter.next(), None);
+

As with split(), if the first or last element is matched, an empty +slice will be the first (or last) item returned by the iterator.

+ +
let v = &[0, 1, 1, 2, 3, 5, 8];
+let mut it = v.rsplit(|n| *n % 2 == 0);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next().unwrap(), &[3, 5]);
+assert_eq!(it.next().unwrap(), &[1, 1]);
+assert_eq!(it.next().unwrap(), &[]);
+assert_eq!(it.next(), None);
1.27.0 · Source

pub fn rsplit_mut<F>(&mut self, pred: F) -> RSplitMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that +match pred, starting at the end of the slice and working +backwards. The matched element is not contained in the subslices.

+
§Examples
+
let mut v = [100, 400, 300, 200, 600, 500];
+
+let mut count = 0;
+for group in v.rsplit_mut(|num| *num % 3 == 0) {
+    count += 1;
+    group[0] = count;
+}
+assert_eq!(v, [3, 400, 300, 2, 600, 1]);
1.0.0 · Source

pub fn splitn<F>(&self, n: usize, pred: F) -> SplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once by numbers divisible by 3 (i.e., [10, 40], +[20, 60, 50]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn splitn_mut<F>(&mut self, n: usize, pred: F) -> SplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over mutable subslices separated by elements that match +pred, limited to returning at most n items. The matched element is +not contained in the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut v = [10, 40, 30, 20, 60, 50];
+
+for group in v.splitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(v, [1, 40, 30, 1, 60, 50]);
1.0.0 · Source

pub fn rsplitn<F>(&self, n: usize, pred: F) -> RSplitN<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+

Print the slice split once, starting from the end, by numbers divisible +by 3 (i.e., [50], [10, 40, 30, 20]):

+ +
let v = [10, 40, 30, 20, 60, 50];
+
+for group in v.rsplitn(2, |num| *num % 3 == 0) {
+    println!("{group:?}");
+}
1.0.0 · Source

pub fn rsplitn_mut<F>(&mut self, n: usize, pred: F) -> RSplitNMut<'_, T, F>
where + F: FnMut(&T) -> bool,

Returns an iterator over subslices separated by elements that match +pred limited to returning at most n items. This starts at the end of +the slice and works backwards. The matched element is not contained in +the subslices.

+

The last element returned, if any, will contain the remainder of the +slice.

+
§Examples
+
let mut s = [10, 40, 30, 20, 60, 50];
+
+for group in s.rsplitn_mut(2, |num| *num % 3 == 0) {
+    group[0] = 1;
+}
+assert_eq!(s, [1, 40, 30, 20, 60, 1]);
Source

pub fn split_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the first element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.split_once(|&x| x == 2), Some((
+    &[1][..],
+    &[3, 2, 4][..]
+)));
+assert_eq!(s.split_once(|&x| x == 0), None);
Source

pub fn rsplit_once<F>(&self, pred: F) -> Option<(&[T], &[T])>
where + F: FnMut(&T) -> bool,

🔬This is a nightly-only experimental API. (slice_split_once)

Splits the slice on the last element that matches the specified +predicate.

+

If any matching elements are present in the slice, returns the prefix +before the match and suffix after. The matching element itself is not +included. If no elements match, returns None.

+
§Examples
+
#![feature(slice_split_once)]
+let s = [1, 2, 3, 2, 4];
+assert_eq!(s.rsplit_once(|&x| x == 2), Some((
+    &[1, 2, 3][..],
+    &[4][..]
+)));
+assert_eq!(s.rsplit_once(|&x| x == 0), None);
1.0.0 · Source

pub fn contains(&self, x: &T) -> bool
where + T: PartialEq,

Returns true if the slice contains an element with the given value.

+

This operation is O(n).

+

Note that if you have a sorted slice, binary_search may be faster.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.contains(&30));
+assert!(!v.contains(&50));
+

If you do not have a &T, but some other value that you can compare +with one (for example, String implements PartialEq<str>), you can +use iter().any:

+ +
let v = [String::from("hello"), String::from("world")]; // slice of `String`
+assert!(v.iter().any(|e| e == "hello")); // search with `&str`
+assert!(!v.iter().any(|e| e == "hi"));
1.0.0 · Source

pub fn starts_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a prefix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.starts_with(&[10]));
+assert!(v.starts_with(&[10, 40]));
+assert!(v.starts_with(&v));
+assert!(!v.starts_with(&[50]));
+assert!(!v.starts_with(&[10, 50]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.starts_with(&[]));
+let v: &[u8] = &[];
+assert!(v.starts_with(&[]));
1.0.0 · Source

pub fn ends_with(&self, needle: &[T]) -> bool
where + T: PartialEq,

Returns true if needle is a suffix of the slice or equal to the slice.

+
§Examples
+
let v = [10, 40, 30];
+assert!(v.ends_with(&[30]));
+assert!(v.ends_with(&[40, 30]));
+assert!(v.ends_with(&v));
+assert!(!v.ends_with(&[50]));
+assert!(!v.ends_with(&[50, 30]));
+

Always returns true if needle is an empty slice:

+ +
let v = &[10, 40, 30];
+assert!(v.ends_with(&[]));
+let v: &[u8] = &[];
+assert!(v.ends_with(&[]));
1.51.0 · Source

pub fn strip_prefix<P>(&self, prefix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix, wrapped in Some. +If prefix is empty, simply returns the original slice. If prefix is equal to the +original slice, returns an empty slice.

+

If the slice does not start with prefix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_prefix(&[10]), Some(&[40, 30][..]));
+assert_eq!(v.strip_prefix(&[10, 40]), Some(&[30][..]));
+assert_eq!(v.strip_prefix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_prefix(&[50]), None);
+assert_eq!(v.strip_prefix(&[10, 50]), None);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".strip_prefix(prefix.as_bytes()),
+           Some(b"llo".as_ref()));
1.51.0 · Source

pub fn strip_suffix<P>(&self, suffix: &P) -> Option<&[T]>
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

Returns a subslice with the suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix, wrapped in Some. +If suffix is empty, simply returns the original slice. If suffix is equal to the +original slice, returns an empty slice.

+

If the slice does not end with suffix, returns None.

+
§Examples
+
let v = &[10, 40, 30];
+assert_eq!(v.strip_suffix(&[30]), Some(&[10, 40][..]));
+assert_eq!(v.strip_suffix(&[40, 30]), Some(&[10][..]));
+assert_eq!(v.strip_suffix(&[10, 40, 30]), Some(&[][..]));
+assert_eq!(v.strip_suffix(&[50]), None);
+assert_eq!(v.strip_suffix(&[50, 30]), None);
Source

pub fn trim_prefix<P>(&self, prefix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional prefix removed.

+

If the slice starts with prefix, returns the subslice after the prefix. If prefix +is empty or the slice does not start with prefix, simply returns the original slice. +If prefix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Prefix present - removes it
+assert_eq!(v.trim_prefix(&[10]), &[40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 40]), &[30][..]);
+assert_eq!(v.trim_prefix(&[10, 40, 30]), &[][..]);
+
+// Prefix absent - returns original slice
+assert_eq!(v.trim_prefix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_prefix(&[10, 50]), &[10, 40, 30][..]);
+
+let prefix : &str = "he";
+assert_eq!(b"hello".trim_prefix(prefix.as_bytes()), b"llo".as_ref());
Source

pub fn trim_suffix<P>(&self, suffix: &P) -> &[T]
where + P: SlicePattern<Item = T> + ?Sized, + T: PartialEq,

🔬This is a nightly-only experimental API. (trim_prefix_suffix)

Returns a subslice with the optional suffix removed.

+

If the slice ends with suffix, returns the subslice before the suffix. If suffix +is empty or the slice does not end with suffix, simply returns the original slice. +If suffix is equal to the original slice, returns an empty slice.

+
§Examples
+
#![feature(trim_prefix_suffix)]
+
+let v = &[10, 40, 30];
+
+// Suffix present - removes it
+assert_eq!(v.trim_suffix(&[30]), &[10, 40][..]);
+assert_eq!(v.trim_suffix(&[40, 30]), &[10][..]);
+assert_eq!(v.trim_suffix(&[10, 40, 30]), &[][..]);
+
+// Suffix absent - returns original slice
+assert_eq!(v.trim_suffix(&[50]), &[10, 40, 30][..]);
+assert_eq!(v.trim_suffix(&[50, 30]), &[10, 40, 30][..]);

Binary searches this slice for a given element. +If the slice is not sorted, the returned result is unspecified and +meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search_by, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+assert_eq!(s.binary_search(&13),  Ok(9));
+assert_eq!(s.binary_search(&4),   Err(7));
+assert_eq!(s.binary_search(&100), Err(13));
+let r = s.binary_search(&1);
+assert!(match r { Ok(1..=4) => true, _ => false, });
+

If you want to find that whole range of matching items, rather than +an arbitrary matching one, that can be done using partition_point:

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let low = s.partition_point(|x| x < &1);
+assert_eq!(low, 1);
+let high = s.partition_point(|x| x <= &1);
+assert_eq!(high, 5);
+let r = s.binary_search(&1);
+assert!((low..high).contains(&r.unwrap()));
+
+assert!(s[..low].iter().all(|&x| x < 1));
+assert!(s[low..high].iter().all(|&x| x == 1));
+assert!(s[high..].iter().all(|&x| x > 1));
+
+// For something not found, the "range" of equal items is empty
+assert_eq!(s.partition_point(|x| x < &11), 9);
+assert_eq!(s.partition_point(|x| x <= &11), 9);
+assert_eq!(s.binary_search(&11), Err(9));
+

If you want to insert an item to a sorted vector, while maintaining +sort order, consider using partition_point:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+// If `num` is unique, `s.partition_point(|&x| x < num)` (with `<`) is equivalent to
+// `s.binary_search(&num).unwrap_or_else(|x| x)`, but using `<=` will allow `insert`
+// to shift less elements.
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.0.0 · Source

pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
where + F: FnMut(&'a T) -> Ordering,

Binary searches this slice with a comparator function.

+

The comparator function should return an order code that indicates +whether its argument is Less, Equal or Greater the desired +target. +If the slice is not sorted or if the comparator function does not +implement an order consistent with the sort order of the underlying +slice, the returned result is unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by_key, and partition_point.

+
§Examples
+

Looks up a series of four elements. The first is found, with a +uniquely determined position; the second and third are not +found; the fourth could match any position in [1, 4].

+ +
let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+
+let seek = 13;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9));
+let seek = 4;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7));
+let seek = 100;
+assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13));
+let seek = 1;
+let r = s.binary_search_by(|probe| probe.cmp(&seek));
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.10.0 · Source

pub fn binary_search_by_key<'a, B, F>( + &'a self, + b: &B, + f: F, +) -> Result<usize, usize>
where + F: FnMut(&'a T) -> B, + B: Ord,

Binary searches this slice with a key extraction function.

+

Assumes that the slice is sorted by the key, for instance with +sort_by_key using the same key extraction function. +If the slice is not sorted by the key, the returned result is +unspecified and meaningless.

+

If the value is found then Result::Ok is returned, containing the +index of the matching element. If there are multiple matches, then any +one of the matches could be returned. The index is chosen +deterministically, but is subject to change in future versions of Rust. +If the value is not found then Result::Err is returned, containing +the index where a matching element could be inserted while maintaining +sorted order.

+

See also binary_search, binary_search_by, and partition_point.

+
§Examples
+

Looks up a series of four elements in a slice of pairs sorted by +their second elements. The first is found, with a uniquely +determined position; the second and third are not found; the +fourth could match any position in [1, 4].

+ +
let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
+         (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
+         (1, 21), (2, 34), (4, 55)];
+
+assert_eq!(s.binary_search_by_key(&13, |&(a, b)| b),  Ok(9));
+assert_eq!(s.binary_search_by_key(&4, |&(a, b)| b),   Err(7));
+assert_eq!(s.binary_search_by_key(&100, |&(a, b)| b), Err(13));
+let r = s.binary_search_by_key(&1, |&(a, b)| b);
+assert!(match r { Ok(1..=4) => true, _ => false, });
1.20.0 · Source

pub fn sort_unstable(&mut self)
where + T: Ord,

Sorts the slice in ascending order without preserving the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for T panics.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_unstable_by such as f32::total_cmp or f64::total_cmp that defines a +total order users can sort slices containing floating-point values. Alternatively, if all +values in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp +forms a total order, it’s possible to sort the slice with sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort_unstable();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.20.0 · Source

pub fn sort_unstable_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, without preserving the +initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the comparison function compare does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if compare panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the compare does not implement a total order, or if +the compare itself panics.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_unstable_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_unstable_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.20.0 · Source

pub fn sort_unstable_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, without preserving +the initial order of equal elements.

+

This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not +allocate), and O(n * log(n)) worst-case.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+

All original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. Same is true if the implementation of Ord for K panics.

+
§Current implementation
+

The current implementation is based on ipnsort by Lukas Bergdoll and Orson Peters, which +combines the fast average case of quicksort with the fast worst case of heapsort, achieving +linear time on fully sorted and reversed inputs. On inputs with k distinct elements, the +expected time to sort the data is O(n * log(k)).

+

It is typically faster than stable sorting, except in a few special cases, e.g., when the +slice is partially sorted.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_unstable_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.49.0 · Source

pub fn select_nth_unstable( + &mut self, + index: usize, +) -> (&mut [T], &mut T, &mut [T])
where + T: Ord,

Reorders the slice such that the element at index is at a sort-order position. All +elements before index will be <= to this value, and all elements after will be >= to +it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple that partitions the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy x <= self[index].

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy x >= self[index].

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if the implementation of Ord for T does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `<=` to the median, the median itself, and the items `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable(2);
+
+assert!(lesser == [-3, -5] || lesser == [-5, -3]);
+assert_eq!(median, &mut 1);
+assert!(greater == [4, 2] || greater == [2, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [-3, -5, 1, 2, 4] ||
+        v == [-5, -3, 1, 2, 4] ||
+        v == [-3, -5, 1, 4, 2] ||
+        v == [-5, -3, 1, 4, 2]);
1.49.0 · Source

pub fn select_nth_unstable_by<F>( + &mut self, + index: usize, + compare: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T, &T) -> Ordering,

Reorders the slice with a comparator function such that the element at index is at a +sort-order position. All elements before index will be <= to this value, and all +elements after will be >= to it, according to the comparator function.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy +compare(x, self[index]).is_le().

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy +compare(x, self[index]).is_ge().

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), and so always panics on empty slices.

+

May panic if compare does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 2, -3, 1];
+
+// Find the items `>=` to the median, the median itself, and the items `<=` to it, by using
+// a reversed comparator.
+let (before, median, after) = v.select_nth_unstable_by(2, |a, b| b.cmp(a));
+
+assert!(before == [4, 2] || before == [2, 4]);
+assert_eq!(median, &mut 1);
+assert!(after == [-3, -5] || after == [-5, -3]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [2, 4, 1, -5, -3] ||
+        v == [2, 4, 1, -3, -5] ||
+        v == [4, 2, 1, -5, -3] ||
+        v == [4, 2, 1, -3, -5]);
1.49.0 · Source

pub fn select_nth_unstable_by_key<K, F>( + &mut self, + index: usize, + f: F, +) -> (&mut [T], &mut T, &mut [T])
where + F: FnMut(&T) -> K, + K: Ord,

Reorders the slice with a key extraction function such that the element at index is at a +sort-order position. All elements before index will have keys <= to the key at index, +and all elements after will have keys >= to it.

+

This reordering is unstable (i.e. any element that compares equal to the nth element may end +up at that position), in-place (i.e. does not allocate), and runs in O(n) time. This +function is also known as “kth element” in other libraries.

+

Returns a triple partitioning the reordered slice:

+
    +
  • +

    The unsorted subslice before index, whose elements all satisfy f(x) <= f(self[index]).

    +
  • +
  • +

    The element at index.

    +
  • +
  • +

    The unsorted subslice after index, whose elements all satisfy f(x) >= f(self[index]).

    +
  • +
+
§Current implementation
+

The current algorithm is an introselect implementation based on ipnsort by Lukas Bergdoll +and Orson Peters, which is also the basis for sort_unstable. The fallback algorithm is +Median of Medians using Tukey’s Ninther for pivot selection, which guarantees linear runtime +for all inputs.

+
§Panics
+

Panics when index >= len(), meaning it always panics on empty slices.

+

May panic if K: Ord does not implement a total order.

+
§Examples
+
let mut v = [-5i32, 4, 1, -3, 2];
+
+// Find the items `<=` to the absolute median, the absolute median itself, and the items
+// `>=` to it.
+let (lesser, median, greater) = v.select_nth_unstable_by_key(2, |a| a.abs());
+
+assert!(lesser == [1, 2] || lesser == [2, 1]);
+assert_eq!(median, &mut -3);
+assert!(greater == [4, -5] || greater == [-5, 4]);
+
+// We are only guaranteed the slice will be one of the following, based on the way we sort
+// about the specified index.
+assert!(v == [1, 2, -3, 4, -5] ||
+        v == [1, 2, -3, -5, 4] ||
+        v == [2, 1, -3, 4, -5] ||
+        v == [2, 1, -3, -5, 4]);
Source

pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
where + T: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all consecutive repeated elements to the end of the slice according to the +PartialEq trait implementation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+
+let (dedup, duplicates) = slice.partition_dedup();
+
+assert_eq!(dedup, [1, 2, 3, 2, 1]);
+assert_eq!(duplicates, [2, 3, 1]);
Source

pub fn partition_dedup_by<F>(&mut self, same_bucket: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T, &mut T) -> bool,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice satisfying +a given equality relation.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

The same_bucket function is passed references to two elements from the slice and +must determine if the elements compare equal. The elements are passed in opposite order +from their order in the slice, so if same_bucket(a, b) returns true, a is moved +at the end of the slice.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+
+let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+
+assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
Source

pub fn partition_dedup_by_key<K, F>(&mut self, key: F) -> (&mut [T], &mut [T])
where + F: FnMut(&mut T) -> K, + K: PartialEq,

🔬This is a nightly-only experimental API. (slice_partition_dedup)

Moves all but the first of consecutive elements to the end of the slice that resolve +to the same key.

+

Returns two slices. The first contains no consecutive repeated elements. +The second contains all the duplicates in no specified order.

+

If the slice is sorted, the first returned slice contains no duplicates.

+
§Examples
+
#![feature(slice_partition_dedup)]
+
+let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+
+let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+
+assert_eq!(dedup, [10, 20, 30, 20, 11]);
+assert_eq!(duplicates, [21, 30, 13]);
1.26.0 · Source

pub fn rotate_left(&mut self, mid: usize)

Rotates the slice in-place such that the first mid elements of the +slice move to the end while the last self.len() - mid elements move to +the front.

+

After calling rotate_left, the element previously at index mid will +become the first element in the slice.

+
§Panics
+

This function will panic if mid is greater than the length of the +slice. Note that mid == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_left(2);
+assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_left(1);
+assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']);
1.26.0 · Source

pub fn rotate_right(&mut self, k: usize)

Rotates the slice in-place such that the first self.len() - k +elements of the slice move to the end while the last k elements move +to the front.

+

After calling rotate_right, the element previously at index +self.len() - k will become the first element in the slice.

+
§Panics
+

This function will panic if k is greater than the length of the +slice. Note that k == self.len() does not panic and is a no-op +rotation.

+
§Complexity
+

Takes linear (in self.len()) time.

+
§Examples
+
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a.rotate_right(2);
+assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']);
+

Rotating a subslice:

+ +
let mut a = ['a', 'b', 'c', 'd', 'e', 'f'];
+a[1..5].rotate_right(1);
+assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']);
1.50.0 · Source

pub fn fill(&mut self, value: T)
where + T: Clone,

Fills self with elements by cloning value.

+
§Examples
+
let mut buf = vec![0; 10];
+buf.fill(1);
+assert_eq!(buf, vec![1; 10]);
1.51.0 · Source

pub fn fill_with<F>(&mut self, f: F)
where + F: FnMut() -> T,

Fills self with elements returned by calling a closure repeatedly.

+

This method uses a closure to create new values. If you’d rather +Clone a given value, use fill. If you want to use the Default +trait to generate values, you can pass Default::default as the +argument.

+
§Examples
+
let mut buf = vec![1; 10];
+buf.fill_with(Default::default);
+assert_eq!(buf, vec![0; 10]);
1.7.0 · Source

pub fn clone_from_slice(&mut self, src: &[T])
where + T: Clone,

Copies the elements from src into self.

+

The length of src must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Cloning two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.clone_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use clone_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].clone_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.clone_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.9.0 · Source

pub fn copy_from_slice(&mut self, src: &[T])
where + T: Copy,

Copies all elements from src into self, using a memcpy.

+

The length of src must be the same as self.

+

If T does not implement Copy, use clone_from_slice.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Examples
+

Copying two elements from a slice into another:

+ +
let src = [1, 2, 3, 4];
+let mut dst = [0, 0];
+
+// Because the slices have to be the same length,
+// we slice the source slice from four elements
+// to two. It will panic if we don't do this.
+dst.copy_from_slice(&src[2..]);
+
+assert_eq!(src, [1, 2, 3, 4]);
+assert_eq!(dst, [3, 4]);
+

Rust enforces that there can only be one mutable reference with no +immutable references to a particular piece of data in a particular +scope. Because of this, attempting to use copy_from_slice on a +single slice will result in a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+slice[..2].copy_from_slice(&slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.copy_from_slice(&right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 4, 5]);
1.37.0 · Source

pub fn copy_within<R>(&mut self, src: R, dest: usize)
where + R: RangeBounds<usize>, + T: Copy,

Copies elements from one part of the slice to another part of itself, +using a memmove.

+

src is the range within self to copy from. dest is the starting +index of the range within self to copy to, which will have the same +length as src. The two ranges may overlap. The ends of the two ranges +must be less than or equal to self.len().

+
§Panics
+

This function will panic if either range exceeds the end of the slice, +or if the end of src is before the start.

+
§Examples
+

Copying four bytes within a slice:

+ +
let mut bytes = *b"Hello, World!";
+
+bytes.copy_within(1..5, 8);
+
+assert_eq!(&bytes, b"Hello, Wello!");
1.27.0 · Source

pub fn swap_with_slice(&mut self, other: &mut [T])

Swaps all elements in self with those in other.

+

The length of other must be the same as self.

+
§Panics
+

This function will panic if the two slices have different lengths.

+
§Example
+

Swapping two elements across slices:

+ +
let mut slice1 = [0, 0];
+let mut slice2 = [1, 2, 3, 4];
+
+slice1.swap_with_slice(&mut slice2[2..]);
+
+assert_eq!(slice1, [3, 4]);
+assert_eq!(slice2, [1, 2, 0, 0]);
+

Rust enforces that there can only be one mutable reference to a +particular piece of data in a particular scope. Because of this, +attempting to use swap_with_slice on a single slice will result in +a compile failure:

+ +
let mut slice = [1, 2, 3, 4, 5];
+slice[..2].swap_with_slice(&mut slice[3..]); // compile fail!
+

To work around this, we can use split_at_mut to create two distinct +mutable sub-slices from a slice:

+ +
let mut slice = [1, 2, 3, 4, 5];
+
+{
+    let (left, right) = slice.split_at_mut(2);
+    left.swap_with_slice(&mut right[1..]);
+}
+
+assert_eq!(slice, [4, 5, 3, 1, 2]);
1.30.0 · Source

pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T])

Transmutes the slice to a slice of another type, ensuring alignment of the types is +maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
1.30.0 · Source

pub unsafe fn align_to_mut<U>(&mut self) -> (&mut [T], &mut [U], &mut [T])

Transmutes the mutable slice to a mutable slice of another type, ensuring alignment of the +types is maintained.

+

This method splits the slice into three distinct slices: prefix, correctly aligned middle +slice of a new type, and the suffix slice. The middle part will be as big as possible under +the given alignment constraint and element size.

+

This method has no purpose when either input element T or output element U are +zero-sized and will return the original slice without splitting anything.

+
§Safety
+

This method is essentially a transmute with respect to the elements in the returned +middle slice, so all the usual caveats pertaining to transmute::<T, U> also apply here.

+
§Examples
+

Basic usage:

+ +
unsafe {
+    let mut bytes: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
+    let (prefix, shorts, suffix) = bytes.align_to_mut::<u16>();
+    // less_efficient_algorithm_for_bytes(prefix);
+    // more_efficient_algorithm_for_aligned_shorts(shorts);
+    // less_efficient_algorithm_for_bytes(suffix);
+}
Source

pub fn as_simd<const LANES: usize>(&self) -> (&[T], &[Simd<T, LANES>], &[T])
where + Simd<T, LANES>: AsRef<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix.

+

This is a safe wrapper around slice::align_to, so inherits the same +guarantees as that method.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
§Examples
+
#![feature(portable_simd)]
+use core::simd::prelude::*;
+
+let short = &[1, 2, 3];
+let (prefix, middle, suffix) = short.as_simd::<4>();
+assert_eq!(middle, []); // Not enough elements for anything in the middle
+
+// They might be split in any possible way between prefix and suffix
+let it = prefix.iter().chain(suffix).copied();
+assert_eq!(it.collect::<Vec<_>>(), vec![1, 2, 3]);
+
+fn basic_simd_sum(x: &[f32]) -> f32 {
+    use std::ops::Add;
+    let (prefix, middle, suffix) = x.as_simd();
+    let sums = f32x4::from_array([
+        prefix.iter().copied().sum(),
+        0.0,
+        0.0,
+        suffix.iter().copied().sum(),
+    ]);
+    let sums = middle.iter().copied().fold(sums, f32x4::add);
+    sums.reduce_sum()
+}
+
+let numbers: Vec<f32> = (1..101).map(|x| x as _).collect();
+assert_eq!(basic_simd_sum(&numbers[1..99]), 4949.0);
Source

pub fn as_simd_mut<const LANES: usize>( + &mut self, +) -> (&mut [T], &mut [Simd<T, LANES>], &mut [T])
where + Simd<T, LANES>: AsMut<[T; LANES]>, + T: SimdElement, + LaneCount<LANES>: SupportedLaneCount,

🔬This is a nightly-only experimental API. (portable_simd)

Splits a mutable slice into a mutable prefix, a middle of aligned SIMD types, +and a mutable suffix.

+

This is a safe wrapper around slice::align_to_mut, so inherits the same +guarantees as that method.

+

This is the mutable version of slice::as_simd; see that for examples.

+
§Panics
+

This will panic if the size of the SIMD type is different from +LANES times that of the scalar.

+

At the time of writing, the trait restrictions on Simd<T, LANES> keeps +that from ever happening, as only power-of-two numbers of lanes are +supported. It’s possible that, in the future, those restrictions might +be lifted in a way that would make it possible to see panics from this +method for something like LANES == 3.

+
1.82.0 · Source

pub fn is_sorted(&self) -> bool
where + T: PartialOrd,

Checks if the elements of this slice are sorted.

+

That is, for each element a and its following element b, a <= b must hold. If the +slice yields exactly zero or one element, true is returned.

+

Note that if Self::Item is only PartialOrd, but not Ord, the above definition +implies that this function returns false if any two consecutive items are not +comparable.

+
§Examples
+
let empty: [i32; 0] = [];
+
+assert!([1, 2, 2, 9].is_sorted());
+assert!(![1, 3, 2, 4].is_sorted());
+assert!([0].is_sorted());
+assert!(empty.is_sorted());
+assert!(![0.0, 1.0, f32::NAN].is_sorted());
1.82.0 · Source

pub fn is_sorted_by<'a, F>(&'a self, compare: F) -> bool
where + F: FnMut(&'a T, &'a T) -> bool,

Checks if the elements of this slice are sorted using the given comparator function.

+

Instead of using PartialOrd::partial_cmp, this function uses the given compare +function to determine whether two elements are to be considered in sorted order.

+
§Examples
+
assert!([1, 2, 2, 9].is_sorted_by(|a, b| a <= b));
+assert!(![1, 2, 2, 9].is_sorted_by(|a, b| a < b));
+
+assert!([0].is_sorted_by(|a, b| true));
+assert!([0].is_sorted_by(|a, b| false));
+
+let empty: [i32; 0] = [];
+assert!(empty.is_sorted_by(|a, b| false));
+assert!(empty.is_sorted_by(|a, b| true));
1.82.0 · Source

pub fn is_sorted_by_key<'a, F, K>(&'a self, f: F) -> bool
where + F: FnMut(&'a T) -> K, + K: PartialOrd,

Checks if the elements of this slice are sorted using the given key extraction function.

+

Instead of comparing the slice’s elements directly, this function compares the keys of the +elements, as determined by f. Apart from that, it’s equivalent to is_sorted; see its +documentation for more information.

+
§Examples
+
assert!(["c", "bb", "aaa"].is_sorted_by_key(|s| s.len()));
+assert!(![-2i32, -1, 0, 3].is_sorted_by_key(|n| n.abs()));
1.52.0 · Source

pub fn partition_point<P>(&self, pred: P) -> usize
where + P: FnMut(&T) -> bool,

Returns the index of the partition point according to the given predicate +(the index of the first element of the second partition).

+

The slice is assumed to be partitioned according to the given predicate. +This means that all elements for which the predicate returns true are at the start of the slice +and all elements for which the predicate returns false are at the end. +For example, [7, 15, 3, 5, 4, 12, 6] is partitioned under the predicate x % 2 != 0 +(all odd numbers are at the start, all even at the end).

+

If this slice is not partitioned, the returned result is unspecified and meaningless, +as this method performs a kind of binary search.

+

See also binary_search, binary_search_by, and binary_search_by_key.

+
§Examples
+
let v = [1, 2, 3, 3, 5, 6, 7];
+let i = v.partition_point(|&x| x < 5);
+
+assert_eq!(i, 4);
+assert!(v[..i].iter().all(|&x| x < 5));
+assert!(v[i..].iter().all(|&x| !(x < 5)));
+

If all elements of the slice match the predicate, including if the slice +is empty, then the length of the slice will be returned:

+ +
let a = [2, 4, 8];
+assert_eq!(a.partition_point(|x| x < &100), a.len());
+let a: [i32; 0] = [];
+assert_eq!(a.partition_point(|x| x < &100), 0);
+

If you want to insert an item to a sorted vector, while maintaining +sort order:

+ +
let mut s = vec![0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
+let num = 42;
+let idx = s.partition_point(|&x| x <= num);
+s.insert(idx, num);
+assert_eq!(s, [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]);
1.87.0 · Source

pub fn split_off<'a, R>(self: &mut &'a [T], range: R) -> Option<&'a [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off(..3).unwrap();
+
+assert_eq!(slice, &['d']);
+assert_eq!(first_three, &['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off(2..).unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(tail, &['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &[_] = &['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off(5..));
+assert_eq!(None, slice.split_off(..5));
+assert_eq!(None, slice.split_off(..=4));
+let expected: &[char] = &['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off(..4));
1.87.0 · Source

pub fn split_off_mut<'a, R>( + self: &mut &'a mut [T], + range: R, +) -> Option<&'a mut [T]>
where + R: OneSidedRange<usize>,

Removes the subslice corresponding to the given range +and returns a mutable reference to it.

+

Returns None and does not modify the slice if the given +range is out of bounds.

+

Note that this method only accepts one-sided ranges such as +2.. or ..6, but not 2..6.

+
§Examples
+

Splitting off the first three elements of a slice:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut first_three = slice.split_off_mut(..3).unwrap();
+
+assert_eq!(slice, &mut ['d']);
+assert_eq!(first_three, &mut ['a', 'b', 'c']);
+

Splitting off a slice starting with the third element:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+let mut tail = slice.split_off_mut(2..).unwrap();
+
+assert_eq!(slice, &mut ['a', 'b']);
+assert_eq!(tail, &mut ['c', 'd']);
+

Getting None when range is out of bounds:

+ +
let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+
+assert_eq!(None, slice.split_off_mut(5..));
+assert_eq!(None, slice.split_off_mut(..5));
+assert_eq!(None, slice.split_off_mut(..=4));
+let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
+assert_eq!(Some(expected), slice.split_off_mut(..4));
1.87.0 · Source

pub fn split_off_first<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the first element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let first = slice.split_off_first().unwrap();
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'a');
1.87.0 · Source

pub fn split_off_first_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the first element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let first = slice.split_off_first_mut().unwrap();
+*first = 'd';
+
+assert_eq!(slice, &['b', 'c']);
+assert_eq!(first, &'d');
1.87.0 · Source

pub fn split_off_last<'a>(self: &mut &'a [T]) -> Option<&'a T>

Removes the last element of the slice and returns a reference +to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &[_] = &['a', 'b', 'c'];
+let last = slice.split_off_last().unwrap();
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'c');
1.87.0 · Source

pub fn split_off_last_mut<'a>(self: &mut &'a mut [T]) -> Option<&'a mut T>

Removes the last element of the slice and returns a mutable +reference to it.

+

Returns None if the slice is empty.

+
§Examples
+
let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
+let last = slice.split_off_last_mut().unwrap();
+*last = 'd';
+
+assert_eq!(slice, &['a', 'b']);
+assert_eq!(last, &'d');
1.86.0 · Source

pub unsafe fn get_disjoint_unchecked_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> [&mut <I as SliceIndex<[T]>>::Output; N]

Returns mutable references to many indices at once, without doing any checks.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

For a safe alternative see get_disjoint_mut.

+
§Safety
+

Calling this method with overlapping or out-of-bounds indices is undefined behavior +even if the resulting references are not used.

+
§Examples
+
let x = &mut [1, 2, 4];
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0, 2]);
+    *a *= 10;
+    *b *= 100;
+}
+assert_eq!(x, &[10, 2, 400]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([0..1, 1..3]);
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(x, &[8, 88, 888]);
+
+unsafe {
+    let [a, b] = x.get_disjoint_unchecked_mut([1..=2, 0..=0]);
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(x, &[1, 11, 111]);
1.86.0 · Source

pub fn get_disjoint_mut<I, const N: usize>( + &mut self, + indices: [I; N], +) -> Result<[&mut <I as SliceIndex<[T]>>::Output; N], GetDisjointMutError>

Returns mutable references to many indices at once.

+

An index can be either a usize, a Range or a RangeInclusive. Note +that this method takes an array, so all indices must be of the same type. +If passed an array of usizes this method gives back an array of mutable references +to single elements, while if passed an array of ranges it gives back an array of +mutable references to slices.

+

Returns an error if any index is out-of-bounds, or if there are overlapping indices. +An empty range is not considered to overlap if it is located at the beginning or at +the end of another range, but is considered to overlap if it is located in the middle.

+

This method does a O(n^2) check to check that there are no overlapping indices, so be careful +when passing many indices.

+
§Examples
+
let v = &mut [1, 2, 3];
+if let Ok([a, b]) = v.get_disjoint_mut([0, 2]) {
+    *a = 413;
+    *b = 612;
+}
+assert_eq!(v, &[413, 2, 612]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([0..1, 1..3]) {
+    a[0] = 8;
+    b[0] = 88;
+    b[1] = 888;
+}
+assert_eq!(v, &[8, 88, 888]);
+
+if let Ok([a, b]) = v.get_disjoint_mut([1..=2, 0..=0]) {
+    a[0] = 11;
+    a[1] = 111;
+    b[0] = 1;
+}
+assert_eq!(v, &[1, 11, 111]);
Source

pub fn element_offset(&self, element: &T) -> Option<usize>

🔬This is a nightly-only experimental API. (substr_range)

Returns the index that an element reference points to.

+

Returns None if element does not point to the start of an element within the slice.

+

This method is useful for extending slice iterators like slice::split.

+

Note that this uses pointer arithmetic and does not compare elements. +To find the index of an element via comparison, use +.iter().position() instead.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums: &[u32] = &[1, 7, 1, 1];
+let num = &nums[2];
+
+assert_eq!(num, &1);
+assert_eq!(nums.element_offset(num), Some(2));
+

Returning None with an unaligned element:

+ +
#![feature(substr_range)]
+
+let arr: &[[u32; 2]] = &[[0, 1], [2, 3]];
+let flat_arr: &[u32] = arr.as_flattened();
+
+let ok_elm: &[u32; 2] = flat_arr[0..2].try_into().unwrap();
+let weird_elm: &[u32; 2] = flat_arr[1..3].try_into().unwrap();
+
+assert_eq!(ok_elm, &[0, 1]);
+assert_eq!(weird_elm, &[1, 2]);
+
+assert_eq!(arr.element_offset(ok_elm), Some(0)); // Points to element 0
+assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1
Source

pub fn subslice_range(&self, subslice: &[T]) -> Option<Range<usize>>

🔬This is a nightly-only experimental API. (substr_range)

Returns the range of indices that a subslice points to.

+

Returns None if subslice does not point within the slice or if it is not aligned with the +elements in the slice.

+

This method does not compare elements. Instead, this method finds the location in the slice that +subslice was obtained from. To find the index of a subslice via comparison, instead use +.windows().position().

+

This method is useful for extending slice iterators like slice::split.

+

Note that this may return a false positive (either Some(0..0) or Some(self.len()..self.len())) +if subslice has a length of zero and points to the beginning or end of another, separate, slice.

+
§Panics
+

Panics if T is zero-sized.

+
§Examples
+

Basic usage:

+ +
#![feature(substr_range)]
+
+let nums = &[0, 5, 10, 0, 0, 5];
+
+let mut iter = nums
+    .split(|t| *t == 0)
+    .map(|n| nums.subslice_range(n).unwrap());
+
+assert_eq!(iter.next(), Some(0..0));
+assert_eq!(iter.next(), Some(1..3));
+assert_eq!(iter.next(), Some(4..4));
+assert_eq!(iter.next(), Some(5..6));
1.79.0 · Source

pub fn utf8_chunks(&self) -> Utf8Chunks<'_>

Creates an iterator over the contiguous valid UTF-8 ranges of this +slice, and the non-UTF-8 fragments in between.

+

See the Utf8Chunk type for documentation of the items yielded by this iterator.

+
§Examples
+

This function formats arbitrary but mostly-UTF-8 bytes into Rust source +code in the form of a C-string literal (c"...").

+ +
use std::fmt::Write as _;
+
+pub fn cstr_literal(bytes: &[u8]) -> String {
+    let mut repr = String::new();
+    repr.push_str("c\"");
+    for chunk in bytes.utf8_chunks() {
+        for ch in chunk.valid().chars() {
+            // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for non-printable characters.
+            write!(repr, "{}", ch.escape_debug()).unwrap();
+        }
+        for byte in chunk.invalid() {
+            write!(repr, "\\x{:02X}", byte).unwrap();
+        }
+    }
+    repr.push('"');
+    repr
+}
+
+fn main() {
+    let lit = cstr_literal(b"\xferris the \xf0\x9f\xa6\x80\x07");
+    let expected = stringify!(c"\xFErris the 🦀\u{7}");
+    assert_eq!(lit, expected);
+}
1.0.0 · Source

pub fn sort(&mut self)
where + T: Ord,

Sorts the slice in ascending order, preserving initial order of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the implementation of Ord for T does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

When applicable, unstable sorting is preferred because it is generally faster than stable +sorting and it doesn’t allocate auxiliary memory. See +sort_unstable. The exception are partially sorted slices, which +may be better served with slice::sort.

+

Sorting types that only implement PartialOrd such as f32 and f64 require +additional precautions. For example, f32::NAN != f32::NAN, which doesn’t fulfill the +reflexivity requirement of Ord. By using an alternative comparison function with +slice::sort_by such as f32::total_cmp or f64::total_cmp that defines a total +order users can sort slices containing floating-point values. Alternatively, if all values +in the slice are guaranteed to be in a subset for which PartialOrd::partial_cmp forms a +total order, it’s possible to sort the slice with sort_by(|a, b| a.partial_cmp(b).unwrap()).

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for T does not implement a total order, or if +the Ord implementation itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+
+v.sort();
+assert_eq!(v, [-5, -3, 1, 2, 4]);
1.0.0 · Source

pub fn sort_by<F>(&mut self, compare: F)
where + F: FnMut(&T, &T) -> Ordering,

Sorts the slice in ascending order with a comparison function, preserving initial order of +equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(n * log(n)) +worst-case.

+

If the comparison function compare does not implement a total order, the function may +panic; even if the function exits normally, the resulting order of elements in the slice is +unspecified. See also the note on panicking below.

+

For example |a, b| (a - b).cmp(a) is a comparison function that is neither transitive nor +reflexive nor total, a < b < c < a with a = 1, b = 2, c = 3. For more information and +examples see the Ord documentation.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if compare does not implement a total order, or if compare itself panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4, -5, 1, -3, 2];
+v.sort_by(|a, b| a.cmp(b));
+assert_eq!(v, [-5, -3, 1, 2, 4]);
+
+// reverse sorting
+v.sort_by(|a, b| b.cmp(a));
+assert_eq!(v, [4, 2, 1, -3, -5]);
1.7.0 · Source

pub fn sort_by_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n * log(n)) +worst-case, where the key function is O(m).

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+
§Current implementation
+

The current implementation is based on driftsort by Orson Peters and Lukas Bergdoll, which +combines the fast average case of quicksort with the fast worst case and partial run +detection of mergesort, achieving linear time on fully sorted and reversed inputs. On inputs +with k distinct elements, the expected time to sort the data is O(n * log(k)).

+

The auxiliary memory allocation behavior depends on the input length. Short slices are +handled without allocation, medium sized slices allocate self.len() and beyond that it +clamps at self.len() / 2.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation or the key-function f panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2];
+
+v.sort_by_key(|k| k.abs());
+assert_eq!(v, [1, 2, -3, 4, -5]);
1.34.0 · Source

pub fn sort_by_cached_key<K, F>(&mut self, f: F)
where + F: FnMut(&T) -> K, + K: Ord,

Sorts the slice in ascending order with a key extraction function, preserving initial order +of equal elements.

+

This sort is stable (i.e., does not reorder equal elements) and O(m * n + n * +log(n)) worst-case, where the key function is O(m).

+

During sorting, the key function is called at most once per element, by using temporary +storage to remember the results of key evaluation. The order of calls to the key function is +unspecified and may change in future versions of the standard library.

+

If the implementation of Ord for K does not implement a total order, the function +may panic; even if the function exits normally, the resulting order of elements in the slice +is unspecified. See also the note on panicking below.

+

For simple key functions (e.g., functions that are property accesses or basic operations), +sort_by_key is likely to be faster.

+
§Current implementation
+

The current implementation is based on instruction-parallel-network sort by Lukas +Bergdoll, which combines the fast average case of randomized quicksort with the fast worst +case of heapsort, while achieving linear time on fully sorted and reversed inputs. And +O(k * log(n)) where k is the number of distinct elements in the input. It leverages +superscalar out-of-order execution capabilities commonly found in CPUs, to efficiently +perform the operation.

+

In the worst case, the algorithm allocates temporary storage in a Vec<(K, usize)> the +length of the slice.

+
§Panics
+

May panic if the implementation of Ord for K does not implement a total order, or if +the Ord implementation panics.

+

All safe functions on slices preserve the invariant that even if the function panics, all +original elements will remain in the slice and any possible modifications via interior +mutability are observed in the input. This ensures that recovery code (for instance inside +of a Drop or following a catch_unwind) will still have access to all the original +elements. For instance, if the slice belongs to a Vec, the Vec::drop method will be able +to dispose of all contained elements.

+
§Examples
+
let mut v = [4i32, -5, 1, -3, 2, 10];
+
+// Strings are sorted by lexicographical order.
+v.sort_by_cached_key(|k| k.to_string());
+assert_eq!(v, [-3, -5, 1, 10, 2, 4]);
1.0.0 · Source

pub fn to_vec(&self) -> Vec<T>
where + T: Clone,

Copies self into a new Vec.

+
§Examples
+
let s = [10, 40, 30];
+let x = s.to_vec();
+// Here, `s` and `x` can be modified independently.
Source

pub fn to_vec_in<A>(&self, alloc: A) -> Vec<T, A>
where + A: Allocator, + T: Clone,

🔬This is a nightly-only experimental API. (allocator_api)

Copies self into a new Vec with an allocator.

+
§Examples
+
#![feature(allocator_api)]
+
+use std::alloc::System;
+
+let s = [10, 40, 30];
+let x = s.to_vec_in(System);
+// Here, `s` and `x` can be modified independently.
1.40.0 · Source

pub fn repeat(&self, n: usize) -> Vec<T>
where + T: Copy,

Creates a vector by copying a slice n times.

+
§Panics
+

This function will panic if the capacity would overflow.

+
§Examples
+
assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]);
+

A panic upon overflow:

+ +
// this will panic at runtime
+b"0123456789abcdef".repeat(usize::MAX);
1.0.0 · Source

pub fn concat<Item>(&self) -> <[T] as Concat<Item>>::Output
where + [T]: Concat<Item>, + Item: ?Sized,

Flattens a slice of T into a single value Self::Output.

+
§Examples
+
assert_eq!(["hello", "world"].concat(), "helloworld");
+assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
1.3.0 · Source

pub fn join<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].join(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].join(&0), [1, 2, 0, 3, 4]);
+assert_eq!([[1, 2], [3, 4]].join(&[0, 0][..]), [1, 2, 0, 0, 3, 4]);
1.0.0 · Source

pub fn connect<Separator>( + &self, + sep: Separator, +) -> <[T] as Join<Separator>>::Output
where + [T]: Join<Separator>,

👎Deprecated since 1.3.0: renamed to join

Flattens a slice of T into a single value Self::Output, placing a +given separator between each.

+
§Examples
+
assert_eq!(["hello", "world"].connect(" "), "hello world");
+assert_eq!([[1, 2], [3, 4]].connect(&0), [1, 2, 0, 3, 4]);
1.23.0 · Source

pub fn to_ascii_uppercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII upper case equivalent.

+

ASCII letters ‘a’ to ‘z’ are mapped to ‘A’ to ‘Z’, +but non-ASCII letters are unchanged.

+

To uppercase the value in-place, use make_ascii_uppercase.

+
1.23.0 · Source

pub fn to_ascii_lowercase(&self) -> Vec<u8>

Returns a vector containing a copy of this slice where each byte +is mapped to its ASCII lower case equivalent.

+

ASCII letters ‘A’ to ‘Z’ are mapped to ‘a’ to ‘z’, +but non-ASCII letters are unchanged.

+

To lowercase the value in-place, use make_ascii_lowercase.

+

Trait Implementations§

Source§

impl Clone for ConnectionId

Source§

fn clone(&self) -> ConnectionId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for ConnectionId

Source§

type Target = [u8]

The resulting type after dereferencing.
Source§

fn deref(&self) -> &[u8]

Dereferences the value.
Source§

impl DerefMut for ConnectionId

Source§

fn deref_mut(&mut self) -> &mut [u8]

Mutably dereferences the value.
Source§

impl Display for ConnectionId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Hash for ConnectionId

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for ConnectionId

Source§

fn cmp(&self, other: &ConnectionId) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for ConnectionId

Source§

fn eq(&self, other: &ConnectionId) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for ConnectionId

Source§

fn partial_cmp(&self, other: &ConnectionId) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl Copy for ConnectionId

Source§

impl Eq for ConnectionId

Source§

impl StructuralPartialEq for ConnectionId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<P, T> Receiver for P
where + P: Deref<Target = T> + ?Sized, + T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where + T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ConnectionStats.html b/pr/234/docs/iroh_quinn_proto/struct.ConnectionStats.html new file mode 100644 index 000000000..521d6831c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ConnectionStats.html @@ -0,0 +1,29 @@ +ConnectionStats in iroh_quinn_proto - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { + pub udp_tx: UdpStats, + pub udp_rx: UdpStats, + pub frame_tx: FrameStats, + pub frame_rx: FrameStats, +}
Expand description

Connection statistics

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§udp_tx: UdpStats

Statistics about UDP datagrams transmitted on a connection

+
§udp_rx: UdpStats

Statistics about UDP datagrams received on a connection

+
§frame_tx: FrameStats

Statistics about frames transmitted on a connection

+
§frame_rx: FrameStats

Statistics about frames received on a connection

+

Trait Implementations§

Source§

impl Clone for ConnectionStats

Source§

fn clone(&self) -> ConnectionStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionStats

Source§

fn default() -> ConnectionStats

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Datagram.html b/pr/234/docs/iroh_quinn_proto/struct.Datagram.html new file mode 100644 index 000000000..ab97fdaa8 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Datagram.html @@ -0,0 +1,23 @@ +Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
+    pub data: Bytes,
+}
Expand description

An unreliable datagram

+

Fields§

§data: Bytes

Payload

+

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Datagram

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Datagrams.html b/pr/234/docs/iroh_quinn_proto/struct.Datagrams.html new file mode 100644 index 000000000..dfbacc4fc --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Datagrams.html @@ -0,0 +1,37 @@ +Datagrams in iroh_quinn_proto - Rust

Datagrams

Struct Datagrams 

Source
pub struct Datagrams<'a> { /* private fields */ }
Expand description

API to control datagram traffic

+

Implementations§

Source§

impl Datagrams<'_>

Source

pub fn send(&mut self, data: Bytes, drop: bool) -> Result<(), SendDatagramError>

Queue an unreliable, unordered datagram for immediate transmission

+

If drop is true, previously queued datagrams which are still unsent may be discarded to +make space for this datagram, in order of oldest to newest. If drop is false, and there +isn’t enough space due to previously queued datagrams, this function will return +SendDatagramError::Blocked. Event::DatagramsUnblocked will be emitted once datagrams +have been sent.

+

Returns Err iff a len-byte datagram cannot currently be sent.

+
Source

pub fn max_size(&self) -> Option<usize>

Compute the maximum size of datagrams that may be passed to send_datagram

+

Returns None if datagrams are unsupported by the peer or disabled locally.

+

This may change over the lifetime of a connection according to variation in the path MTU +estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer’s +limit is large this is guaranteed to be a little over a kilobyte at minimum.

+

Not necessarily the maximum size of received datagrams.

+

When multipath is enabled, this is calculated using the smallest MTU across all +available paths.

+
Source

pub fn recv(&mut self) -> Option<Bytes>

Receive an unreliable, unordered datagram

+
Source

pub fn send_buffer_space(&self) -> usize

Bytes available in the outgoing datagram buffer

+

When greater than zero, sending a datagram of at most this size is +guaranteed not to cause older datagrams to be dropped.

+

Auto Trait Implementations§

§

impl<'a> Freeze for Datagrams<'a>

§

impl<'a> !RefUnwindSafe for Datagrams<'a>

§

impl<'a> Send for Datagrams<'a>

§

impl<'a> Sync for Datagrams<'a>

§

impl<'a> Unpin for Datagrams<'a>

§

impl<'a> !UnwindSafe for Datagrams<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Endpoint.html b/pr/234/docs/iroh_quinn_proto/struct.Endpoint.html new file mode 100644 index 000000000..596485395 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Endpoint.html @@ -0,0 +1,77 @@ +Endpoint in iroh_quinn_proto - Rust

Endpoint

Struct Endpoint 

Source
pub struct Endpoint { /* private fields */ }
Expand description

The main entry point to the library

+

This object performs no I/O whatsoever. Instead, it consumes incoming packets and +connection-generated events via handle and handle_event.

+

Implementations§

Source§

impl Endpoint

Source

pub fn new( + config: Arc<EndpointConfig>, + server_config: Option<Arc<ServerConfig>>, + allow_mtud: bool, + rng_seed: Option<[u8; 32]>, +) -> Self

Create a new endpoint

+

allow_mtud enables path MTU detection when requested by Connection configuration for +better performance. This requires that outgoing packets are never fragmented, which can be +achieved via e.g. the IPV6_DONTFRAG socket option.

+

If rng_seed is provided, it will be used to initialize the endpoint’s rng (having priority +over the rng seed configured in EndpointConfig). Note that the rng_seed parameter will +be removed in a future release, so prefer setting it to None and configuring rng seeds +using EndpointConfig::rng_seed.

+
Source

pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>)

Replace the server configuration, affecting new incoming connections only

+
Source

pub fn handle_event( + &mut self, + ch: ConnectionHandle, + event: EndpointEvent, +) -> Option<ConnectionEvent>

Process EndpointEvents emitted from related Connections

+

In turn, processing this event may return a ConnectionEvent for the same Connection.

+
Source

pub fn handle( + &mut self, + now: Instant, + remote: SocketAddr, + local_ip: Option<IpAddr>, + 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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.EndpointConfig.html b/pr/234/docs/iroh_quinn_proto/struct.EndpointConfig.html new file mode 100644 index 000000000..727b70062 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.EndpointConfig.html @@ -0,0 +1,59 @@ +EndpointConfig in iroh_quinn_proto - Rust

EndpointConfig

Struct EndpointConfig 

Source
pub struct EndpointConfig { /* private fields */ }
Expand description

Global configuration for the endpoint, affecting all connections

+

Default values should be suitable for most internet applications.

+

Implementations§

Source§

impl EndpointConfig

Source

pub fn new(reset_key: Arc<dyn HmacKey>) -> Self

Create a default config with a particular reset_key

+
Source

pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>( + &mut self, + factory: F, +) -> &mut Self

Supply a custom connection ID generator factory

+

Called once by each Endpoint constructed from this configuration to obtain the CID +generator which will be used to generate the CIDs used for incoming packets on all +connections involving that Endpoint. A custom CID generator allows applications to embed +information in local connection IDs, e.g. to support stateless packet-level load balancers.

+

Defaults to HashedConnectionIdGenerator.

+
Source

pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self

Private key used to send authenticated connection resets to peers who were +communicating with a previous instance of this endpoint.

+
Source

pub fn max_udp_payload_size( + &mut self, + value: u16, +) -> Result<&mut Self, ConfigError>

Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).

+

Must be greater or equal than 1200.

+

Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical +1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet +with jumbo frames) can raise this to improve performance at the cost of a linear increase in +datagram receive buffer size.

+
Source

pub fn get_max_udp_payload_size(&self) -> u64

Get the current value of max_udp_payload_size

+
Source

pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self

Override supported QUIC versions

+
Source

pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self

Whether to accept QUIC packets containing any value for the fixed bit

+

Enabled by default. Helps protect against protocol ossification and makes traffic less +identifiable to observers. Disable if helping observers identify this traffic as QUIC is +desired.

+
Source

pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self

Minimum interval between outgoing stateless reset packets

+

Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets, +e.g. ISAKMP/IKE amplification. Larger values provide a stronger defense, but may delay +detection of some error conditions by clients. Using a ConnectionIdGenerator with a low +rate of false positives in validate reduces the risk +incurred by a small minimum reset interval.

+
Source

pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self

Optional seed to be used internally for random number generation

+

By default, quinn will initialize an endpoint’s rng using a platform entropy source. +However, you can seed the rng yourself through this method (e.g. if you need to run quinn +deterministically or if you are using quinn in an environment that doesn’t have a source of +entropy available).

+

Trait Implementations§

Source§

impl Clone for EndpointConfig

Source§

fn clone(&self) -> EndpointConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EndpointConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for EndpointConfig

Available on crate features aws-lc-rs or ring only.
Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.EndpointEvent.html b/pr/234/docs/iroh_quinn_proto/struct.EndpointEvent.html new file mode 100644 index 000000000..ab61a7a97 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.EndpointEvent.html @@ -0,0 +1,23 @@ +EndpointEvent in iroh_quinn_proto - Rust

EndpointEvent

Struct EndpointEvent 

Source
pub struct EndpointEvent(/* private fields */);
Expand description

Events sent from a Connection to an Endpoint

+

Implementations§

Source§

impl EndpointEvent

Source

pub fn drained() -> Self

Construct an event that indicating that a Connection will no longer emit events

+

Useful for notifying an Endpoint that a Connection has been destroyed outside of the +usual state machine flow, e.g. when being dropped by the user.

+
Source

pub fn is_drained(&self) -> bool

Determine whether this is the last event a Connection will emit

+

Useful for determining when connection-related event loop state can be freed.

+

Trait Implementations§

Source§

impl Debug for EndpointEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html b/pr/234/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html new file mode 100644 index 000000000..bd414463e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html @@ -0,0 +1,19 @@ +FixedLengthConnectionIdParser in iroh_quinn_proto - Rust

FixedLengthConnectionIdParser

Struct FixedLengthConnectionIdParser 

Source
pub struct FixedLengthConnectionIdParser { /* private fields */ }
Expand description

A ConnectionIdParser implementation that assumes the connection ID is of fixed length

+

Implementations§

Source§

impl FixedLengthConnectionIdParser

Source

pub fn new(expected_len: usize) -> Self

Create a new instance of FixedLengthConnectionIdParser

+

Trait Implementations§

Source§

impl ConnectionIdParser for FixedLengthConnectionIdParser

Source§

fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>

Parse a connection id from given buffer

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.FrameStats.html b/pr/234/docs/iroh_quinn_proto/struct.FrameStats.html new file mode 100644 index 000000000..31d44ed8e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.FrameStats.html @@ -0,0 +1,58 @@ +FrameStats in iroh_quinn_proto - Rust

FrameStats

Struct FrameStats 

Source
#[non_exhaustive]
pub struct FrameStats {
Show 37 fields + pub acks: u64, + pub path_acks: u64, + pub ack_frequency: u64, + pub crypto: u64, + pub connection_close: u64, + pub data_blocked: u64, + pub datagram: u64, + pub handshake_done: u8, + pub immediate_ack: u64, + pub max_data: u64, + pub max_stream_data: u64, + pub max_streams_bidi: u64, + pub max_streams_uni: u64, + pub new_connection_id: u64, + pub path_new_connection_id: u64, + pub new_token: u64, + pub path_challenge: u64, + pub path_response: u64, + pub ping: u64, + pub reset_stream: u64, + pub retire_connection_id: u64, + pub path_retire_connection_id: u64, + pub stream_data_blocked: u64, + pub streams_blocked_bidi: u64, + pub streams_blocked_uni: u64, + pub stop_sending: u64, + pub stream: u64, + pub observed_addr: u64, + pub path_abandon: u64, + pub path_status_available: u64, + pub path_status_backup: u64, + pub max_path_id: u64, + pub paths_blocked: u64, + pub path_cids_blocked: u64, + pub add_address: u64, + pub reach_out: u64, + pub remove_address: u64, +
}
Expand description

Number of frames transmitted or received of each frame type

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§acks: u64§path_acks: u64§ack_frequency: u64§crypto: u64§connection_close: u64§data_blocked: u64§datagram: u64§handshake_done: u8§immediate_ack: u64§max_data: u64§max_stream_data: u64§max_streams_bidi: u64§max_streams_uni: u64§new_connection_id: u64§path_new_connection_id: u64§new_token: u64§path_challenge: u64§path_response: u64§ping: u64§reset_stream: u64§retire_connection_id: u64§path_retire_connection_id: u64§stream_data_blocked: u64§streams_blocked_bidi: u64§streams_blocked_uni: u64§stop_sending: u64§stream: u64§observed_addr: u64§path_abandon: u64§path_status_available: u64§path_status_backup: u64§max_path_id: u64§paths_blocked: u64§path_cids_blocked: u64§add_address: u64§reach_out: u64§remove_address: u64

Trait Implementations§

Source§

impl Clone for FrameStats

Source§

fn clone(&self) -> FrameStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for FrameStats

Source§

fn default() -> FrameStats

Returns the “default value” for a type. Read more
Source§

impl Copy for FrameStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.FrameType.html b/pr/234/docs/iroh_quinn_proto/struct.FrameType.html new file mode 100644 index 000000000..fb3675bd4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.FrameType.html @@ -0,0 +1,26 @@ +FrameType in iroh_quinn_proto - Rust

FrameType

Struct FrameType 

Source
pub struct FrameType(/* private fields */);
Expand description

A QUIC frame type

+

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 Codec 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§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer
Source§

impl Debug for FrameType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for FrameType

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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 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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html new file mode 100644 index 000000000..e9108611f --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.HashedConnectionIdGenerator.html @@ -0,0 +1,26 @@ +HashedConnectionIdGenerator in iroh_quinn_proto - Rust

HashedConnectionIdGenerator

Struct HashedConnectionIdGenerator 

Source
pub struct HashedConnectionIdGenerator { /* private fields */ }
Expand description

Generates 8-byte connection IDs that can be efficiently +validated

+

This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless +helps prevents Quinn from responding to non-QUIC packets at very low cost.

+

Implementations§

Source§

impl HashedConnectionIdGenerator

Source

pub fn new() -> Self

Create a generator with a random key

+
Source

pub fn from_key(key: u64) -> Self

Create a generator with a specific key

+

Allows validate to recognize a consistent set of +connection IDs across restarts

+
Source

pub fn set_lifetime(&mut self, d: Duration) -> &mut Self

Set the lifetime of CIDs created by this generator

+

Trait Implementations§

Source§

impl ConnectionIdGenerator for HashedConnectionIdGenerator

Source§

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID Read more
Source§

fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator Read more
Source§

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator
Source§

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs Read more
Source§

impl Default for HashedConnectionIdGenerator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.IdleTimeout.html b/pr/234/docs/iroh_quinn_proto/struct.IdleTimeout.html new file mode 100644 index 000000000..8a9e01aee --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.IdleTimeout.html @@ -0,0 +1,42 @@ +IdleTimeout in iroh_quinn_proto - Rust

IdleTimeout

Struct IdleTimeout 

Source
pub struct IdleTimeout(/* private fields */);
Expand description

Maximum duration of inactivity to accept before timing out the connection

+

This wraps an underlying VarInt, representing the duration in milliseconds. Values can be +constructed by converting directly from VarInt, or using TryFrom<Duration>.

+ +
// A `VarInt`-encoded value in milliseconds
+let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+
+// Try to convert a `Duration` into a `VarInt`-encoded timeout
+let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;

Trait Implementations§

Source§

impl Clone for IdleTimeout

Source§

fn clone(&self) -> IdleTimeout

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for IdleTimeout

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for IdleTimeout

Source§

fn default() -> IdleTimeout

Returns the “default value” for a type. Read more
Source§

impl From<VarInt> for IdleTimeout

Source§

fn from(inner: VarInt) -> Self

Converts to this type from the input type.
Source§

impl Hash for IdleTimeout

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where + H: Hasher, + Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for IdleTimeout

Source§

fn cmp(&self, other: &IdleTimeout) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where + Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where + Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for IdleTimeout

Source§

fn eq(&self, other: &IdleTimeout) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl PartialOrd for IdleTimeout

Source§

fn partial_cmp(&self, other: &IdleTimeout) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the +<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > +operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by +the >= operator. Read more
Source§

impl TryFrom<Duration> for IdleTimeout

Source§

type Error = VarIntBoundsExceeded

The type returned in the event of a conversion error.
Source§

fn try_from(timeout: Duration) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl Copy for IdleTimeout

Source§

impl Eq for IdleTimeout

Source§

impl StructuralPartialEq for IdleTimeout

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Comparable<K> for Q
where + Q: Ord + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Incoming.html b/pr/234/docs/iroh_quinn_proto/struct.Incoming.html new file mode 100644 index 000000000..ee52e607a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Incoming.html @@ -0,0 +1,30 @@ +Incoming in iroh_quinn_proto - Rust

Incoming

Struct Incoming 

Source
pub struct Incoming { /* private fields */ }
Expand description

An incoming connection for which the server has not yet begun its part of the handshake.

+

Implementations§

Source§

impl Incoming

Source

pub fn local_ip(&self) -> Option<IpAddr>

The local IP address which was used when the peer established the connection

+

This has the same behavior as Connection::local_ip.

+
Source

pub fn remote_address(&self) -> SocketAddr

The peer’s UDP address

+
Source

pub fn remote_address_validated(&self) -> bool

Whether the socket address that is initiating this connection has been validated

+

This means that the sender of the initial packet has proved that they can receive traffic +sent to self.remote_address().

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn may_retry(&self) -> bool

Whether it is legal to respond with a retry packet

+

If self.remote_address_validated() is false, self.may_retry() is guaranteed to be true. +The inverse is not guaranteed.

+
Source

pub fn orig_dst_cid(&self) -> ConnectionId

The original destination connection ID sent by the client

+

Trait Implementations§

Source§

impl Debug for Incoming

Source§

fn fmt(&self, 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<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.InvalidCid.html b/pr/234/docs/iroh_quinn_proto/struct.InvalidCid.html new file mode 100644 index 000000000..6788a7756 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.InvalidCid.html @@ -0,0 +1,20 @@ +InvalidCid in iroh_quinn_proto - Rust

InvalidCid

Struct InvalidCid 

Source
pub struct InvalidCid;
Expand description

The connection ID was not recognized by the ConnectionIdGenerator

+

Trait Implementations§

Source§

impl Clone for InvalidCid

Source§

fn clone(&self) -> InvalidCid

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for InvalidCid

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for InvalidCid

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html b/pr/234/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html new file mode 100644 index 000000000..ade8eadb7 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.MtuDiscoveryConfig.html @@ -0,0 +1,78 @@ +MtuDiscoveryConfig in iroh_quinn_proto - Rust

MtuDiscoveryConfig

Struct MtuDiscoveryConfig 

Source
pub struct MtuDiscoveryConfig { /* private fields */ }
Expand description

Parameters governing MTU discovery.

+

§The why of MTU discovery

+

By design, QUIC ensures during the handshake that the network path between the client and the +server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words, +once the connection is established, we know that the network path’s maximum transmission unit +(MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can +split outgoing data in packets of 1200 bytes, with confidence that the network will be able to +deliver them (if the endpoint were to send bigger packets, they could prove too big and end up +being dropped).

+

There is, however, a significant overhead associated to sending a packet. If the same +information can be sent in fewer packets, that results in higher throughput. The amount of +packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the +bigger the packets that can be sent, and the fewer packets that are needed to transmit a given +amount of bytes.

+

Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the +path’s MTU and, if it turns out to be higher, start sending bigger packets.

+

§MTU discovery internals

+

Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU +Discovery), described in section 14.3 of RFC +9000. This method consists of sending +QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote +peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote +peer, which in turn means that the network path’s MTU is of at least the packet’s size. If the +probe is lost, it is sent another 2 times before concluding that the MTU is lower than the +packet’s size.

+

MTU discovery runs on a schedule (e.g. every 600 seconds) specified through +MtuDiscoveryConfig::interval. The first run happens right after the handshake, and +subsequent discoveries are scheduled to run when the interval has elapsed, starting from the +last time when MTU discovery completed.

+

Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest +is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The +lower bound of the search is equal to TransportConfig::initial_mtu in the +initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The +upper bound is determined by the minimum of MtuDiscoveryConfig::upper_bound and the +max_udp_payload_size transport parameter received from the peer during the handshake.

+

§Black hole detection

+

If, at some point, the network path no longer accepts packets of the detected size, packet loss +will eventually trigger black hole detection and reset the detected MTU to 1200. In that case, +MTU discovery will be triggered after MtuDiscoveryConfig::black_hole_cooldown (ignoring the +timer that was set based on MtuDiscoveryConfig::interval).

+

§Interaction between peers

+

There is no guarantee that the MTU on the path between A and B is the same as the MTU of the +path between B and A. Therefore, each peer in the connection needs to run MTU discovery +independently in order to discover the path’s MTU.

+

Implementations§

Source§

impl MtuDiscoveryConfig

Source

pub fn interval(&mut self, value: Duration) -> &mut Self

Specifies the time to wait after completing MTU discovery before starting a new MTU +discovery run.

+

Defaults to 600 seconds, as recommended by RFC +8899.

+
Source

pub fn upper_bound(&mut self, value: u16) -> &mut Self

Specifies the upper bound to the max UDP payload size that MTU discovery will search for.

+

Defaults to 1452, to stay within Ethernet’s MTU when using IPv4 and IPv6. The highest +allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.

+

It is safe to use an arbitrarily high upper bound, regardless of the network path’s MTU. The +only drawback is that MTU discovery might take more time to finish.

+
Source

pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self

Specifies the amount of time that MTU discovery should wait after a black hole was detected +before running again. Defaults to one minute.

+

Black hole detection can be spuriously triggered in case of congestion, so it makes sense to +try MTU discovery again after a short period of time.

+
Source

pub fn minimum_change(&mut self, value: u16) -> &mut Self

Specifies the minimum MTU change to stop the MTU discovery phase. +Defaults to 20.

+

Trait Implementations§

Source§

impl Clone for MtuDiscoveryConfig

Source§

fn clone(&self) -> MtuDiscoveryConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for MtuDiscoveryConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for MtuDiscoveryConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.NoneTokenLog.html b/pr/234/docs/iroh_quinn_proto/struct.NoneTokenLog.html new file mode 100644 index 000000000..784d98480 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.NoneTokenLog.html @@ -0,0 +1,24 @@ +NoneTokenLog in iroh_quinn_proto - Rust

NoneTokenLog

Struct NoneTokenLog 

Source
pub struct NoneTokenLog;
Expand description

Null implementation of TokenLog, which never accepts tokens

+

Trait Implementations§

Source§

impl TokenLog for NoneTokenLog

Source§

fn check_and_insert( + &self, + _: u128, + _: SystemTime, + _: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.NoneTokenStore.html b/pr/234/docs/iroh_quinn_proto/struct.NoneTokenStore.html new file mode 100644 index 000000000..72ed49d31 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.NoneTokenStore.html @@ -0,0 +1,18 @@ +NoneTokenStore in iroh_quinn_proto - Rust

NoneTokenStore

Struct NoneTokenStore 

Source
pub struct NoneTokenStore;
Expand description

Null implementation of TokenStore, which does not store any tokens

+

Trait Implementations§

Source§

impl TokenStore for NoneTokenStore

Source§

fn insert(&self, _: &str, _: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, _: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.PartialDecode.html b/pr/234/docs/iroh_quinn_proto/struct.PartialDecode.html new file mode 100644 index 000000000..f486bdc13 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.PartialDecode.html @@ -0,0 +1,35 @@ +PartialDecode in iroh_quinn_proto - Rust

PartialDecode

Struct PartialDecode 

Source
pub struct PartialDecode { /* private fields */ }
Expand description

Decodes a QUIC packet’s invariant header

+

Due to packet number encryption, it is impossible to fully decode a header +(which includes a variable-length packet number) without crypto context. +The crypto context (represented by the Crypto type in Quinn) is usually +part of the Connection, or can be derived from the destination CID for +Initial packets.

+

To cope with this, we decode the invariant header (which should be stable +across QUIC versions), which gives us the destination CID and allows us +to inspect the version and packet type (which depends on the version). +This information allows us to fully decode and decrypt the packet.

+

Implementations§

Source§

impl PartialDecode

Source

pub fn new( + bytes: BytesMut, + cid_parser: &(impl ConnectionIdParser + ?Sized), + supported_versions: &[u32], + grease_quic_bit: bool, +) -> Result<(Self, Option<BytesMut>), PacketDecodeError>

Begin decoding a QUIC packet from bytes, returning any trailing data not part of that packet

+
Source

pub fn dst_cid(&self) -> ConnectionId

The destination connection ID of the packet

+
Source

pub fn len(&self) -> usize

Length of QUIC packet being decoded

+

Trait Implementations§

Source§

impl Debug for PartialDecode

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.PathId.html b/pr/234/docs/iroh_quinn_proto/struct.PathId.html new file mode 100644 index 000000000..4d1ce736c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.PathId.html @@ -0,0 +1,42 @@ +PathId in iroh_quinn_proto - Rust

PathId

Struct PathId 

Source
pub struct PathId(/* private fields */);
Expand description

Id representing different paths when using multipath extension

+

Implementations§

Source§

impl PathId

Source

pub const MAX: Self

The maximum path ID allowed.

+
Source

pub const ZERO: Self

The 0 path id.

+
Source

pub fn saturating_add(self, rhs: impl Into<Self>) -> Self

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+
Source

pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self

Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds +instead of overflowing.

+

Trait Implementations§

Source§

impl Clone for PathId

Source§

fn clone(&self) -> PathId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B: BufMut>(&self, w: &mut B)

Append the encoding of self to the provided buffer
Source§

impl Debug for PathId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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<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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.PathStats.html b/pr/234/docs/iroh_quinn_proto/struct.PathStats.html new file mode 100644 index 000000000..5995d16ba --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.PathStats.html @@ -0,0 +1,47 @@ +PathStats in iroh_quinn_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { + pub rtt: Duration, + pub cwnd: u64, + pub congestion_events: u64, + pub lost_packets: u64, + pub lost_bytes: u64, + pub sent_packets: u64, + pub sent_plpmtud_probes: u64, + pub lost_plpmtud_probes: u64, + pub black_holes_detected: u64, + pub current_mtu: u16, +}
Expand description

Statistics related to a transmission path

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§rtt: Duration

Current best estimate of this connection’s latency (round-trip-time)

+
§cwnd: u64

Current congestion window of the connection

+
§congestion_events: u64

Congestion events on the connection

+
§lost_packets: u64

The amount of packets lost on this path

+
§lost_bytes: u64

The amount of bytes lost on this path

+
§sent_packets: u64

The amount of packets sent on this path

+
§sent_plpmtud_probes: u64

The amount of PLPMTUD probe packets sent on this path (also counted by sent_packets)

+
§lost_plpmtud_probes: u64

The amount of PLPMTUD probe packets lost on this path (ignored by lost_packets and +lost_bytes)

+
§black_holes_detected: u64

The number of times a black hole was detected in the path

+
§current_mtu: u16

Largest UDP payload size the path currently supports

+

Trait Implementations§

Source§

impl Clone for PathStats

Source§

fn clone(&self) -> PathStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PathStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PathStats

Source§

fn default() -> PathStats

Returns the “default value” for a type. Read more
Source§

impl PartialEq for PathStats

Source§

fn eq(&self, other: &PathStats) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for PathStats

Source§

impl Eq for PathStats

Source§

impl StructuralPartialEq for PathStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html b/pr/234/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html new file mode 100644 index 000000000..3bf510c25 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html @@ -0,0 +1,31 @@ +ProtectedInitialHeader in iroh_quinn_proto - Rust

ProtectedInitialHeader

Struct ProtectedInitialHeader 

Source
pub struct ProtectedInitialHeader {
+    pub dst_cid: ConnectionId,
+    pub src_cid: ConnectionId,
+    pub token_pos: Range<usize>,
+    pub len: u64,
+    pub version: u32,
+}
Expand description

Header of an Initial packet, before decryption

+

Fields§

§dst_cid: ConnectionId

Destination Connection ID

+
§src_cid: ConnectionId

Source Connection ID

+
§token_pos: Range<usize>

The position of a token in the packet buffer

+
§len: u64

Length of the packet payload

+
§version: u32

QUIC version

+

Trait Implementations§

Source§

impl Clone for ProtectedInitialHeader

Source§

fn clone(&self) -> ProtectedInitialHeader

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ProtectedInitialHeader

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.QlogConfig.html b/pr/234/docs/iroh_quinn_proto/struct.QlogConfig.html new file mode 100644 index 000000000..28f3a95c4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.QlogConfig.html @@ -0,0 +1,27 @@ +QlogConfig in iroh_quinn_proto - Rust

QlogConfig

Struct QlogConfig 

Source
pub struct QlogConfig { /* private fields */ }
Expand description

Configuration for qlog trace logging.

+

This struct is returned from QlogFactory::for_connection if qlog logging should +be enabled for a connection. It allows to set metadata for the qlog trace.

+

The trace will be written to the provided writer in the [JSON-SEQ format] defined in the qlog spec.

+

JSON-SEQ format

+

Implementations§

Source§

impl QlogConfig

Source

pub fn new(writer: Box<dyn Write + Send + Sync>) -> Self

Creates a new QlogConfig that writes a qlog trace to the specified writer.

+
Source

pub fn title(&mut self, title: Option<String>) -> &mut Self

Title to record in the qlog capture

+
Source

pub fn description(&mut self, description: Option<String>) -> &mut Self

Description to record in the qlog capture

+
Source

pub fn start_time(&mut self, start_time: Instant) -> &mut Self

Epoch qlog event times are recorded relative to

+

If unset, the start of the connection is used.

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.QlogFileFactory.html b/pr/234/docs/iroh_quinn_proto/struct.QlogFileFactory.html new file mode 100644 index 000000000..ee8685ae3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.QlogFileFactory.html @@ -0,0 +1,32 @@ +QlogFileFactory in iroh_quinn_proto - Rust

QlogFileFactory

Struct QlogFileFactory 

Source
pub struct QlogFileFactory { /* private fields */ }
Expand description

Enables writing qlog traces to a directory.

+

Implementations§

Source§

impl QlogFileFactory

Source

pub fn new(dir: PathBuf) -> Self

Creates a new qlog factory that writes files into the specified directory.

+
Source

pub fn from_env() -> Self

Creates a new qlog factory that writes files into QLOGDIR, if set.

+

If the environment variable QLOGDIR is set, qlog traces for all connections handled +by this endpoint will be written into that directory. +If the directory doesn’t exist it will be created.

+
Source

pub fn with_prefix(self, prefix: impl ToString) -> Self

Sets a prefix to the filename of the generated files.

+
Source

pub fn with_start_instant(self, start: Instant) -> Self

Override the instant relative to which all events are recorded.

+

If not set, events will be recorded relative to the start of the connection.

+

Trait Implementations§

Source§

impl Debug for QlogFileFactory

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl QlogFactory for QlogFileFactory

Source§

fn for_connection( + &self, + side: Side, + _remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html new file mode 100644 index 000000000..8b72ad8e0 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.RandomConnectionIdGenerator.html @@ -0,0 +1,26 @@ +RandomConnectionIdGenerator in iroh_quinn_proto - Rust

RandomConnectionIdGenerator

Struct RandomConnectionIdGenerator 

Source
pub struct RandomConnectionIdGenerator { /* private fields */ }
Expand description

Generates purely random connection IDs of a specified length

+

Random CIDs can be smaller than those produced by HashedConnectionIdGenerator, but cannot be +usefully validated.

+

Implementations§

Source§

impl RandomConnectionIdGenerator

Source

pub fn new(cid_len: usize) -> Self

Initialize Random CID generator with a fixed CID length

+

The given length must be less than or equal to MAX_CID_SIZE.

+
Source

pub fn set_lifetime(&mut self, d: Duration) -> &mut Self

Set the lifetime of CIDs created by this generator

+

Trait Implementations§

Source§

impl Clone for RandomConnectionIdGenerator

Source§

fn clone(&self) -> RandomConnectionIdGenerator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl ConnectionIdGenerator for RandomConnectionIdGenerator

Source§

fn cid_len(&self) -> usize

Provide the length of dst_cid in short header packet

+
Source§

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID Read more
Source§

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs Read more
Source§

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator Read more
Source§

impl Debug for RandomConnectionIdGenerator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RandomConnectionIdGenerator

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl Copy for RandomConnectionIdGenerator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.RecvStream.html b/pr/234/docs/iroh_quinn_proto/struct.RecvStream.html new file mode 100644 index 000000000..0cbbd53aa --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.RecvStream.html @@ -0,0 +1,37 @@ +RecvStream in iroh_quinn_proto - Rust

RecvStream

Struct RecvStream 

Source
pub struct RecvStream<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl RecvStream<'_>

Source

pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError>

Read from the given recv stream

+

max_length limits the maximum size of the returned Bytes value; passing usize::MAX +will yield the best performance. ordered will make sure the returned chunk’s offset will +have an offset exactly equal to the previously returned offset plus the previously returned +bytes’ length.

+

Yields Ok(None) if the stream was finished. Otherwise, yields a segment of data and its +offset in the stream. If ordered is false, segments may be received in any order, and +the Chunk’s offset field can be used to determine ordering in the caller.

+

While most applications will prefer to consume stream data in order, unordered reads can +improve performance when packet loss occurs and data cannot be retransmitted before the flow +control window is filled. On any given stream, you can switch from ordered to unordered +reads, but ordered reads on streams that have seen previous unordered reads will return +ReadError::IllegalOrderedRead.

+
Source

pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Stop accepting data on the given receive stream

+

Discards unread data and notifies the peer to stop transmitting. Once stopped, further +attempts to operate on a stream will yield ClosedStream errors.

+
Source

pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream>

Check whether this stream has been reset by the peer, returning the reset error code if so

+

After returning Ok(Some(_)) once, stream state will be discarded and all future calls will +return Err(ClosedStream).

+

Auto Trait Implementations§

§

impl<'a> Freeze for RecvStream<'a>

§

impl<'a> RefUnwindSafe for RecvStream<'a>

§

impl<'a> Send for RecvStream<'a>

§

impl<'a> Sync for RecvStream<'a>

§

impl<'a> Unpin for RecvStream<'a>

§

impl<'a> !UnwindSafe for RecvStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.RetryError.html b/pr/234/docs/iroh_quinn_proto/struct.RetryError.html new file mode 100644 index 000000000..26e3fee38 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.RetryError.html @@ -0,0 +1,20 @@ +RetryError in iroh_quinn_proto - Rust

RetryError

Struct RetryError 

Source
pub struct RetryError(/* private fields */);
Expand description

Error for attempting to retry an Incoming which already bears a token from a previous retry

+

Implementations§

Trait Implementations§

Source§

impl Debug for RetryError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for RetryError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for RetryError

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> 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/234/docs/iroh_quinn_proto/struct.RttEstimator.html b/pr/234/docs/iroh_quinn_proto/struct.RttEstimator.html new file mode 100644 index 000000000..5d0b8a2ab --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.RttEstimator.html @@ -0,0 +1,25 @@ +RttEstimator in iroh_quinn_proto - Rust

RttEstimator

Struct RttEstimator 

Source
pub struct RttEstimator { /* private fields */ }
Expand description

RTT estimation for a particular network path

+

Implementations§

Source§

impl RttEstimator

Source

pub fn get(&self) -> Duration

The current best RTT estimation.

+
Source

pub fn conservative(&self) -> Duration

Conservative estimate of RTT

+

Takes the maximum of smoothed and latest RTT, as recommended +in 6.1.2 of the recovery spec (draft 29).

+
Source

pub fn min(&self) -> Duration

Minimum RTT registered so far for this estimator.

+

Trait Implementations§

Source§

impl Clone for RttEstimator

Source§

fn clone(&self) -> RttEstimator

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RttEstimator

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Copy for RttEstimator

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.SendStream.html b/pr/234/docs/iroh_quinn_proto/struct.SendStream.html new file mode 100644 index 000000000..b58367800 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.SendStream.html @@ -0,0 +1,46 @@ +SendStream in iroh_quinn_proto - Rust

SendStream

Struct SendStream 

Source
pub struct SendStream<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl<'a> SendStream<'a>

Source

pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError>

Send data on the given stream

+

Returns the number of bytes successfully written.

+
Source

pub fn write_chunks( + &mut self, + data: &mut [Bytes], +) -> Result<Written, WriteError>

Send data on the given stream

+

Returns the number of bytes and chunks successfully written. +Note that this method might also write a partial chunk. In this case +Written::chunks will not count this chunk as fully written. However +the chunk will be advanced and contain only non-written data after the call.

+
Source

pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream>

Check if this stream was stopped, get the reason if it was

+
Source

pub fn finish(&mut self) -> Result<(), FinishError>

Finish a send stream, signalling that no more data will be sent.

+

If this fails, no StreamEvent::Finished will be generated.

+
Source

pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream>

Abandon transmitting data on a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream>

Set the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+
Source

pub fn priority(&self) -> Result<i32, ClosedStream>

Get the priority of a stream

+
§Panics
+
    +
  • when applied to a receive stream
  • +
+

Auto Trait Implementations§

§

impl<'a> Freeze for SendStream<'a>

§

impl<'a> !RefUnwindSafe for SendStream<'a>

§

impl<'a> Send for SendStream<'a>

§

impl<'a> Sync for SendStream<'a>

§

impl<'a> Unpin for SendStream<'a>

§

impl<'a> !UnwindSafe for SendStream<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ServerConfig.html b/pr/234/docs/iroh_quinn_proto/struct.ServerConfig.html new file mode 100644 index 000000000..1022c4d13 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ServerConfig.html @@ -0,0 +1,95 @@ +ServerConfig in iroh_quinn_proto - Rust

ServerConfig

Struct ServerConfig 

Source
pub struct ServerConfig {
+    pub transport: Arc<TransportConfig>,
+    pub crypto: Arc<dyn ServerConfig>,
+    pub validation_token: ValidationTokenConfig,
+    /* private fields */
+}
Expand description

Parameters governing incoming connections

+

Default values should be suitable for most internet applications.

+

Fields§

§transport: Arc<TransportConfig>

Transport configuration to use for incoming connections

+
§crypto: Arc<dyn ServerConfig>

TLS configuration used for incoming connections

+

Must be set to use TLS 1.3 only.

+
§validation_token: ValidationTokenConfig

Configuration for sending and handling validation tokens

+

Implementations§

Source§

impl ServerConfig

Source

pub fn new( + crypto: Arc<dyn ServerConfig>, + token_key: Arc<dyn HandshakeTokenKey>, +) -> Self

Create a default config with a particular handshake token key

+
Source

pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self

Set a custom TransportConfig

+
Source

pub fn validation_token_config( + &mut self, + validation_token: ValidationTokenConfig, +) -> &mut Self

Set a custom ValidationTokenConfig

+
Source

pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self

Private key used to authenticate data included in handshake tokens

+
Source

pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self

Duration after a retry token was issued for which it’s considered valid

+

Defaults to 15 seconds.

+
Source

pub fn migration(&mut self, value: bool) -> &mut Self

Whether to allow clients to migrate to new addresses

+

Improves behavior for clients that move between different internet connections or suffer NAT +rebinding. Enabled by default.

+
Source

pub fn preferred_address_v4( + &mut self, + address: Option<SocketAddrV4>, +) -> &mut Self

The preferred IPv4 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn preferred_address_v6( + &mut self, + address: Option<SocketAddrV6>, +) -> &mut Self

The preferred IPv6 address that will be communicated to clients during handshaking

+

If the client is able to reach this address, it will switch to it.

+
Source

pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self

Maximum number of Incoming to allow to exist at a time

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. While this limit is reached, new incoming connection attempts are immediately +refused. Larger values have greater worst-case memory consumption, but accommodate greater +application latency in handling incoming connection attempts.

+

The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits +memory consumption from this to under 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self

Maximum number of received bytes to buffer for each Incoming

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 10 MiB–an amount such that in most situations a client would +not transmit that much 0-RTT data faster than the server handles the corresponding +Incoming.

+
Source

pub fn incoming_buffer_size_total( + &mut self, + incoming_buffer_size_total: u64, +) -> &mut Self

Maximum number of received bytes to buffer for all Incoming +collectively

+

An Incoming comes into existence when an incoming connection attempt +is received and stops existing when the application either accepts it or otherwise disposes +of it. This limit governs only packets received within that period, and does not include +the first packet. Packets received in excess of this limit are dropped, which may cause +0-RTT or handshake data to have to be retransmitted.

+

The default value is set to 100 MiB–a generous amount that still prevents memory +exhaustion in most contexts.

+
Source

pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Defaults to StdSystemTime, which simply calls SystemTime::now().

+
Source§

impl ServerConfig

Source

pub fn with_single_cert( + cert_chain: Vec<CertificateDer<'static>>, + key: PrivateKeyDer<'static>, +) -> Result<Self, Error>

Create a server config with the given certificate chain to be presented to clients

+

Uses a randomized handshake token key.

+
Source§

impl ServerConfig

Source

pub fn with_crypto(crypto: Arc<dyn ServerConfig>) -> Self

Create a server config with the given crypto::ServerConfig

+

Uses a randomized handshake token key.

+

Trait Implementations§

Source§

impl Clone for ServerConfig

Source§

fn clone(&self) -> ServerConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ServerConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ShouldTransmit.html b/pr/234/docs/iroh_quinn_proto/struct.ShouldTransmit.html new file mode 100644 index 000000000..4da361499 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ShouldTransmit.html @@ -0,0 +1,28 @@ +ShouldTransmit in iroh_quinn_proto - Rust

ShouldTransmit

Struct ShouldTransmit 

Source
pub struct ShouldTransmit(/* private fields */);
Expand description

Indicates whether a frame needs to be transmitted

+

This type wraps around bool and uses the #[must_use] attribute in order +to prevent accidental loss of the frame transmission requirement.

+

Implementations§

Source§

impl ShouldTransmit

Source

pub fn should_transmit(self) -> bool

Returns whether a frame should be transmitted

+

Trait Implementations§

Source§

impl Clone for ShouldTransmit

Source§

fn clone(&self) -> ShouldTransmit

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ShouldTransmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ShouldTransmit

Source§

fn default() -> ShouldTransmit

Returns the “default value” for a type. Read more
Source§

impl PartialEq for ShouldTransmit

Source§

fn eq(&self, other: &ShouldTransmit) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for ShouldTransmit

Source§

impl Eq for ShouldTransmit

Source§

impl StructuralPartialEq for ShouldTransmit

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.StdSystemTime.html b/pr/234/docs/iroh_quinn_proto/struct.StdSystemTime.html new file mode 100644 index 000000000..9c5f156dd --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.StdSystemTime.html @@ -0,0 +1,19 @@ +StdSystemTime in iroh_quinn_proto - Rust

StdSystemTime

Struct StdSystemTime 

Source
pub struct StdSystemTime;
Expand description

Default implementation of TimeSource

+

Implements now by calling SystemTime::now().

+

Trait Implementations§

Source§

impl TimeSource for StdSystemTime

Source§

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.StreamId.html b/pr/234/docs/iroh_quinn_proto/struct.StreamId.html new file mode 100644 index 000000000..4e39f687d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.StreamId.html @@ -0,0 +1,45 @@ +StreamId in iroh_quinn_proto - Rust

StreamId

Struct StreamId 

Source
pub struct StreamId(/* private fields */);
Expand description

Identifier for a stream within a particular connection

+

Implementations§

Source§

impl StreamId

Source

pub fn new(initiator: Side, dir: Dir, index: u64) -> Self

Create a new StreamId

+
Source

pub fn initiator(self) -> Side

Which side of a connection initiated the stream

+
Source

pub fn dir(self) -> Dir

Which directions data flows in

+
Source

pub fn index(self) -> u64

Distinguishes streams of the same initiator and directionality

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for StreamId

Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
Source§

fn arbitrary_take_rest(u: Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
Source§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for StreamId

Source§

fn clone(&self) -> StreamId

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer
Source§

impl Debug for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for StreamId

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.Streams.html b/pr/234/docs/iroh_quinn_proto/struct.Streams.html new file mode 100644 index 000000000..2c7dad070 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Streams.html @@ -0,0 +1,28 @@ +Streams in iroh_quinn_proto - Rust

Streams

Struct Streams 

Source
pub struct Streams<'a> { /* private fields */ }
Expand description

Access to streams

+

Implementations§

Source§

impl<'a> Streams<'a>

Source

pub fn open(&mut self, dir: Dir) -> Option<StreamId>

Open a single stream if possible

+

Returns None if the streams in the given direction are currently exhausted.

+
Source

pub fn accept(&mut self, dir: Dir) -> Option<StreamId>

Accept a remotely initiated stream of a certain directionality, if possible

+

Returns None if there are no new incoming streams for this connection. +Has no impact on the data flow-control or stream concurrency limits.

+
Source

pub fn send_streams(&self) -> usize

The number of streams that may have unacknowledged data.

+
Source

pub fn remote_open_streams(&self, dir: Dir) -> u64

The number of remotely initiated open streams of a certain directionality.

+

Includes remotely initiated streams, which have not been accepted via accept. +These streams count against the respective concurrency limit reported by +Connection::max_concurrent_streams.

+

Auto Trait Implementations§

§

impl<'a> Freeze for Streams<'a>

§

impl<'a> !RefUnwindSafe for Streams<'a>

§

impl<'a> Send for Streams<'a>

§

impl<'a> Sync for Streams<'a>

§

impl<'a> Unpin for Streams<'a>

§

impl<'a> !UnwindSafe for Streams<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.TokenMemoryCache.html b/pr/234/docs/iroh_quinn_proto/struct.TokenMemoryCache.html new file mode 100644 index 000000000..123c38fc0 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.TokenMemoryCache.html @@ -0,0 +1,21 @@ +TokenMemoryCache in iroh_quinn_proto - Rust

TokenMemoryCache

Struct TokenMemoryCache 

Source
pub struct TokenMemoryCache(/* private fields */);
Expand description

TokenStore implementation that stores up to N tokens per server name for up to a +limited number of server names, in-memory

+

Implementations§

Source§

impl TokenMemoryCache

Source

pub fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self

Construct empty

+

Trait Implementations§

Source§

impl Debug for TokenMemoryCache

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TokenMemoryCache

Defaults to a maximum of 256 servers and 2 tokens per server

+
Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl TokenStore for TokenMemoryCache

Source§

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use Read more
Source§

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.TokenReuseError.html b/pr/234/docs/iroh_quinn_proto/struct.TokenReuseError.html new file mode 100644 index 000000000..9ba9767a1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.TokenReuseError.html @@ -0,0 +1,18 @@ +TokenReuseError in iroh_quinn_proto - Rust

TokenReuseError

Struct TokenReuseError 

Source
pub struct TokenReuseError;
Expand description

Error for when a validation token may have been reused

+

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.Transmit.html b/pr/234/docs/iroh_quinn_proto/struct.Transmit.html new file mode 100644 index 000000000..50adf5e56 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Transmit.html @@ -0,0 +1,30 @@ +Transmit in iroh_quinn_proto - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub size: usize,
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§size: usize

Amount of data written to the caller-supplied buffer

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl Debug for Transmit

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.TransportConfig.html b/pr/234/docs/iroh_quinn_proto/struct.TransportConfig.html new file mode 100644 index 000000000..863901208 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.TransportConfig.html @@ -0,0 +1,215 @@ +TransportConfig in iroh_quinn_proto - Rust

TransportConfig

Struct TransportConfig 

Source
pub struct TransportConfig { /* private fields */ }
Expand description

Parameters governing the core QUIC state machine

+

Default values should be suitable for most internet applications. Applications protocols which +forbid remotely-initiated streams should set max_concurrent_bidi_streams and +max_concurrent_uni_streams to zero.

+

In some cases, performance or resource requirements can be improved by tuning these values to +suit a particular application and/or network connection. In particular, data window sizes can be +tuned for a particular expected round trip time, link capacity, and memory availability. Tuning +for higher bandwidths and latencies increases worst-case memory consumption, but does not impair +performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps +link with a 100ms round trip time.

+

Implementations§

Source§

impl TransportConfig

Source

pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self

Maximum number of incoming bidirectional streams that may be open concurrently

+

Must be nonzero for the peer to open any bidirectional streams.

+

Worst-case memory use is directly proportional to max_concurrent_bidi_streams * stream_receive_window, with an upper bound proportional to receive_window.

+
Source

pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self

Variant of max_concurrent_bidi_streams affecting unidirectional streams

+
Source

pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self

Maximum duration of inactivity to accept before timing out the connection.

+

The true idle timeout is the minimum of this and the peer’s own max idle timeout. None +represents an infinite timeout. Defaults to 30 seconds.

+

WARNING: If a peer or its network path malfunctions or acts maliciously, an infinite +idle timeout can result in permanently hung futures!

+ +
let mut config = TransportConfig::default();
+
+// Set the idle timeout as `VarInt`-encoded milliseconds
+config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+
+// Set the idle timeout as a `Duration`
+config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
Source

pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self

Maximum number of bytes the peer may transmit without acknowledgement on any one stream +before becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Setting this smaller than receive_window helps ensure that a single +stream doesn’t monopolize receive buffers, which may otherwise occur if the application +chooses not to read from a large stream for a time while still requiring data on other +streams.

+
Source

pub fn receive_window(&mut self, value: VarInt) -> &mut Self

Maximum number of bytes the peer may transmit across all streams of a connection before +becoming blocked.

+

This should be set to at least the expected connection latency multiplied by the maximum +desired throughput. Larger values can be useful to allow maximum throughput within a +stream while another is blocked.

+
Source

pub fn send_window(&mut self, value: u64) -> &mut Self

Maximum number of bytes to transmit to a peer without acknowledgment

+

Provides an upper bound on memory when communicating with peers that issue large amounts of +flow control credit. Endpoints that wish to handle large numbers of connections robustly +should take care to set this low enough to guarantee memory exhaustion does not occur if +every connection uses the entire window.

+
Source

pub fn send_fairness(&mut self, value: bool) -> &mut Self

Whether to implement fair queuing for send streams having the same priority.

+

When enabled, connections schedule data from outgoing streams having the same priority in a +round-robin fashion. When disabled, streams are scheduled in the order they are written to.

+

Note that this only affects streams with the same priority. Higher priority streams always +take precedence over lower priority streams.

+

Disabling fairness can reduce fragmentation and protocol overhead for workloads that use +many small streams.

+
Source

pub fn packet_threshold(&mut self, value: u32) -> &mut Self

Maximum reordering in packet number space before FACK style loss detection considers a +packet lost. Should not be less than 3, per RFC5681.

+
Source

pub fn time_threshold(&mut self, value: f32) -> &mut Self

Maximum reordering in time space before time based loss detection considers a packet lost, +as a factor of RTT

+
Source

pub fn initial_rtt(&mut self, value: Duration) -> &mut Self

The RTT used before an RTT sample is taken

+
Source

pub fn initial_mtu(&mut self, value: u16) -> &mut Self

The initial value to be used as the maximum UDP payload size before running MTU discovery +(see TransportConfig::mtu_discovery_config).

+

Must be at least 1200, which is the default, and known to be safe for typical internet +applications. Larger values are more efficient, but increase the risk of packet loss due to +exceeding the network path’s IP MTU. If the provided value is higher than what the network +path actually supports, packet loss will eventually trigger black hole detection and bring +it down to TransportConfig::min_mtu.

+
Source

pub fn min_mtu(&mut self, value: u16) -> &mut Self

The maximum UDP payload size guaranteed to be supported by the network.

+

Must be at least 1200, which is the default, and lower than or equal to +TransportConfig::initial_mtu.

+

Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links +outside of either endpoint’s control. Extreme care should be used when raising this value +outside of private networks where these factors are fully controlled. If the provided value +is higher than what the network path actually supports, the result will be unpredictable and +catastrophic packet loss, without a possibility of repair. Prefer +TransportConfig::initial_mtu together with +TransportConfig::mtu_discovery_config to set a maximum UDP payload size that robustly +adapts to the network.

+
Source

pub fn mtu_discovery_config( + &mut self, + value: Option<MtuDiscoveryConfig>, +) -> &mut Self

Specifies the MTU discovery config (see MtuDiscoveryConfig for details).

+

Enabled by default.

+
Source

pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self

Pad UDP datagrams carrying application data to current maximum UDP payload size

+

Disabled by default. UDP datagrams containing loss probes are exempt from padding.

+

Enabling this helps mitigate traffic analysis by network observers, but it increases +bandwidth usage. Without this mitigation precise plain text size of application datagrams as +well as the total size of stream write bursts can be inferred by observers under certain +conditions. This analysis requires either an uncongested connection or application datagrams +too large to be coalesced.

+
Source

pub fn ack_frequency_config( + &mut self, + value: Option<AckFrequencyConfig>, +) -> &mut Self

Specifies the ACK frequency config (see AckFrequencyConfig for details)

+

The provided configuration will be ignored if the peer does not support the acknowledgement +frequency QUIC extension.

+

Defaults to None, which disables controlling the peer’s acknowledgement frequency. Even +if set to None, the local side still supports the acknowledgement frequency QUIC +extension and may use it in other ways.

+
Source

pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self

Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.

+
Source

pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self

Period of inactivity before sending a keep-alive packet

+

Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.

+

None to disable, which is the default. Only one side of any given connection needs keep-alive +enabled for the connection to be preserved. Must be set lower than the idle_timeout of both +peers to be effective.

+
Source

pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self

Maximum quantity of out-of-order crypto layer data to buffer

+
Source

pub fn allow_spin(&mut self, value: bool) -> &mut Self

Whether the implementation is permitted to set the spin bit on this connection

+

This allows passive observers to easily judge the round trip time of a connection, which can +be useful for network administration but sacrifices a small amount of privacy.

+
Source

pub fn datagram_receive_buffer_size( + &mut self, + value: Option<usize>, +) -> &mut Self

Maximum number of incoming application datagram bytes to buffer, or None to disable +incoming datagrams

+

The peer is forbidden to send single datagrams larger than this size. If the aggregate size +of all datagrams that have been received from the peer but not consumed by the application +exceeds this value, old datagrams are dropped until it is no longer exceeded.

+
Source

pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self

Maximum number of outgoing application datagram bytes to buffer

+

While datagrams are sent ASAP, it is possible for an application to generate data faster +than the link, or even the underlying hardware, can transmit them. This limits the amount of +memory that may be consumed in that case. When the send buffer is full and a new datagram is +sent, older datagrams are dropped until sufficient space is available.

+
Source

pub fn congestion_controller_factory( + &mut self, + factory: Arc<dyn ControllerFactory + Send + Sync + 'static>, +) -> &mut Self

How to construct new congestion::Controllers

+

Typically the refcounted configuration of a congestion::Controller, +e.g. a congestion::NewRenoConfig.

+
§Example
+
let mut config = TransportConfig::default();
+config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
Source

pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self

Whether to use “Generic Segmentation Offload” to accelerate transmits, when supported by the +environment

+

Defaults to true.

+

GSO dramatically reduces CPU consumption when sending large numbers of packets with the same +headers, such as when transmitting bulk data on a connection. However, it is not supported +by all network interface drivers or packet inspection tools. quinn-udp will attempt to +disable GSO automatically when unavailable, but this can lead to spurious packet loss at +startup, temporarily degrading performance.

+
Source

pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self

Whether to send observed address reports to peers.

+

This will aid peers in inferring their reachable address, which in most NATd networks +will not be easily available to them.

+
Source

pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self

Whether to receive observed address reports from other peers.

+

Peers with the address discovery extension enabled that are willing to provide observed +address reports will do so if this transport parameter is set. In general, observed address +reports cannot be trusted. This, however, can aid the current endpoint in inferring its +reachable address, which in most NATd networks will not be easily available.

+
Source

pub fn max_concurrent_multipath_paths( + &mut self, + max_concurrent: u32, +) -> &mut Self

Enables the Multipath Extension for QUIC.

+

Setting this to any nonzero value will enable the Multipath Extension for QUIC, +https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/.

+

The value provided specifies the number maximum number of paths this endpoint may open +concurrently when multipath is negotiated. For any path to be opened, the remote must +enable multipath as well.

+
Source

pub fn default_path_max_idle_timeout( + &mut self, + timeout: Option<Duration>, +) -> &mut Self

Sets a default per-path maximum idle timeout

+

If the path is idle for this long the path will be abandoned. Bear in mind this will +interact with the TransportConfig::max_idle_timeout, if the last path is +abandoned the entire connection will be closed.

+

You can also change this using Connection::set_path_max_idle_timeout for +existing paths.

+
Source

pub fn default_path_keep_alive_interval( + &mut self, + interval: Option<Duration>, +) -> &mut Self

Sets a default per-path keep alive interval

+

Note that this does not interact with the connection-wide +TransportConfig::keep_alive_interval. This setting will keep this path active, +TransportConfig::keep_alive_interval will keep the connection active, with no +control over which path is used for this.

+

You can also change this using Connection::set_path_keep_alive_interval for +existing path.

+
Source

pub fn set_max_remote_nat_traversal_addresses( + &mut self, + max_addresses: u8, +) -> &mut Self

Sets the maximum number of nat traversal addresses this endpoint allows the remote to +advertise

+

Setting this to any nonzero value will enable Iroh’s holepunching, loosely based in the Nat +Traversal Extension for QUIC, see +https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html

+

This implementation expects the multipath extension to be enabled as well. if not yet +enabled via Self::max_concurrent_multipath_paths, a default value of +DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED will be used.

+
Source

pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self

Configures qlog capturing by setting a QlogFactory.

+

This assigns a QlogFactory that produces qlog capture configurations for +individual connections.

+
Source

pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self

Configures qlog capturing through the QLOGDIR environment variable.

+

This uses QlogFileFactory::from_env to create a factory to write qlog traces +into the directory set through the QLOGDIR environment variable.

+

If QLOGDIR is not set, no traces will be written. If QLOGDIR is set to a path +that does not exist, it will be created.

+

The files will be prefixed with prefix.

+
Source

pub fn qlog_from_path( + &mut self, + path: impl AsRef<Path>, + prefix: &str, +) -> &mut Self

Configures qlog capturing into a directory.

+

This uses QlogFileFactory to create a factory to write qlog traces into +the specified directory. The files will be prefixed with prefix.

+

Trait Implementations§

Source§

impl Clone for TransportConfig

Source§

fn clone(&self) -> TransportConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for TransportConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.TransportError.html b/pr/234/docs/iroh_quinn_proto/struct.TransportError.html new file mode 100644 index 000000000..d9a34b060 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.TransportError.html @@ -0,0 +1,38 @@ +TransportError in iroh_quinn_proto - Rust

TransportError

Struct TransportError 

Source
#[non_exhaustive]
pub struct TransportError { + pub code: Code, + pub frame: Option<FrameType>, + 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: Option<FrameType>

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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.TransportErrorCode.html b/pr/234/docs/iroh_quinn_proto/struct.TransportErrorCode.html new file mode 100644 index 000000000..4124cc1a6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.TransportErrorCode.html @@ -0,0 +1,48 @@ +TransportErrorCode in iroh_quinn_proto - Rust

TransportErrorCode

Struct TransportErrorCode 

Source
pub struct TransportErrorCode(/* private fields */);
Expand description

Transport-level error code

+

Implementations§

Source§

impl Code

Source

pub fn crypto(code: u8) -> Self

Create QUIC error code from TLS alert code

+
Source§

impl Code

Source

pub const NO_ERROR: Self

the connection is being closed abruptly in the absence of any error

+
Source

pub const INTERNAL_ERROR: Self

the endpoint encountered an internal error and cannot continue with the connection

+
Source

pub const CONNECTION_REFUSED: Self

the server refused to accept a new connection

+
Source

pub const FLOW_CONTROL_ERROR: Self

received more data than permitted in advertised data limits

+
Source

pub const STREAM_LIMIT_ERROR: Self

received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type

+
Source

pub const STREAM_STATE_ERROR: Self

received a frame for a stream that was not in a state that permitted that frame

+
Source

pub const FINAL_SIZE_ERROR: Self

received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established

+
Source

pub const FRAME_ENCODING_ERROR: Self

received a frame that was badly formatted

+
Source

pub const TRANSPORT_PARAMETER_ERROR: Self

received transport parameters that were badly formatted, included an invalid value, was absent even though it is mandatory, was present though it is forbidden, or is otherwise in error

+
Source

pub const CONNECTION_ID_LIMIT_ERROR: Self

the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit

+
Source

pub const PROTOCOL_VIOLATION: Self

detected an error with protocol compliance that was not covered by more specific error codes

+
Source

pub const INVALID_TOKEN: Self

received an invalid Retry Token in a client Initial

+
Source

pub const APPLICATION_ERROR: Self

the application or application protocol caused the connection to be closed during the handshake

+
Source

pub const CRYPTO_BUFFER_EXCEEDED: Self

received more data in CRYPTO frames than can be buffered

+
Source

pub const KEY_UPDATE_ERROR: Self

key update error

+
Source

pub const AEAD_LIMIT_REACHED: Self

the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm

+
Source

pub const NO_VIABLE_PATH: Self

no viable network path exists

+
Source

pub const APPLICATION_ABANDON_PATH: Self

Path abandoned at the application’s request

+
Source

pub const PATH_RESOURCE_LIMIT_REACHED: Self

Path abandoned due to resource limitations in the transport

+
Source

pub const PATH_UNSTABLE_OR_POOR: Self

Path abandoned due to unstable interfaces

+
Source

pub const NO_CID_AVAILABLE_FOR_PATH: Self

Path abandoned due to no available connection IDs for the path

+

Trait Implementations§

Source§

impl Clone for Code

Source§

fn clone(&self) -> Code

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer
Source§

impl Debug for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Code

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.UdpStats.html b/pr/234/docs/iroh_quinn_proto/struct.UdpStats.html new file mode 100644 index 000000000..a3aeb94de --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.UdpStats.html @@ -0,0 +1,29 @@ +UdpStats in iroh_quinn_proto - Rust

UdpStats

Struct UdpStats 

Source
#[non_exhaustive]
pub struct UdpStats { + pub datagrams: u64, + pub bytes: u64, + pub ios: u64, +}
Expand description

Statistics about UDP datagrams transmitted or received on a connection

+

All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§datagrams: u64

The amount of UDP datagrams observed

+
§bytes: u64

The total amount of bytes which have been transferred inside UDP datagrams

+
§ios: u64

The amount of I/O operations executed

+

Can be less than datagrams when GSO, GRO, and/or batched system calls are in use.

+

Trait Implementations§

Source§

impl Clone for UdpStats

Source§

fn clone(&self) -> UdpStats

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for UdpStats

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for UdpStats

Source§

fn default() -> UdpStats

Returns the “default value” for a type. Read more
Source§

impl Copy for UdpStats

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html b/pr/234/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html new file mode 100644 index 000000000..8e9acc49a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.ValidationTokenConfig.html @@ -0,0 +1,67 @@ +ValidationTokenConfig in iroh_quinn_proto - Rust

ValidationTokenConfig

Struct ValidationTokenConfig 

Source
pub struct ValidationTokenConfig { /* private fields */ }
Expand description

Configuration for sending and handling validation tokens in incoming connections

+

Default values should be suitable for most internet applications.

+

§QUIC Tokens

+

The QUIC protocol defines a concept of “address validation”. Essentially, one side of a +QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address, +but it will only consider that remote address “validated” once it has convincing evidence that +the address is not being spoofed.

+

Validation is important primarily because of QUIC’s “anti-amplification limit.” This limit +prevents a QUIC server from sending a client more than three times the number of bytes it has +received from the client on a given address until that address is validated. This is designed +to mitigate the ability of attackers to use QUIC-based servers as reflectors in amplification +attacks.

+

A path may become validated in several ways. The server is always considered validated by the +client. The client usually begins in an unvalidated state upon first connecting or migrating, +but then becomes validated through various mechanisms that usually take one network round trip. +However, in some cases, a client which has previously attempted to connect to a server may have +been given a one-time use cryptographically secured “token” that it can send in a subsequent +connection attempt to be validated immediately.

+

There are two ways these tokens can originate:

+
    +
  • If the server responds to an incoming connection with retry, a “retry token” is minted and +sent to the client, which the client immediately uses to attempt to connect again. Retry +tokens operate on short timescales, such as 15 seconds.
  • +
  • If a client’s path within an active connection is validated, the server may send the client +one or more “validation tokens,” which the client may store for use in later connections to +the same server. Validation tokens may be valid for much longer lifetimes than retry token.
  • +
+

The usage of validation tokens is most impactful in situations where 0-RTT data is also being +used–in particular, in situations where the server sends the client more than three times more +0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection +handshake implicitly causes the client’s address to be validated, transmission of 0.5-RTT data +is the main situation where a server might be sending application data to an address that could +be validated by token usage earlier than it would become validated without token usage.

+

These tokens should not be confused with “stateless reset tokens,” which are similarly named +but entirely unrelated.

+

Implementations§

Source§

impl ValidationTokenConfig

Source

pub fn lifetime(&mut self, value: Duration) -> &mut Self

Duration after an address validation token was issued for which it’s considered valid

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

Defaults to 2 weeks.

+
Source

pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self

Set a custom TokenLog

+

If the bloom feature is enabled (which it is by default), defaults to a default +BloomTokenLog, which is suitable for most internet applications.

+

If the bloom feature is disabled, defaults to NoneTokenLog, +which makes the server ignore all address validation tokens (that is, tokens originating +from NEW_TOKEN frames–retry tokens are not affected).

+
Source

pub fn sent(&mut self, value: u32) -> &mut Self

Number of address validation tokens sent to a client when its path is validated

+

This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.

+

If the bloom feature is enabled (which it is by default), defaults to 2. Otherwise, +defaults to 0.

+

Trait Implementations§

Source§

impl Clone for ValidationTokenConfig

Source§

fn clone(&self) -> ValidationTokenConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ValidationTokenConfig

Source§

fn fmt(&self, fmt: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ValidationTokenConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/struct.VarInt.html b/pr/234/docs/iroh_quinn_proto/struct.VarInt.html new file mode 100644 index 000000000..72bf30216 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.VarInt.html @@ -0,0 +1,55 @@ +VarInt in iroh_quinn_proto - Rust

VarInt

Struct VarInt 

Source
pub struct VarInt(/* private fields */);
Expand description

An integer less than 2^62

+

Values of this type are suitable for encoding as QUIC variable-length integer.

+

Implementations§

Source§

impl VarInt

Source

pub const MAX: Self

The largest representable value

+
Source

pub const MAX_SIZE: usize = 8usize

The largest encoded value length

+
Source

pub const fn from_u32(x: u32) -> Self

Construct a VarInt infallibly

+
Source

pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded>

Succeeds iff x < 2^62

+
Source

pub const unsafe fn from_u64_unchecked(x: u64) -> Self

Create a VarInt without ensuring it’s in range

+
§Safety
+

x must be less than 2^62.

+
Source

pub const fn into_inner(self) -> u64

Extract the integer value

+
Source

pub fn saturating_add(self, rhs: impl Into<Self>) -> Self

Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead +of overflowing.

+

Trait Implementations§

Source§

impl<'arbitrary> Arbitrary<'arbitrary> for VarInt

Available on crate feature arbitrary only.
Source§

fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given +unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
§

fn try_size_hint( + depth: usize, +) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type +needs to construct itself. Read more
Source§

impl Clone for VarInt

Source§

fn clone(&self) -> VarInt

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Codec 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§

fn encode<B: BufMut>(&self, w: &mut B)

Append the encoding of self to the provided buffer
Source§

impl Debug for VarInt

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
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 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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html b/pr/234/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..e7a2f7c7c --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.VarIntBoundsExceeded.html @@ -0,0 +1,26 @@ +VarIntBoundsExceeded in iroh_quinn_proto - Rust

VarIntBoundsExceeded

Struct VarIntBoundsExceeded 

Source
pub struct VarIntBoundsExceeded;
Expand description

Error returned when constructing a VarInt from a value >= 2^62

+

Trait Implementations§

Source§

impl Clone for VarIntBoundsExceeded

Source§

fn clone(&self) -> VarIntBoundsExceeded

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for VarIntBoundsExceeded

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for VarIntBoundsExceeded

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for VarIntBoundsExceeded

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<VarIntBoundsExceeded> for ConfigError

Source§

fn from(_: VarIntBoundsExceeded) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for VarIntBoundsExceeded

Source§

fn eq(&self, other: &VarIntBoundsExceeded) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for VarIntBoundsExceeded

Source§

impl Eq for VarIntBoundsExceeded

Source§

impl StructuralPartialEq for VarIntBoundsExceeded

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/struct.Written.html b/pr/234/docs/iroh_quinn_proto/struct.Written.html new file mode 100644 index 000000000..0e151c465 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/struct.Written.html @@ -0,0 +1,31 @@ +Written in iroh_quinn_proto - Rust

Written

Struct Written 

Source
pub struct Written {
+    pub bytes: usize,
+    pub chunks: usize,
+}
Expand description

Indicates how many bytes and chunks had been transferred in a write operation

+

Fields§

§bytes: usize

The amount of bytes which had been written

+
§chunks: usize

The amount of full chunks which had been written

+

If a chunk was only partially written, it will not be counted by this field.

+

Trait Implementations§

Source§

impl Clone for Written

Source§

fn clone(&self) -> Written

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Written

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Written

Source§

fn default() -> Written

Returns the “default value” for a type. Read more
Source§

impl PartialEq for Written

Source§

fn eq(&self, other: &Written) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Written

Source§

impl Eq for Written

Source§

impl StructuralPartialEq for Written

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html b/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html new file mode 100644 index 000000000..8b2bbfeeb --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.NoneTokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html b/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html new file mode 100644 index 000000000..db4ca3380 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token/struct.NoneTokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.NoneTokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token/struct.TokenReuseError.html b/pr/234/docs/iroh_quinn_proto/token/struct.TokenReuseError.html new file mode 100644 index 000000000..9439eab73 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token/struct.TokenReuseError.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TokenReuseError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token/trait.TokenLog.html b/pr/234/docs/iroh_quinn_proto/token/trait.TokenLog.html new file mode 100644 index 000000000..f296a9087 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token/trait.TokenLog.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TokenLog.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token/trait.TokenStore.html b/pr/234/docs/iroh_quinn_proto/token/trait.TokenStore.html new file mode 100644 index 000000000..a8e1acc4d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token/trait.TokenStore.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/trait.TokenStore.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html b/pr/234/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html new file mode 100644 index 000000000..339118bbf --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/token_memory_cache/struct.TokenMemoryCache.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TokenMemoryCache.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html b/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html new file mode 100644 index 000000000..12479fd74 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdGenerator.html @@ -0,0 +1,21 @@ +ConnectionIdGenerator in iroh_quinn_proto - Rust

ConnectionIdGenerator

Trait ConnectionIdGenerator 

Source
pub trait ConnectionIdGenerator: Send + Sync {
+    // Required methods
+    fn generate_cid(&mut self) -> ConnectionId;
+    fn cid_len(&self) -> usize;
+    fn cid_lifetime(&self) -> Option<Duration>;
+
+    // Provided method
+    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> { ... }
+}
Expand description

Generates connection IDs for incoming connections

+

Required Methods§

Source

fn generate_cid(&mut self) -> ConnectionId

Generates a new CID

+

Connection IDs MUST NOT contain any information that can be used by +an external observer (that is, one that does not cooperate with the +issuer) to correlate them with other connection IDs for the same +connection. They MUST have high entropy, e.g. due to encrypted data +or cryptographic-grade random data.

+
Source

fn cid_len(&self) -> usize

Returns the length of a CID for connections created by this generator

+
Source

fn cid_lifetime(&self) -> Option<Duration>

Returns the lifetime of generated Connection IDs

+

Connection IDs will be retired after the returned Duration, if any. Assumed to be constant.

+

Provided Methods§

Source

fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid>

Quickly determine whether cid could have been generated by this generator

+

False positives are permitted, but increase the cost of handling invalid packets.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdParser.html b/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdParser.html new file mode 100644 index 000000000..9503bd58a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.ConnectionIdParser.html @@ -0,0 +1,9 @@ +ConnectionIdParser in iroh_quinn_proto - Rust

ConnectionIdParser

Trait ConnectionIdParser 

Source
pub trait ConnectionIdParser {
+    // Required method
+    fn parse(
+        &self,
+        buf: &mut dyn Buf,
+    ) -> Result<ConnectionId, PacketDecodeError>;
+}
Expand description

Parse connection id in short header packet

+

Required Methods§

Source

fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>

Parse a connection id from given buffer

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.QlogFactory.html b/pr/234/docs/iroh_quinn_proto/trait.QlogFactory.html new file mode 100644 index 000000000..c8ea9697e --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.QlogFactory.html @@ -0,0 +1,23 @@ +QlogFactory in iroh_quinn_proto - Rust

QlogFactory

Trait QlogFactory 

Source
pub trait QlogFactory:
+    Send
+    + Sync
+    + 'static {
+    // Required method
+    fn for_connection(
+        &self,
+        side: Side,
+        remote: SocketAddr,
+        initial_dst_cid: ConnectionId,
+        now: Instant,
+    ) -> Option<QlogConfig>;
+}
Expand description

Constructs a QlogConfig for individual connections.

+

This is set via [TransportConfig::qlog_factory].

+

Required Methods§

Source

fn for_connection( + &self, + side: Side, + remote: SocketAddr, + initial_dst_cid: ConnectionId, + now: Instant, +) -> Option<QlogConfig>

Returns a QlogConfig for a connection, if logging should be enabled.

+

If None is returned, qlog capture is disabled for the connection.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.TimeSource.html b/pr/234/docs/iroh_quinn_proto/trait.TimeSource.html new file mode 100644 index 000000000..ad97fe27d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.TimeSource.html @@ -0,0 +1,7 @@ +TimeSource in iroh_quinn_proto - Rust

TimeSource

Trait TimeSource 

Source
pub trait TimeSource: Send + Sync {
+    // Required method
+    fn now(&self) -> SystemTime;
+}
Expand description

Object to get current SystemTime

+

This exists to allow system time to be mocked in tests, or wherever else desired.

+

Required Methods§

Source

fn now(&self) -> SystemTime

Get SystemTime::now() or the mocked equivalent

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.TokenLog.html b/pr/234/docs/iroh_quinn_proto/trait.TokenLog.html new file mode 100644 index 000000000..0a440a60d --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.TokenLog.html @@ -0,0 +1,45 @@ +TokenLog in iroh_quinn_proto - Rust

TokenLog

Trait TokenLog 

Source
pub trait TokenLog: Send + Sync {
+    // Required method
+    fn check_and_insert(
+        &self,
+        nonce: u128,
+        issued: SystemTime,
+        lifetime: Duration,
+    ) -> Result<(), TokenReuseError>;
+}
Expand description

Responsible for limiting clients’ ability to reuse validation tokens

+

RFC 9000 § 8.1.4:

+
+

Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect +against such attacks, servers MUST ensure that replay of tokens is prevented or limited. +Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time, +as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames +(Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times. +Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include +additional information about clients to further narrow applicability or reuse.

+
+

TokenLog pertains only to tokens provided in NEW_TOKEN frames.

+

Required Methods§

Source

fn check_and_insert( + &self, + nonce: u128, + issued: SystemTime, + lifetime: Duration, +) -> Result<(), TokenReuseError>

Record that the token was used and, ideally, return a token reuse error if the token may +have been already used previously

+

False negatives and false positives are both permissible. Called when a client uses an +address validation token.

+

Parameters:

+
    +
  • nonce: A server-generated random unique value for the token.
  • +
  • issued: The time the server issued the token.
  • +
  • lifetime: The expiration time of address validation tokens sent via NEW_TOKEN frames, +as configured by ServerValidationTokenConfig::lifetime.
  • +
+
§Security & Performance
+

To the extent that it is possible to repeatedly trigger false negatives (returning Ok for +a token which has been reused), an attacker could use the server to perform amplification +attacks. The QUIC specification requires that this be limited, if not prevented fully.

+

A false positive (returning Err for a token which has never been used) is not a security +vulnerability; it is permissible for a TokenLog to always return Err. A false positive +causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be +delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/trait.TokenStore.html b/pr/234/docs/iroh_quinn_proto/trait.TokenStore.html new file mode 100644 index 000000000..af8cf71ba --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/trait.TokenStore.html @@ -0,0 +1,13 @@ +TokenStore in iroh_quinn_proto - Rust

TokenStore

Trait TokenStore 

Source
pub trait TokenStore: Send + Sync {
+    // Required methods
+    fn insert(&self, server_name: &str, token: Bytes);
+    fn take(&self, server_name: &str) -> Option<Bytes>;
+}
Expand description

Responsible for storing validation tokens received from servers and retrieving them for use in +subsequent connections

+

Required Methods§

Source

fn insert(&self, server_name: &str, token: Bytes)

Potentially store a token for later one-time use

+

Called when a NEW_TOKEN frame is received from the server.

+
Source

fn take(&self, server_name: &str) -> Option<Bytes>

Try to find and take a token that was stored with the given server name

+

The same token must never be returned from take twice, as doing so can be used to +de-anonymize a client’s traffic.

+

Called when trying to connect to a server. It is always ok for this to return None.

+

Implementors§

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/transport_error/struct.Code.html b/pr/234/docs/iroh_quinn_proto/transport_error/struct.Code.html new file mode 100644 index 000000000..402d12b55 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_error/struct.Code.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TransportErrorCode.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/transport_error/struct.Error.html b/pr/234/docs/iroh_quinn_proto/transport_error/struct.Error.html new file mode 100644 index 000000000..a6bb9ca48 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_error/struct.Error.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.TransportError.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/transport_parameters/enum.Error.html b/pr/234/docs/iroh_quinn_proto/transport_parameters/enum.Error.html new file mode 100644 index 000000000..5ed448480 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_parameters/enum.Error.html @@ -0,0 +1,31 @@ +Error in iroh_quinn_proto::transport_parameters - Rust

Error

Enum Error 

Source
pub enum Error {
+    IllegalValue,
+    Malformed,
+}
Expand description

Errors encountered while decoding TransportParameters

+

Variants§

§

IllegalValue

Parameters that are semantically invalid

+
§

Malformed

Catch-all error for problems while decoding transport parameters

+

Trait Implementations§

Source§

impl Clone for Error

Source§

fn clone(&self) -> Error

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Error

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for Error

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for Error

1.30.0 · Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<Error> for TransportError

Source§

fn from(e: Error) -> Self

Converts to this type from the input type.
Source§

impl From<UnexpectedEnd> for Error

Source§

fn from(_: UnexpectedEnd) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Error

Source§

fn eq(&self, other: &Error) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for Error

Source§

impl Eq for Error

Source§

impl StructuralPartialEq for Error

Auto Trait Implementations§

§

impl Freeze for Error

§

impl RefUnwindSafe for Error

§

impl Send for Error

§

impl Sync for Error

§

impl Unpin for Error

§

impl UnwindSafe for Error

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read 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/234/docs/iroh_quinn_proto/transport_parameters/index.html b/pr/234/docs/iroh_quinn_proto/transport_parameters/index.html new file mode 100644 index 000000000..b8239f12a --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_parameters/index.html @@ -0,0 +1,7 @@ +iroh_quinn_proto::transport_parameters - Rust

Module transport_parameters

Module transport_parameters 

Source
Expand description

QUIC connection transport parameters

+

The TransportParameters type is used to represent the transport parameters +negotiated by peers while establishing a QUIC connection. This process +happens as part of the establishment of the TLS session. As such, the types +contained in this modules should generally only be referred to by custom +implementations of the crypto::Session trait.

+

Structs§

TransportParameters
Transport parameters used to negotiate connection-level preferences between peers

Enums§

Error
Errors encountered while decoding TransportParameters
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js b/pr/234/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js new file mode 100644 index 000000000..be0904c68 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_parameters/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Error"],"struct":["TransportParameters"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html b/pr/234/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html new file mode 100644 index 000000000..d2d4ee9d3 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/transport_parameters/struct.TransportParameters.html @@ -0,0 +1,31 @@ +TransportParameters in iroh_quinn_proto::transport_parameters - Rust

TransportParameters

Struct TransportParameters 

Source
pub struct TransportParameters {
+    pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+    /* private fields */
+}
Expand description

Transport parameters used to negotiate connection-level preferences between peers

+

Fields§

§max_remote_nat_traversal_addresses: Option<NonZeroU8>

Nat traversal draft

+

Implementations§

Source§

impl TransportParameters

Source

pub fn write<W: BufMut>(&self, w: &mut W)

Encode TransportParameters into buffer

+
Source

pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error>

Decode TransportParameters from buffer

+

Trait Implementations§

Source§

impl Clone for TransportParameters

Source§

fn clone(&self) -> TransportParameters

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TransportParameters

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for TransportParameters

Source§

fn eq(&self, other: &TransportParameters) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for TransportParameters

Source§

impl Eq for TransportParameters

Source§

impl StructuralPartialEq for TransportParameters

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where + Q: Eq + ?Sized, + K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/varint/struct.VarInt.html b/pr/234/docs/iroh_quinn_proto/varint/struct.VarInt.html new file mode 100644 index 000000000..678acb585 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/varint/struct.VarInt.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.VarInt.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html b/pr/234/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html new file mode 100644 index 000000000..95add4901 --- /dev/null +++ b/pr/234/docs/iroh_quinn_proto/varint/struct.VarIntBoundsExceeded.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_proto/struct.VarIntBoundsExceeded.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/all.html b/pr/234/docs/iroh_quinn_udp/all.html new file mode 100644 index 000000000..bf279c3f6 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/constant.BATCH_SIZE.html b/pr/234/docs/iroh_quinn_udp/constant.BATCH_SIZE.html new file mode 100644 index 000000000..aeff14292 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/constant.BATCH_SIZE.html @@ -0,0 +1,2 @@ +BATCH_SIZE in iroh_quinn_udp - Rust

BATCH_SIZE

Constant BATCH_SIZE 

Source
pub const BATCH_SIZE: usize = imp::BATCH_SIZE; // 32usize
Expand description

Number of UDP packets to send/receive at a time

+
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/enum.EcnCodepoint.html b/pr/234/docs/iroh_quinn_udp/enum.EcnCodepoint.html new file mode 100644 index 000000000..c130f9df1 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/enum.EcnCodepoint.html @@ -0,0 +1,28 @@ +EcnCodepoint in iroh_quinn_udp - Rust

EcnCodepoint

Enum EcnCodepoint 

Source
#[repr(u8)]
pub enum EcnCodepoint { + Ect0 = 2, + Ect1 = 1, + Ce = 3, +}
Expand description

Explicit congestion notification codepoint

+

Variants§

§

Ect0 = 2

The ECT(0) codepoint, indicating that an endpoint is ECN-capable

+
§

Ect1 = 1

The ECT(1) codepoint, indicating that an endpoint is ECN-capable

+
§

Ce = 3

The CE codepoint, signalling that congestion was experienced

+

Implementations§

Source§

impl EcnCodepoint

Source

pub fn from_bits(x: u8) -> Option<Self>

Create new object from the given bits

+

Trait Implementations§

Source§

impl Clone for EcnCodepoint

Source§

fn clone(&self) -> EcnCodepoint

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for EcnCodepoint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for EcnCodepoint

Source§

fn eq(&self, other: &EcnCodepoint) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, +and should not be overridden without very good reason.
Source§

impl Copy for EcnCodepoint

Source§

impl Eq for EcnCodepoint

Source§

impl StructuralPartialEq for EcnCodepoint

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html b/pr/234/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html new file mode 100644 index 000000000..ef7f29570 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/imp/struct.UdpSocketState.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../iroh_quinn_udp/struct.UdpSocketState.html...

+ + + \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/index.html b/pr/234/docs/iroh_quinn_udp/index.html new file mode 100644 index 000000000..189d8f7f7 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/index.html @@ -0,0 +1,22 @@ +iroh_quinn_udp - Rust

Crate iroh_quinn_udp

Crate iroh_quinn_udp 

Source
Expand description

Uniform interface to send and receive UDP packets with advanced features useful for QUIC

+

This crate exposes kernel UDP stack features available on most modern systems which are required +for an efficient and conformant QUIC implementation. As of this writing, these are not available +in std or major async runtimes, and their niche character and complexity are a barrier to adding +them. Hence, a dedicated crate.

+

Exposed features include:

+
    +
  • Segmentation offload for bulk send and receive operations, reducing CPU load.
  • +
  • Reporting the exact destination address of received packets and specifying explicit source +addresses for sent packets, allowing responses to be sent from the address that the peer +expects when there are multiple possibilities. This is common when bound to a wildcard address +in IPv6 due to RFC 8981 temporary addresses.
  • +
  • Explicit Congestion Notification, which is required by QUIC to prevent packet loss and reduce +latency on congested links when supported by the network path.
  • +
  • Disabled IP-layer fragmentation, which allows the true physical MTU to be detected and reduces +risk of QUIC packet loss.
  • +
+

Some features are unavailable in some environments. This can be due to an outdated operating +system or drivers. Some operating systems may not implement desired features at all, or may not +yet be supported by the crate. When support is unavailable, functionality will gracefully +degrade.

+

Structs§

RecvMeta
Metadata for a single buffer filled with bytes received from the network
Transmit
An outgoing packet
UdpSockRef
A borrowed UDP socket
UdpSocketState
Tokio-compatible UDP socket with some useful specializations.

Enums§

EcnCodepoint
Explicit congestion notification codepoint

Constants§

BATCH_SIZE
Number of UDP packets to send/receive at a time
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/sidebar-items.js b/pr/234/docs/iroh_quinn_udp/sidebar-items.js new file mode 100644 index 000000000..d1db6b942 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"constant":["BATCH_SIZE"],"enum":["EcnCodepoint"],"struct":["RecvMeta","Transmit","UdpSockRef","UdpSocketState"]}; \ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/struct.RecvMeta.html b/pr/234/docs/iroh_quinn_udp/struct.RecvMeta.html new file mode 100644 index 000000000..db917c321 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/struct.RecvMeta.html @@ -0,0 +1,41 @@ +RecvMeta in iroh_quinn_udp - Rust

RecvMeta

Struct RecvMeta 

Source
#[non_exhaustive]
pub struct RecvMeta { + pub addr: SocketAddr, + pub len: usize, + pub stride: usize, + pub ecn: Option<EcnCodepoint>, + pub dst_ip: Option<IpAddr>, + pub interface_index: Option<u32>, +}
Expand description

Metadata for a single buffer filled with bytes received from the network

+

This associated buffer can contain one or more datagrams, see stride.

+

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§addr: SocketAddr

The source address of the datagram(s) contained in the buffer

+
§len: usize

The number of bytes the associated buffer has

+
§stride: usize

The size of a single datagram in the associated buffer

+

When GRO (Generic Receive Offload) is used this indicates the size of a single +datagram inside the buffer. If the buffer is larger, that is if len is greater +then this value, then the individual datagrams contained have their boundaries at +stride increments from the start. The last datagram could be smaller than +stride.

+
§ecn: Option<EcnCodepoint>

The Explicit Congestion Notification bits for the datagram(s) in the buffer

+
§dst_ip: Option<IpAddr>

The destination IP address which was encoded in this datagram

+

Populated on platforms: Windows, Linux, Android (API level > 25), +FreeBSD, OpenBSD, NetBSD, macOS, and iOS.

+
§interface_index: Option<u32>

The interface index of the interface on which the datagram was received

+

Trait Implementations§

Source§

impl Clone for RecvMeta

Source§

fn clone(&self) -> RecvMeta

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RecvMeta

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for RecvMeta

Source§

fn default() -> Self

Constructs a value with arbitrary fields, intended to be overwritten

+
Source§

impl Copy for RecvMeta

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/struct.Transmit.html b/pr/234/docs/iroh_quinn_udp/struct.Transmit.html new file mode 100644 index 000000000..b0cf49ba4 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/struct.Transmit.html @@ -0,0 +1,31 @@ +Transmit in iroh_quinn_udp - Rust

Transmit

Struct Transmit 

Source
pub struct Transmit<'a> {
+    pub destination: SocketAddr,
+    pub ecn: Option<EcnCodepoint>,
+    pub contents: &'a [u8],
+    pub segment_size: Option<usize>,
+    pub src_ip: Option<IpAddr>,
+}
Expand description

An outgoing packet

+

Fields§

§destination: SocketAddr

The socket this datagram should be sent to

+
§ecn: Option<EcnCodepoint>

Explicit congestion notification bits to set on the packet

+
§contents: &'a [u8]

Contents of the datagram

+
§segment_size: Option<usize>

The segment size if this transmission contains multiple datagrams. +This is None if the transmit only contains a single datagram

+
§src_ip: Option<IpAddr>

Optional source IP address for the datagram

+

Trait Implementations§

Source§

impl<'a> Clone for Transmit<'a>

Source§

fn clone(&self) -> Transmit<'a>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<'a> Debug for Transmit<'a>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'a> Freeze for Transmit<'a>

§

impl<'a> RefUnwindSafe for Transmit<'a>

§

impl<'a> Send for Transmit<'a>

§

impl<'a> Sync for Transmit<'a>

§

impl<'a> Unpin for Transmit<'a>

§

impl<'a> UnwindSafe for Transmit<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where + T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/struct.UdpSockRef.html b/pr/234/docs/iroh_quinn_udp/struct.UdpSockRef.html new file mode 100644 index 000000000..046df30b2 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/struct.UdpSockRef.html @@ -0,0 +1,19 @@ +UdpSockRef in iroh_quinn_udp - Rust

UdpSockRef

Struct UdpSockRef 

Source
pub struct UdpSockRef<'a>(/* private fields */);
Expand description

A borrowed UDP socket

+

On Unix, constructible via From<T: AsFd>. On Windows, constructible via From<T: AsSocket>.

+

Trait Implementations§

Source§

impl<'s, S> From<&'s S> for UdpSockRef<'s>
where + S: AsFd,

Available on Unix only.
Source§

fn from(socket: &'s S) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl<'a> Freeze for UdpSockRef<'a>

§

impl<'a> RefUnwindSafe for UdpSockRef<'a>

§

impl<'a> Send for UdpSockRef<'a>

§

impl<'a> Sync for UdpSockRef<'a>

§

impl<'a> Unpin for UdpSockRef<'a>

§

impl<'a> UnwindSafe for UdpSockRef<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/iroh_quinn_udp/struct.UdpSocketState.html b/pr/234/docs/iroh_quinn_udp/struct.UdpSocketState.html new file mode 100644 index 000000000..a1e39f089 --- /dev/null +++ b/pr/234/docs/iroh_quinn_udp/struct.UdpSocketState.html @@ -0,0 +1,62 @@ +UdpSocketState in iroh_quinn_udp - Rust

UdpSocketState

Struct UdpSocketState 

Source
pub struct UdpSocketState { /* private fields */ }
Expand description

Tokio-compatible UDP socket with some useful specializations.

+

Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some +platforms.

+

Implementations§

Source§

impl UdpSocketState

Source

pub fn new(sock: UdpSockRef<'_>) -> Result<Self>

Source

pub fn send( + &self, + socket: UdpSockRef<'_>, + transmit: &Transmit<'_>, +) -> Result<()>

Sends a Transmit on the given socket.

+

This function will only ever return errors of kind io::ErrorKind::WouldBlock. +All other errors will be logged and converted to Ok.

+

UDP transmission errors are considered non-fatal because higher-level protocols must +employ retransmits and timeouts anyway in order to deal with UDP’s unreliable nature. +Thus, logging is most likely the only thing you can do with these errors.

+

If you would like to handle these errors yourself, use UdpSocketState::try_send +instead.

+
Source

pub fn try_send( + &self, + socket: UdpSockRef<'_>, + transmit: &Transmit<'_>, +) -> Result<()>

Sends a Transmit on the given socket without any additional error handling.

+
Source

pub fn recv( + &self, + socket: UdpSockRef<'_>, + bufs: &mut [IoSliceMut<'_>], + meta: &mut [RecvMeta], +) -> Result<usize>

Source

pub fn max_gso_segments(&self) -> usize

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) -> usize

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/234/docs/perf/all.html b/pr/234/docs/perf/all.html new file mode 100644 index 000000000..e395c4e55 --- /dev/null +++ b/pr/234/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/234/docs/perf/client/fn.run.html b/pr/234/docs/perf/client/fn.run.html new file mode 100644 index 000000000..4e3af1a2f --- /dev/null +++ b/pr/234/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/234/docs/perf/client/index.html b/pr/234/docs/perf/client/index.html new file mode 100644 index 000000000..35b1a32ed --- /dev/null +++ b/pr/234/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/234/docs/perf/client/sidebar-items.js b/pr/234/docs/perf/client/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/234/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/234/docs/perf/client/struct.Opt.html b/pr/234/docs/perf/client/struct.Opt.html new file mode 100644 index 000000000..9e115c3fa --- /dev/null +++ b/pr/234/docs/perf/client/struct.Opt.html @@ -0,0 +1,36 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/enum.CongestionAlgorithm.html b/pr/234/docs/perf/enum.CongestionAlgorithm.html new file mode 100644 index 000000000..244d49b34 --- /dev/null +++ b/pr/234/docs/perf/enum.CongestionAlgorithm.html @@ -0,0 +1,23 @@ +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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/fn.parse_byte_size.html b/pr/234/docs/perf/fn.parse_byte_size.html new file mode 100644 index 000000000..89aa88fa3 --- /dev/null +++ b/pr/234/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/234/docs/perf/index.html b/pr/234/docs/perf/index.html new file mode 100644 index 000000000..7a3b1be19 --- /dev/null +++ b/pr/234/docs/perf/index.html @@ -0,0 +1 @@ +perf - Rust

Crate perf

\ No newline at end of file diff --git a/pr/234/docs/perf/noprotection/index.html b/pr/234/docs/perf/noprotection/index.html new file mode 100644 index 000000000..36fb17ad6 --- /dev/null +++ b/pr/234/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/234/docs/perf/noprotection/sidebar-items.js b/pr/234/docs/perf/noprotection/sidebar-items.js new file mode 100644 index 000000000..7ed1562e0 --- /dev/null +++ b/pr/234/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/234/docs/perf/noprotection/struct.NoProtectionClientConfig.html b/pr/234/docs/perf/noprotection/struct.NoProtectionClientConfig.html new file mode 100644 index 000000000..830f39d3a --- /dev/null +++ b/pr/234/docs/perf/noprotection/struct.NoProtectionClientConfig.html @@ -0,0 +1,22 @@ +NoProtectionClientConfig in perf::noprotection - Rust

NoProtectionClientConfig

Struct NoProtectionClientConfig 

Source
pub struct NoProtectionClientConfig { /* private fields */ }

Implementations§

Trait Implementations§

Source§

impl ClientConfig for NoProtectionClientConfig

Source§

fn start_session( + self: Arc<Self>, + version: u32, + server_name: &str, + params: &TransportParameters, +) -> Result<Box<dyn Session>, ConnectError>

Start a client session with this configuration

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/noprotection/struct.NoProtectionServerConfig.html b/pr/234/docs/perf/noprotection/struct.NoProtectionServerConfig.html new file mode 100644 index 000000000..09a6aa2a6 --- /dev/null +++ b/pr/234/docs/perf/noprotection/struct.NoProtectionServerConfig.html @@ -0,0 +1,30 @@ +NoProtectionServerConfig in perf::noprotection - Rust

NoProtectionServerConfig

Struct NoProtectionServerConfig 

Source
pub struct NoProtectionServerConfig { /* private fields */ }

Implementations§

Trait Implementations§

Source§

impl ServerConfig for NoProtectionServerConfig

Source§

fn initial_keys( + &self, + version: u32, + dst_cid: ConnectionId, +) -> Result<Keys, UnsupportedVersion>

Create the initial set of keys given the client’s initial destination ConnectionId
Source§

fn retry_tag( + &self, + version: u32, + orig_dst_cid: ConnectionId, + packet: &[u8], +) -> [u8; 16]

Generate the integrity tag for a retry packet Read more
Source§

fn start_session( + self: Arc<Self>, + version: u32, + params: &TransportParameters, +) -> Box<dyn Session>

Start a server session with this configuration Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/server/fn.run.html b/pr/234/docs/perf/server/fn.run.html new file mode 100644 index 000000000..d244218b7 --- /dev/null +++ b/pr/234/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/234/docs/perf/server/index.html b/pr/234/docs/perf/server/index.html new file mode 100644 index 000000000..88db7d540 --- /dev/null +++ b/pr/234/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/234/docs/perf/server/sidebar-items.js b/pr/234/docs/perf/server/sidebar-items.js new file mode 100644 index 000000000..3dceadfac --- /dev/null +++ b/pr/234/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/234/docs/perf/server/struct.Opt.html b/pr/234/docs/perf/server/struct.Opt.html new file mode 100644 index 000000000..ddf5994ce --- /dev/null +++ b/pr/234/docs/perf/server/struct.Opt.html @@ -0,0 +1,35 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/sidebar-items.js b/pr/234/docs/perf/sidebar-items.js new file mode 100644 index 000000000..317d83630 --- /dev/null +++ b/pr/234/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/234/docs/perf/static.PERF_CIPHER_SUITES.html b/pr/234/docs/perf/static.PERF_CIPHER_SUITES.html new file mode 100644 index 000000000..4e1443460 --- /dev/null +++ b/pr/234/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/234/docs/perf/stats/index.html b/pr/234/docs/perf/stats/index.html new file mode 100644 index 000000000..15fd04ede --- /dev/null +++ b/pr/234/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/234/docs/perf/stats/sidebar-items.js b/pr/234/docs/perf/stats/sidebar-items.js new file mode 100644 index 000000000..5c7b90d68 --- /dev/null +++ b/pr/234/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/234/docs/perf/stats/struct.OpenStreamStats.html b/pr/234/docs/perf/stats/struct.OpenStreamStats.html new file mode 100644 index 000000000..52a6bec60 --- /dev/null +++ b/pr/234/docs/perf/stats/struct.OpenStreamStats.html @@ -0,0 +1,28 @@ +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> ToOwned for T
where + T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/stats/struct.Stats.html b/pr/234/docs/perf/stats/struct.Stats.html new file mode 100644 index 000000000..dd6e4533e --- /dev/null +++ b/pr/234/docs/perf/stats/struct.Stats.html @@ -0,0 +1,17 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/stats/struct.StreamStats.html b/pr/234/docs/perf/stats/struct.StreamStats.html new file mode 100644 index 000000000..e413dafe6 --- /dev/null +++ b/pr/234/docs/perf/stats/struct.StreamStats.html @@ -0,0 +1,17 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/perf/struct.CommonOpt.html b/pr/234/docs/perf/struct.CommonOpt.html new file mode 100644 index 000000000..e6c80b51e --- /dev/null +++ b/pr/234/docs/perf/struct.CommonOpt.html @@ -0,0 +1,77 @@ +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, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/quinn_perf/all.html b/pr/234/docs/quinn_perf/all.html new file mode 100644 index 000000000..0c76accc7 --- /dev/null +++ b/pr/234/docs/quinn_perf/all.html @@ -0,0 +1 @@ +List of all items in this crate

All

List of all items

Structs

Enums

Functions

\ No newline at end of file diff --git a/pr/234/docs/quinn_perf/enum.Commands.html b/pr/234/docs/quinn_perf/enum.Commands.html new file mode 100644 index 000000000..b7ba828b0 --- /dev/null +++ b/pr/234/docs/quinn_perf/enum.Commands.html @@ -0,0 +1,33 @@ +Commands in quinn_perf - Rust

Commands

Enum Commands 

Source
pub(crate) enum Commands {
+    Server(Opt),
+    Client(Opt),
+}

Variants§

§

Server(Opt)

Run as a perf server

+
§

Client(Opt)

Run as a perf client

+

Trait Implementations§

Source§

impl FromArgMatches for Commands

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut<'b>( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Subcommand for Commands

Source§

fn augment_subcommands<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_subcommands_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

fn has_subcommand(__clap_name: &str) -> bool

Test whether Self can parse a specific subcommand

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where + L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/quinn_perf/fn.main.html b/pr/234/docs/quinn_perf/fn.main.html new file mode 100644 index 000000000..d02fe8057 --- /dev/null +++ b/pr/234/docs/quinn_perf/fn.main.html @@ -0,0 +1 @@ +main in quinn_perf - Rust

main

Function main 

Source
pub(crate) fn main()
\ No newline at end of file diff --git a/pr/234/docs/quinn_perf/index.html b/pr/234/docs/quinn_perf/index.html new file mode 100644 index 000000000..520445aa4 --- /dev/null +++ b/pr/234/docs/quinn_perf/index.html @@ -0,0 +1 @@ +quinn_perf - Rust

Crate quinn_perf

Crate quinn_perf 

Source

Structs§

Cli 🔒

Enums§

Commands 🔒

Functions§

main 🔒
\ No newline at end of file diff --git a/pr/234/docs/quinn_perf/sidebar-items.js b/pr/234/docs/quinn_perf/sidebar-items.js new file mode 100644 index 000000000..9a5c272f9 --- /dev/null +++ b/pr/234/docs/quinn_perf/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["Commands"],"fn":["main"],"struct":["Cli"]}; \ No newline at end of file diff --git a/pr/234/docs/quinn_perf/struct.Cli.html b/pr/234/docs/quinn_perf/struct.Cli.html new file mode 100644 index 000000000..4e792e97c --- /dev/null +++ b/pr/234/docs/quinn_perf/struct.Cli.html @@ -0,0 +1,38 @@ +Cli in quinn_perf - Rust

Cli

Struct Cli 

Source
pub(crate) struct Cli {
+    pub(crate) command: Commands,
+}

Fields§

§command: Commands

Trait Implementations§

Source§

impl Args for Cli

Source§

fn group_id() -> Option<Id>

Report the [ArgGroup::id][crate::ArgGroup::id] for this set of arguments
Source§

fn augment_args<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate Self via +[FromArgMatches::from_arg_matches_mut] Read more
Source§

fn augment_args_for_update<'b>(__clap_app: Command) -> Command

Append to [Command] so it can instantiate self via +[FromArgMatches::update_from_arg_matches_mut] Read more
Source§

impl CommandFactory for Cli

Source§

fn command<'b>() -> Command

Build a [Command] that can instantiate Self. Read more
Source§

fn command_for_update<'b>() -> Command

Build a [Command] that can update self. Read more
Source§

impl FromArgMatches for Cli

Source§

fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn from_arg_matches_mut( + __clap_arg_matches: &mut ArgMatches, +) -> Result<Self, Error>

Instantiate Self from [ArgMatches], parsing the arguments as needed. Read more
Source§

fn update_from_arg_matches( + &mut self, + __clap_arg_matches: &ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

fn update_from_arg_matches_mut( + &mut self, + __clap_arg_matches: &mut ArgMatches, +) -> Result<(), Error>

Assign values from ArgMatches to self.
Source§

impl Parser for Cli

§

fn parse() -> Self

Parse from std::env::args_os(), [exit][Error::exit] on error.
§

fn try_parse() -> Result<Self, Error>

Parse from std::env::args_os(), return Err on error.
§

fn parse_from<I, T>(itr: I) -> Self
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, [exit][Error::exit] on error.
§

fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Parse from iterator, return Err on error.
§

fn update_from<I, T>(&mut self, itr: I)
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, [exit][Error::exit] on error. Read more
§

fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
where + I: IntoIterator<Item = T>, + T: Into<OsString> + Clone,

Update from iterator, return Err on error.

Auto Trait Implementations§

§

impl Freeze for Cli

§

impl RefUnwindSafe for Cli

§

impl Send for Cli

§

impl Sync for Cli

§

impl Unpin for Cli

§

impl UnwindSafe for Cli

Blanket Implementations§

Source§

impl<T> Any for T
where + T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where + T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

+
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an +Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an +Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where + U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where + L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
Source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where + V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where + S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a +[WithDispatch] wrapper. Read more
\ No newline at end of file diff --git a/pr/234/docs/search.index/003a3a433ad6.js b/pr/234/docs/search.index/003a3a433ad6.js new file mode 100644 index 000000000..d1f5819a8 --- /dev/null +++ b/pr/234/docs/search.index/003a3a433ad6.js @@ -0,0 +1 @@ +rn_("AYACsRAS8QABsBAL1gABZW8BBQA7MAAAAQAADQABAFsQDQABAgDz8g4AAAEAAQABAgD07g4AAAEAAQABAAEDADswAAABAAAIAAEAkA8IAIcHAACHsKASLwABsNAStwABoFAAABKGjBQR9WMHAABZCAEAAQABAPOBAmx08wABaTswAAABAAB3AAMAHAQAAMoPAAC3EHUAAQUA89gSAAABADQAAQABoEAAAA/OZfPvCAAAAQBPCBOAAqAwAAAO0WRy8wABZfMBBwAAAQABAPOAAmh0AQEAOzAAAAEAAHYAAgDKDwAAtxB1ABOAAqBQAAAPC2l0AQgA9GMSAAABAHoABQATAISw8BLaACywkBK+AAGwUBG8AT9AiQA7MAAAAQAACgAFAE8PAgCrEQEAVBIBAJESAQDQEgEA1wEAAIawgBBzAAGgQAAACRAIMCY6MAAAAQAAAAAACQAQAAAAeQB6AGsHSwmUC3gMNRE2ESISIxI=") \ No newline at end of file diff --git a/pr/234/docs/search.index/005aa45d6078.js b/pr/234/docs/search.index/005aa45d6078.js new file mode 100644 index 000000000..baac2e0be --- /dev/null +++ b/pr/234/docs/search.index/005aa45d6078.js @@ -0,0 +1 @@ +rn_("AQoA9BASAAABAA0AAQABgwKgIAAADzWgMAAAD7tpdAEEAbAAEZYAAWTySBEAAAEAI4aEsKASsQABsEARlAABsHASZgABFYAAAQACsDAPKAABsBASJgABc3QBzQUAAAEMAPSVEgAAAQBGAAEAE4ACoAAAAAdddHnzgIURwQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/00663acd356b.js b/pr/234/docs/search.index/00663acd356b.js new file mode 100644 index 000000000..b163e25f8 --- /dev/null +++ b/pr/234/docs/search.index/00663acd356b.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABHRoAAAABHSZHIBgAKgcAAAElGgMAAAD+JmdAELAaBAAAASmWXywxIAAAEAI4ECsFAPwgABY3YBAAGwgBKbAAFw8pcSAAABAEMGA6AgAAAQJaBAAAARSmRlc/PlDgAA3wIBAPMABGlyc3Y7MAAAAQAADwAFAAkHBQBmEQUAkREBAPURAADOEgAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/006b398a6f2a.js b/pr/234/docs/search.index/006b398a6f2a.js new file mode 100644 index 000000000..b4b823de9 --- /dev/null +++ b/pr/234/docs/search.index/006b398a6f2a.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAAkAEAAAAJUQlhCDEYQRUhJTEp8SoBIOEw8T8wABb/JUCQAAAQATgAOxABLhAAGwQBADAAFob3AjgAOgoAAAEhuw8BLQAAFtc3Q=") \ No newline at end of file diff --git a/pr/234/docs/search.index/00b1c72d15ee.js b/pr/234/docs/search.index/00b1c72d15ee.js new file mode 100644 index 000000000..a291f70ca --- /dev/null +++ b/pr/234/docs/search.index/00b1c72d15ee.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABBBZQElDwAAAQgA9H0QAAABAAEAAQABAwGgQAAAEEBlAb8OAAABAgDzswsAAAEAAQAjAAKwIAulAAFrcvYFBwAAAQDOCM8AAQDiAfOAAnJ4AQMBsIASugABdjswAAABAAAKAAMAhxAGABsSAAAsEgIAI4ACsGAPYQCwYXIBAwGgAAAADyRzAb4OAAAjgAKgsAAAElFlaAEDAbBAD+AAAWXy/AgAAAEA1yUAAIywIAdtCJ+wcBBIAAGwIAdVAAGwsBG0AAGggAAAEIGgAAAAEcG/DEw7MAAAAQAACgAFAMYBAAD9AwIAlgsBAO0LAgDCEQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/019bd8f229e4.js b/pr/234/docs/search.index/019bd8f229e4.js new file mode 100644 index 000000000..5069183d8 --- /dev/null +++ b/pr/234/docs/search.index/019bd8f229e4.js @@ -0,0 +1 @@ +rn_("AQMBsDAR1QABY/NEEAAAAQABAAEDAYAR8gAQ4XM7MAAAAQAABQABANcRBQAjggKgkAAAEr1pcwcBAIDFsFARpAABoKAAABIysHAR/AABoNAAABLJgAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/234/docs/search.index/01bda78c46d2.js b/pr/234/docs/search.index/01bda78c46d2.js new file mode 100644 index 000000000..d3e1773aa --- /dev/null +++ b/pr/234/docs/search.index/01bda78c46d2.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABCoZfNuDAAAdQMBAAGBA7BgEIwAAaAAAAAQI7EgEvgAAWFkbgEUAPUWEwAAAQABAAEAAQABBgGwgBK6AAF2OzAAAAEAAAgAAwCHEAQAGxIAACwSAgBXAAABhqDAAAAR+qCgAAASHKCQAAAR7rBQD2IAAQXgAvInBwAAAQABBgDzYBEAAAEAkwDzgANydHYBAwA7MAAAAQAABwACACgSAwD8EgMA8wABcwG9EgAA8wABcwGnEAAAZwEAgIagMAAAC8SwABHpAAGgEAAADFkSQA4=") \ No newline at end of file diff --git a/pr/234/docs/search.index/01d8a434418f.js b/pr/234/docs/search.index/01d8a434418f.js new file mode 100644 index 000000000..22c4e474f --- /dev/null +++ b/pr/234/docs/search.index/01d8a434418f.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABLuc/KSEAAAAQABCAD1oRIAAAEAAQABAAEAowCEsCAJUgABoIAAABF6MEAIOzAAAAEAAAUAAgCyAQAAcg8EAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/02e652a4afc3.js b/pr/234/docs/search.index/02e652a4afc3.js new file mode 100644 index 000000000..aa2df0ce6 --- /dev/null +++ b/pr/234/docs/search.index/02e652a4afc3.js @@ -0,0 +1 @@ +rn_("AYACoFAAABAOoFAAABEtYXUBgwKxABMQAAGw0BMCAAFrbQEEAaCgAAATG3fyCxMAAAEAAYACoIAAABKnsLAShAABZW9jAwOgYAAAEkRlbnABqQsAACOAAqBAAAAPqW1wAQoA82gSAAABAGkAE4ACohAAABMbbG/3AAAAhaEAAAAS1ASBAzswAAABAAA7AAQA6wUFAFwHAAClBzMAQxAAAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/0369b203eb42.js b/pr/234/docs/search.index/0369b203eb42.js new file mode 100644 index 000000000..edfc7c3e8 --- /dev/null +++ b/pr/234/docs/search.index/0369b203eb42.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQDXEQUAg4YEsFARpAABsHAR/AABoNAAABLJYm5vdhOAArAQEYEAAXByAQABoDAAABCidAEcEgAA8wIBc/KADwAAnQITgAKgYAAAD3FldQECAPZzDAAAAQC/AgEABwEBAAGAArAAEW0AAYANyi1QAWVzAQEA82QHAACSCgEAY4ADsIASagABYm5084ADYW9yARQA9PMSAAABAAEAAQABAAA7MAAAAQAABgACAPsRAABdEgUAI4gCsEASxwABZXMBBAGwMBHVAAFj80QQAAABAAEAAQUAOzAAAAEAAAgAAwCrEAIAihEEAIgSAADzAgNjZXDzPQwAAAEAAQBjgAOgYAAADuBlb3UBAAGwYBJ0AAFlAaYSAADzAgFz9gsJAAABAAEAAQc0APwAAQgA81MRAAABAAEAQ4ADoBAAABH+oOAAABJ/ZG9zAQIBoFAAABBDcgHuDwAAAYACoEAAAA99oHAAABK0YWUBAAA7MAAAAQAACwACAAkHBQBmEQUA84ACdHYBAQD08wcAAAEAAQABAPMAAm5y9AEJAABQALIGAQDzAIcwkQz1fxEAAAEAcgABAOEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/03a2caa15777.js b/pr/234/docs/search.index/03a2caa15777.js new file mode 100644 index 000000000..23c5db59a --- /dev/null +++ b/pr/234/docs/search.index/03a2caa15777.js @@ -0,0 +1 @@ +rn_("AQADoKAAABK0oCAAABH+sDAQnwABZWl08n8RAAABAAEDADswAAABAAAGAAEAKRAGAAEIAPRjEgAAAQB6AAUAEwEDsJASvgABsFARvAE/Z2xwOzAAAAEAAAoABQBPDwIAqxEBAFQSAQCREgEA0BIBAAGAArAwEJsAAbAwEJ0AAXJ2AQIBsAARSwABZPSZEAAAAQDVAAEAI4ECoJAAABKzaW8BBwD0zxIAAAsAHQAPAHcLAAGIsFARTQDpsGARugABlQkDOjAAAAEAAAAAAA0AEAAAAE0MTgxPDMsNzA2jD6QP2Q/3D00QThBPEFAQshEjgAKgEAAADExubw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/059f564e1f6b.js b/pr/234/docs/search.index/059f564e1f6b.js new file mode 100644 index 000000000..e576d341f --- /dev/null +++ b/pr/234/docs/search.index/059f564e1f6b.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABMKZgEGEgAAAQIA9a4PAAABAAEACAIBAPMAAml19o0PAADtAcsAAQAuAAEAAQgA9aESAAABAAEAAQABAPMCAWb1cg8AAAEAAQABAAEAE4ACsKAS5AABcnTzAgJhc/OfDwAAAQABAAEEAbBAEsEAAWX1iRIAAAEAAQABAAEAAQIAOzAAAAEAAAsAAgA7EgUA6BIFAPMDAW07MAAAAQAADQAEAEIMAAAyEAAAcxEFAKkSBQDzgQJjZfOAAmFl") \ No newline at end of file diff --git a/pr/234/docs/search.index/05ae80dc1c58.js b/pr/234/docs/search.index/05ae80dc1c58.js new file mode 100644 index 000000000..3d6698aac --- /dev/null +++ b/pr/234/docs/search.index/05ae80dc1c58.js @@ -0,0 +1 @@ +rn_("AQECoCAAABCUoDAAABFAYmgBww4AAAEOAPSVEgAAAQBGAAEAAQMBoKAAABMbd/ILEwAAAQCzAISwQA87AAEBCAny5gcAAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/05df7f0f67ba.js b/pr/234/docs/search.index/05df7f0f67ba.js new file mode 100644 index 000000000..b9cb57855 --- /dev/null +++ b/pr/234/docs/search.index/05df7f0f67ba.js @@ -0,0 +1 @@ +rn_("AQAA9IUPAAABAAEAAQDzAgFz9SkJAAABAAEAAQCgBQEGAPSyEAAAAQABAAEAAQEAOzAAAAEAAAYAAQAUEwYA8wYBZfTKEgAAAQAtAAEAEwYDsEAS8QABoGAAABJ2YXJ1AVwPAADzgAJhZQEDAPR1EAAAAQABAAEAAQIA9MEOAAABAL4DAQABgAOwAAd9AAGgAAAAC6GwcA9kAAFucnOnBwAAh6BwAAAQsbBwEYMAAZEFQTowAAABAAAAAAALABAAAACnAagBqQGqAX8H6AcyCdgL8g7zDvQO5g8=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0731b0da5a08.js b/pr/234/docs/search.index/0731b0da5a08.js new file mode 100644 index 000000000..bc74cafc6 --- /dev/null +++ b/pr/234/docs/search.index/0731b0da5a08.js @@ -0,0 +1 @@ +rn_("AQIA9WoQAAABAAEAAQABAAGAArBgEZwAAaAgAAAP0W95AQABoMAAABKaafTmEQAABwASABsAAQYA9PwSAAABAAEAAQABAgGgQAAAEcFyOzAAAAEAAAcAAgDPDwEAUhAFABOAArAgEFgAAWFvZwMAAIawgBHAAE2woBImAAEMMAY7MAAAAQAACAADAKQLAAB9EAMARBEDAPOCAmVpIwACoIAAABMKZmjy5ggAACAJAQACsBALkgABsDASsQABZXDypw8AAAEAAQADsEASggABoUAAABLjoFAAABChYmNmAR0HAADzgAJtbgEBA6CwAAASQbBAD44AAaBgAAAQUWNldPQEBwAANAgVAukAAQUAOzAAAAEAAAUAAQBmEQUAAQcA9d0RAAABAAEAAQABACOAAqBQAAAQKGFv8wCFIEkCOzAAAAEAAAgABABaBwAAhQcFAG0MAABaEAAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/081bb183a3a6.js b/pr/234/docs/search.index/081bb183a3a6.js new file mode 100644 index 000000000..5a05bb1b5 --- /dev/null +++ b/pr/234/docs/search.index/081bb183a3a6.js @@ -0,0 +1 @@ +rn_("AYECsDAPKAABsBASJgABc3QBAwDzWREAAAEAAQAjgAKwABB7AAFoaQEAADswAAABAAATAAEAiwcTACOAAqAwAAAMS2Fl84ACcnTzAAJhbDswAAABAAAoAAMAfQATAG8BAAAODxMA") \ No newline at end of file diff --git a/pr/234/docs/search.index/083bd29ff9ff.js b/pr/234/docs/search.index/083bd29ff9ff.js new file mode 100644 index 000000000..4a4cbc966 --- /dev/null +++ b/pr/234/docs/search.index/083bd29ff9ff.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABHJc/THDgAAaAMBAAIAAQECsGASnQABsEAS1gABZHQ7MAAAAQAADQAGAIYRAwDiEQEAQhIBAFkSAQDMEgEA7xIBAAEBhLBwEK8AAbCAEWQAAaBgAAAR+YAR8gAQ4QAwAzswAAABAAALAAIA3QgFANcRBQCjgISgsAAAEjGxcBMOAAEIQIIBAISgcAAAETegcAAAD9yxABKdAAGwABG8AT8CIQHysAUAAAEAAQEAOzAAAAEAAAYAAQAUEwYA8wYBZfTKEgAAAQAtAAEAEwEDsEAS8QABoGAAABJ2YXJ1AVwPAAB3BAAAh6AQAAAF3rAwEa8BJqBAAAAP+hHBDDswAAABAAAGAAIA+xEAAF0SBQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0850db5b7e99.js b/pr/234/docs/search.index/0850db5b7e99.js new file mode 100644 index 000000000..1c123258a --- /dev/null +++ b/pr/234/docs/search.index/0850db5b7e99.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQA2CQcAAQIA9zUPAAC6AFgCAQABAGwAAQATgAKwMA+7AJFpdPMAAXPy5BIAAAEAM4ADsRASZgABZG5w") \ No newline at end of file diff --git a/pr/234/docs/search.index/0852167fd512.js b/pr/234/docs/search.index/0852167fd512.js new file mode 100644 index 000000000..3c1fa93b4 --- /dev/null +++ b/pr/234/docs/search.index/0852167fd512.js @@ -0,0 +1 @@ +rn_("AQcA9NUPAAABAAEAAQABAQD1QQkAAAEAAQABAAEAAQMBoJAAABJxZfKsDgAAAQDXAAAAhaBQAAAPWrFAExQAAYEpAPKuBQAAAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0878d7ee02a1.js b/pr/234/docs/search.index/0878d7ee02a1.js new file mode 100644 index 000000000..90f017652 --- /dev/null +++ b/pr/234/docs/search.index/0878d7ee02a1.js @@ -0,0 +1 @@ +rn_("AQEA9+oPAAABAAEAdwIBAHoABQABAgD0iQ8AAAEAAQABABOAAqDQAAASzmNzU4ADoHAAABLJZWlvQ4ADoCAAAA7ZsAAHXwABZGx0") \ No newline at end of file diff --git a/pr/234/docs/search.index/0977b4e74445.js b/pr/234/docs/search.index/0977b4e74445.js new file mode 100644 index 000000000..b4be3297b --- /dev/null +++ b/pr/234/docs/search.index/0977b4e74445.js @@ -0,0 +1 @@ +rn_("AQIA96ILAAABAAIDAQArASsCAQABgQKgAAAAEEegEAAADsBldROAArAwD6wAAWFyAYECoJAAABJasFARPAABYncTgAKwkBGwAAFucvMAAmV09JsPAAABABYAAQABAQD19wsAAAEAAQABAAEAE4ACsLASVgABYW8jAAKwQAxmAAFldDswAAABAAAJAAQADgUBALoFAQDeCwEAbxADAAECADswAAABAAAJAAIA6QcHAJoRAQABgAKwkBHwAAGgoAAAEllldhOAArGAExYAAWNtAYICsEASggABoFAAABChYmYTgAKgQAAAEqZkcyOAAqDQAAAS7mNu84CEEQFAAQUBsEAR0wABZQE7EQAAQ4IDoQAAABL6oNAAABLTa21zE4ACsGAPtgABYXIBBQA7MAAAAQAAdgACAMoPAAC3EHUAAYIDsCARnAABsCAPXQABsEAQOwABa2xzU4ADsCAS1gABa3R1AYmEoIAAABMJoEAAABK8oGAAABLboJAAABMNAoAKE4KEoKAAABJYsaATGQABsNAStQABBAEJAYACsGAPtAABsYATFAABaW8BAAD0qgsAAAEASgABACOBAqDgAAASqGV5AQ0A9FQSAAABAHsAAQABAgA6MAAAAQAAAAAACAAQAAAA0g7TDlEQexF8EVgS0xICEwMT8wEBbzswAAABAAAJAAQAPwkBAHcPAwC0DwEAVhIBAFOAA7BgEecAAW1ucgEAADswAAABAAAGAAIA+xEAAF0SBQBDgAOwQBLHAAGgIAAADGVlcnMjAAKwMBI5AAFjcvImEgAAAQABgQKwgBGgAAGhAAAAEtJjZgGAAqAAAAAPbaAAAAAPbjQ28wIBdgFsDAAA8wABZAEACQAA1/4RAJGwoBKAAAGwcBCVAAGwgBGDAAGgsAAAEbKwAAkdAAGd3W86MAAAAQAAAAAAFQAQAAAAbQEKBQsFDAUNBbcFuAW5BRAHEQcaBxsHHAe4C+QOJBCFEIYQQRFCEUMRuRI=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0a2079f1a35f.js b/pr/234/docs/search.index/0a2079f1a35f.js new file mode 100644 index 000000000..fb3e137f6 --- /dev/null +++ b/pr/234/docs/search.index/0a2079f1a35f.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA7IoDAAABJBYXLzAAF0AYQHAAABBAGgQAAAEsBl8t8SAAABAAEFADswAAABAAALAAUAPw8FAFwRAQBoEgEAsxIAANISAADjAISgMAAADsaFAAI7MAAAAQAACwAFANkFAgDUBgQA2QsBAEoQAABsEQAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/0b5dc05327dc.js b/pr/234/docs/search.index/0b5dc05327dc.js new file mode 100644 index 000000000..6f65fda26 --- /dev/null +++ b/pr/234/docs/search.index/0b5dc05327dc.js @@ -0,0 +1 @@ +rn_("AQUA9SwPAAABAAEADgIBAAEEAaAAAAAPsXP1aAwAAAEAAQDAAgEAAQIA9WoQAAABAAEAAQABAAGAArBgEZwAAaAgAAAP0W95AQABoMAAABKaafTmEQAABwASABsAAQYA9PwSAAABAAEAAQABAgGgQAAAEcFyOzAAAAEAAAcAAgDPDwEAUhAFABOAArAgEFgAAWFvZwMAAIawgBHAAE2woBImAAEMMAY7MAAAAQAACAADAKQLAAB9EAMARBEDAPOCAmVpRwEAAIWwcBIzAAGggAAAEwqgQAAADuSwQAT05ggAAO8FAQAwAwGBAqAgAAAPzbBQEU8AAWFvAQQBoAAAABJMcwHMDwAAYwQDsFAQMAABY2Vz9uwIAAABAAEAKgABACEIAQAAOzAAAAEAACQACABNBwcALA8CAFsQDQCCEAIAPBEBAKgRAQDwEQEAehICAAEAArAQC5IAAbAwErEAAWVw8qcPAAABAAEAA7BAEoIAAaFAAAAS46BQAAAQoWJjZgEdBwAA44CEsUATBwABAjAQAQEDoLAAABJBsEAPjgABoGAAABBRY2V09AQHAAA0CBUC6QABAwA7MAAAAQAABQABAGYRBQABAAGgoAAAEqZy8rgPAAABAPOBAmdzAQcA9d0RAAABAAEAAQABAAGAAqBAAAAQKLBQEBMAAWNp84ACYW8BAAGgIAAAEEFy87cLAAD5Aj0BE4ACgAWqABuHcngBAwKwQBGiAAGgMAAAEe9ld/JmDwAArAATgwKgcAAAEh1nbwECAPWIEQAAAQDRAHIAAQAThgKwMBGvASZpcAECAPQHDwAAAQABAAEAI4ACoGAAABE4YmP3vwAAjKCQAAAROTvJGjowAAABAAAAAAAXABAAAADHAdUB1gEFBAYEBwQkB1oHhQeGB4cHiAeJB4oHjQvxC20MJQ82DzcP5Q9aEPES8hI=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0ba626d32cbc.js b/pr/234/docs/search.index/0ba626d32cbc.js new file mode 100644 index 000000000..36b0c97a3 --- /dev/null +++ b/pr/234/docs/search.index/0ba626d32cbc.js @@ -0,0 +1 @@ +rn_("AQIBoEAAABHBcjswAAABAAAHAAIAzw8BAFIQBQATAISwMBJbAAGwIBBYAAGwIBIiAAEDQAg6MAAAAQAAAAAADQAQAAAAVwdYB1kHfg9/DwUQBhCtEa4RvRHkEeURIBIhEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/0baf5c0b970e.js b/pr/234/docs/search.index/0baf5c0b970e.js new file mode 100644 index 000000000..61247e949 --- /dev/null +++ b/pr/234/docs/search.index/0baf5c0b970e.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAcAAgD8CwUAIRABAAEAAbBQEp0AAXIBEgUAAFMBhLBAEVEAAbBAEtYAAZgABDowAAABAAAAAAAnABAAAAAOBA8EEATOBfUI9ggaCUgJRgzEDigPKQ81D64Prw+wD+8PNxGGEYcRiBGJEaoRuBG5EeIR4xFCEkMSRxJIEkkSWRJaErUSthLMEs0S7xLwEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/0c04c5c572cf.js b/pr/234/docs/search.index/0c04c5c572cf.js new file mode 100644 index 000000000..00fc30ff3 --- /dev/null +++ b/pr/234/docs/search.index/0c04c5c572cf.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABLaZPJrBwAA4AEBBgGgMAAAEKJ0ARwSAAABAwGwUBGBAAFl9PoIAADuAgEAcgMBAQD04wUAAAEAAQABAAEBAaEAAAASz2Ty4gUAABUClwcAAIewoBF/AAGg4AAAEqYegArz5QMAAAEAAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0c918467df3c.js b/pr/234/docs/search.index/0c918467df3c.js new file mode 100644 index 000000000..9afe932cf --- /dev/null +++ b/pr/234/docs/search.index/0c918467df3c.js @@ -0,0 +1 @@ +rn_("AQABoLAAABI1YvXSEQAAwwABAEYAAQABgAKgIAAADsigMAAAEkFhcvMAAXQBhAcAAAEEAaBAAAASwGXy3xIAAAEAAQUAOzAAAAEAAAsABQA/DwUAXBEBAGgSAQCzEgAA0hIAAOMBhKAwAAAOxoUAAjswAAABAAALAAUA2QUCANQGBADZCwEAShAAAGwRAABjgAOwAA7gAvFkbnI=") \ No newline at end of file diff --git a/pr/234/docs/search.index/0caf669e19ba.js b/pr/234/docs/search.index/0caf669e19ba.js new file mode 100644 index 000000000..e1faa22ee --- /dev/null +++ b/pr/234/docs/search.index/0caf669e19ba.js @@ -0,0 +1 @@ +rn_("AQUBsEARVgABZfJxEQAAAQABggKgkAAAEpOgkAAAEpQyMwEAAqAgAAARN6AgAAAI5GltOzAAAAEAAAwABgDtBAAAIgUFAEoJAACuDgEAcBIAAIISAQDzAAFzAfoPAAABBQA7MAAAAQAABQABAAASBQABAAD19wsAAAEAAQABAAEA84ACZ2tzgISwsBJWAAERSAABBgGgQAAAEKhl824MAAB1AwEAAYACoJAAABJlsFAQIQABZ3oBgAKgEAAAC42gcAAAEfVucuMAhKBQAAASvRQBBDswAAABAAARAAQAvgEBAJAPCAB1EAMAUxECAAGFArBwEn0AAbAwEeIAAWVzEwADsEAOzQABoGAAABBLYmVzOzAAAAEAABAAAwDeBgoAOQ8BAIEPAwABAwD0Ug8AAAEABAABAAELAaBAAAASwGXy3xIAAAEA84ACZHLzAAJlbzswAAABAAAHAAIAoQcDAOIHAwABAgA7MAAAAQAACAABAIQLCAABAAD0hQ8AAAEAAQABAAEBADswAAABAADUAAIA9gUAAAgI0wBjAAOwEBGkAAFlaXM6MAAAAQAAAAAADAAQAAAA9AgpCSoJKwksCaILowulDqYOzA7RD/wR/RHjgISwIAdlAAERQAQBAYSwMAuWAAGgQAAADEOggAAAECSwQAxEAAEAAB0BygEAAAEFAPVHEgAAAQABAGwAAQATgAKgAAAABw9hZwEDAPQwEQAAAQA9AQEA84ACaW8BAQA7MAAAAQAA1AACAHwHAACwCtMAAQgBsEASwQABZfWJEgAAAQABAAEAAQDzgQJndAEBAaBwAAAQrmk7MAAAAQAA5AACAHsHAADMCeMAAQUA858PAAABAAEA84ACYW8TgAKgQAAAC7ZydQGAAqAAAAAHZ6AQAAAI82R4EwACsCAMZgABZXLyBQ8AAAEAE4ACsLATBAABcngBCgD0CRIAAAEAAQABABOAArAQD98B7WFlAQQA9IEPAAABAAEAAQABBAA6MAAAAQAAAAAABwAQAAAAthG3EcYRxxE3EjgSShJLEgEEAPTVDwAAAQABAAEAAQMA82ARAAABAJMA84CEAUgB598BAI6ggAAAEWKwQAveAAGxMBLcAAE9qR87MAAAAQAASgAhAGIAAABkAAAADgUBABcFAAC6BQEASwcBAFUHAQBdBwAAYQcBACMJAAA2CQcATAkBAJULAAClCwEAqgsBALsLBwDeCwEA9QsBADoMAQDXDgEAqg8BALYPAQDODwAA5w8CAPUPAQAHEAQAKBAAAG8QAwClEAEAmBEBAJwRAQDBEQAAABMBAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/0d537b6a73b7.js b/pr/234/docs/search.index/0d537b6a73b7.js new file mode 100644 index 000000000..6fab0c660 --- /dev/null +++ b/pr/234/docs/search.index/0d537b6a73b7.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABHmZPIHBwAAAQABgQKgMAAAEs6gUAAAEmVjcwEEA7AgEBUAAaBQAAAQp6BAAAASmmxzdvKTEgAAAQDzAANhZW/yDAQAAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/0e480fb4ebc7.js b/pr/234/docs/search.index/0e480fb4ebc7.js new file mode 100644 index 000000000..0d90258f4 --- /dev/null +++ b/pr/234/docs/search.index/0e480fb4ebc7.js @@ -0,0 +1 @@ +rn_("AQMBsEAQJgABZQHFDQAAAQUAOzAAAAEAAAYAAQBFDwYAAQMA9EcMAAABAAEAAQDzAAJwczowAAABAAAAAAANABAAAADcBd0F2Qb5CNsL5wvVD9YP1w/YD0sRTBGWEZcRMwADsFAOyQCyZHByOjAAAAEAAAAAAAsAEAAAAPEEnwWgBaEFogW2DpkQmhBIEUkRbxFwEQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/0f7753ba0d50.js b/pr/234/docs/search.index/0f7753ba0d50.js new file mode 100644 index 000000000..6ba7059a8 --- /dev/null +++ b/pr/234/docs/search.index/0f7753ba0d50.js @@ -0,0 +1 @@ +rn_("AQABsIASmwABcPKXEgAAAQBDBQOgIAAAECWgQAAAEUpkZXPz5Q4AAN8CAQABAgA7MAAAAQAAEwABAA4PEwATgYSgUAAAD92gEAAAC6ygIAAADF0cQAAzgAOwUBH2AAFhZm4=") \ No newline at end of file diff --git a/pr/234/docs/search.index/10355c6b286e.js b/pr/234/docs/search.index/10355c6b286e.js new file mode 100644 index 000000000..79e225730 --- /dev/null +++ b/pr/234/docs/search.index/10355c6b286e.js @@ -0,0 +1 @@ +rn_("AQYA9CgSAAABAAEAAQABDwD0/BIAAAEAAQABAAEAAbDwEwQAAWv0fxIAAAUAAQAiAOcAAACFsCAP8QABsQAS8QABiTAA8mQRAAABABOAArAQC9YAAWVvAQUAOzAAAAEAAA0AAQBbEA0AAQIA8/IOAAABAAEAAQIA9O4OAAABAAEAAQABAwA7MAAAAQAACAABAJAPCAABgAKgAAAAC6ywMBLBAAFxcgGAAqBAAAAP3bDAErcAAWVzAQEAOzAAAAEAABMAAQAODxMAI4ACsJASLwABaW13NgAAiqBQAAAShrBwEm4AAaAgAAAMXZxVETswAAABAAAJAAMAYwcAALwPAwCJEgQAAQIBsDAQOQABcAE5EQAAs4CEoCAAABCYAigE") \ No newline at end of file diff --git a/pr/234/docs/search.index/1180cfa8fde3.js b/pr/234/docs/search.index/1180cfa8fde3.js new file mode 100644 index 000000000..64ad1b2a2 --- /dev/null +++ b/pr/234/docs/search.index/1180cfa8fde3.js @@ -0,0 +1 @@ +rn_("AQcA9LIQAAABAAEAAQAjAAKgcAAAELFhcgF/BwAAAQAA9AcTAAABAAoAAQABAAA7MAAAAQAABgACAPsRAABdEgUAI4gCsEASxwABZXMBDQGwQBLBAAFl9YkSAAABAAEAAQABAAEFADswAAABAAAIAAMAqxACAIoRBACIEgAA1wMAAIYBm9jyKeQUhAb1EAUAAAEALAcBAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/11b8f016baed.js b/pr/234/docs/search.index/11b8f016baed.js new file mode 100644 index 000000000..77d377d62 --- /dev/null +++ b/pr/234/docs/search.index/11b8f016baed.js @@ -0,0 +1 @@ +rn_("AYACsHARMAABsCATBAABaW8BAAGw8BMEAAFr9H8SAAAFAAEAIgDzBQFk8mQRAAABAAECAPOuDwAAAQABAPMAAWn2jQ8AAO0BywABAC4AAQAjgwKwsBLkAAFhcwGBArCgEcIAAaBAAAAPWW9284CEAVAB") \ No newline at end of file diff --git a/pr/234/docs/search.index/12171281f9bb.js b/pr/234/docs/search.index/12171281f9bb.js new file mode 100644 index 000000000..81bc9ea0a --- /dev/null +++ b/pr/234/docs/search.index/12171281f9bb.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABBHc/W4CwAAbAQdAQEAAQATggKgIAAADHVlcgGBAqAQAAALt7AgD7gAAWVpE4ACoPAAABKHbnIzAAOwMAkOAAFlb3QBlBAAAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/12df68ee6610.js b/pr/234/docs/search.index/12df68ee6610.js new file mode 100644 index 000000000..94cde2aea --- /dev/null +++ b/pr/234/docs/search.index/12df68ee6610.js @@ -0,0 +1 @@ +rn_("AQUA9NUPAAABAAEAAQATgAKgsAAAEmVjcwEEAPO5DgAAAQABAAGAAqBQAAAPqaBgAAAQDmlyAYACoCAAAAvxoGAAABANZWgBAQGgIAAAEAxi8+0LAAABAAEAE4ECoBAAAAvwYWMBgAKgEAAAEjGwMAx5AAFlcyMAAqBAAAASwGV38t8SAAABAAEGAPSYEQAAAQBnAQEAEwACsGARmgABZm3yUAwAAAEAAYACoLAAABJMsBAQeQABZGcBggKwoBHCAAGgQAAAD1lvdvOAA2ducgGAAqBAAAARk6AwAAAOtmhstx8AAImwQA63AAE0QFc7MAAAAQAAGAAEAMUBAADuAw0AhAsIAEEMAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/132765159445.js b/pr/234/docs/search.index/132765159445.js new file mode 100644 index 000000000..fb99c2bac --- /dev/null +++ b/pr/234/docs/search.index/132765159445.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQAUEwYA8wYBZfTKEgAAAQAtAAEAEwMDsEAS8QABoGAAABJ2YXJ1AVwPAAAjgAKAED0AEAFkcg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/132fa3273a2a.js b/pr/234/docs/search.index/132fa3273a2a.js new file mode 100644 index 000000000..97abaa2b5 --- /dev/null +++ b/pr/234/docs/search.index/132fa3273a2a.js @@ -0,0 +1 @@ +rn_("AQQBsAARpgABczswAAABAAALAAIAEhIHAPMSAwBHAAABhqCAAAAR+LEAEwAAAbBAEW0AAbCAEw4AAbBQEagAARUAFvTGDQAAAQABAAEAAQUA8+sOAAABAAEAY4CEsEAPZAABoAAAAAb1APAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/1404ee0d6cc3.js b/pr/234/docs/search.index/1404ee0d6cc3.js new file mode 100644 index 000000000..008aecc8c --- /dev/null +++ b/pr/234/docs/search.index/1404ee0d6cc3.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABLAZfLfEgAAAQABgAOgMAAAETmw4BKfAAGwkBLhAAFhY22HAQCAhbAQD/UAAbAQEBUAAaCgAAASzjEJAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/149132598270.js b/pr/234/docs/search.index/149132598270.js new file mode 100644 index 000000000..848508069 --- /dev/null +++ b/pr/234/docs/search.index/149132598270.js @@ -0,0 +1 @@ +rn_("AYECoCAAAA/NsFARTwABYW8BAwGgAAAAEkxzAcwPAAAjgAKgAAAAB/lydmMAA7BQEDAAAWNlczswAAABAAAIAAQA7AgCABgJAQBUDwIAOhEAAAcBAICFsFAP8wABoKAAABIasPASLwABsCAOpwABRCEE") \ No newline at end of file diff --git a/pr/234/docs/search.index/149b3bfa7d2f.js b/pr/234/docs/search.index/149b3bfa7d2f.js new file mode 100644 index 000000000..7ace1a96e --- /dev/null +++ b/pr/234/docs/search.index/149b3bfa7d2f.js @@ -0,0 +1 @@ +rn_("AQUA9LYRAAABAIAAAQDzAAFj8+cPAAABAAEAAQkA89gSAAABADQAAQ0BoKAAABMbd/ILEwAAAQABgQKwgBLmAAGgYAAAEwlkczcBAISFoEAAABK8sGASjgBNApAKI4ACoHAAABAkZHIBCwGgQAAAEsBl8t8SAAABABOAArAwD1cAAWRy8wIBZfTiBwAAAQABAAEAI4ACsCALlgABZXkBCgDzUxEAAAEAAQABCwA7MAAAAQAABwABABISBwABAgD0bBIAAAEAQgABALcAAICFsHARfwABsNATAAABYCAGAQYA81kRAAABAAEAAQMBsEAS1gABdPJCEgAAAQAjhQKgQAAAEnBicxMCAqBQAAAQNWJm9/EIAAABAEEAagYBAFUAAQDzgAJpcAEEAPNUDwAAAQABAPMAAXA7MAAAAQAACgABAPYGCgATgAKwcBIkAAFlafOAA2RtbgEAAPb5CAAA7gJkBQEASgABAPMCAWT2mRAAAAEArgABACYAAQAjgAKgkAAAErNpbwEAAbBAEeQAAWUBGAUAAOcVAACJoAAAAAv0oEAAAAxDsEAMRAABkEhdAcoBAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/14af5b4de5e7.js b/pr/234/docs/search.index/14af5b4de5e7.js new file mode 100644 index 000000000..e3670ce90 --- /dev/null +++ b/pr/234/docs/search.index/14af5b4de5e7.js @@ -0,0 +1 @@ +rn_("AQABoCAAAAxZbQG8BQAAAQQBsAARlgABZPJIEQAAAQAjhISwoBKxAAGwQBGUAAGwcBJmAAEVgAAzgAOw0BKfAAFhZWk=") \ No newline at end of file diff --git a/pr/234/docs/search.index/150ff4191cab.js b/pr/234/docs/search.index/150ff4191cab.js new file mode 100644 index 000000000..6f6ba8429 --- /dev/null +++ b/pr/234/docs/search.index/150ff4191cab.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABK5cwE4EQAAAQYA9AcTAAABAAoAAQDzAgFyAXcSAADzAwFl9F8PAAABAJgAAQAThAKwsBKPAAFhZWOAA6BQAAAS7mNldA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/156e94e5de34.js b/pr/234/docs/search.index/156e94e5de34.js new file mode 100644 index 000000000..442c00448 --- /dev/null +++ b/pr/234/docs/search.index/156e94e5de34.js @@ -0,0 +1 @@ +rn_("AQAA9CEJAAABAO0GAQABCQGggAAAEwpmAQYSAAABAgD1rg8AAAEAAQAIAgEA8wACaXX2jQ8AAO0BywABAC4AAQABCAD1oRIAAAEAAQABAAEA8wIBZvVyDwAAAQABAAEAAQATgAKwoBLkAAFydPMAAmFz858PAAABAAEA84ACdHY=") \ No newline at end of file diff --git a/pr/234/docs/search.index/161ff8529816.js b/pr/234/docs/search.index/161ff8529816.js new file mode 100644 index 000000000..159c02e1d --- /dev/null +++ b/pr/234/docs/search.index/161ff8529816.js @@ -0,0 +1 @@ +rn_("AQABsIASUgABdAH7CAAAAQkA81kRAAABAAEA84ACY2cBgwKgkAAAEpOgkAAAEpQyMwGAAqBQAAASvaCAAAAR9WNl8wACc3TySwcAAAEAAQABoLAAABI1YvXSEQAAwwABAEYAAQAjgAKgAAAAEdFkciOAAqBQAAASh2Nl84ACaXkBBgD1BxAAAAEAAQABAAEAAQEBsEASwQABZfWJEgAAAQABAAEAAQDzgIQpIAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/16c553f3af58.js b/pr/234/docs/search.index/16c553f3af58.js new file mode 100644 index 000000000..eb77c70a7 --- /dev/null +++ b/pr/234/docs/search.index/16c553f3af58.js @@ -0,0 +1 @@ +rn_("AYECoCAAABLusEAO6QABYnABCQD0xhEAAAEAgwABAAEMAPTzEgAAAQABAAEA84ACbnYBgAKwMAvZAAGgMAAAC9tpbwECAaAAAAASvXMBpxAAACOAAqBAAAAScWFl5wEAgIWwAAWsAAGQSAE=") \ No newline at end of file diff --git a/pr/234/docs/search.index/16c72901534d.js b/pr/234/docs/search.index/16c72901534d.js new file mode 100644 index 000000000..bbc6d55d1 --- /dev/null +++ b/pr/234/docs/search.index/16c72901534d.js @@ -0,0 +1 @@ +rn_("AQcA9g4SAAABAJkAUgAWAAEA8wEBaQE+CQAAAQAA9UEJAAABAAEAAQABAKcAAIDFMKjKCzRpsGASbgABoBAAAAkaAAIgig==") \ No newline at end of file diff --git a/pr/234/docs/search.index/170f27c7a8bd.js b/pr/234/docs/search.index/170f27c7a8bd.js new file mode 100644 index 000000000..c0f710f63 --- /dev/null +++ b/pr/234/docs/search.index/170f27c7a8bd.js @@ -0,0 +1 @@ +rn_("AQIBsEASJAABZfLaBgAAAQABBADzVA8AAAEAAQCDAISgAAAABPKwQBGgAAGxABJoAAE0gAA7MAAAAQAAIAAGANQBAADpBgsA9gYKAJ8OBQDdDwAA1REBAPOAAmVp") \ No newline at end of file diff --git a/pr/234/docs/search.index/18822e341642.js b/pr/234/docs/search.index/18822e341642.js new file mode 100644 index 000000000..f1ab07034 --- /dev/null +++ b/pr/234/docs/search.index/18822e341642.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABAA/CQEAdw8DALQPAQBWEgEAAQIBsEAS1gABdDswAAABAAANAAYAhhEDAOIRAQBCEgEAWRIBAMwSAQDvEgEAE4ACsDARrwEmaXABiAKwABJNAAGwABJPAAE0NhOBAqAwAAAQQ2RzAYACoCAAAAv0sGASfQABbnjjAISgQAAAEnASAAMB7g8AAPOAAnJ0") \ No newline at end of file diff --git a/pr/234/docs/search.index/1882ea84a648.js b/pr/234/docs/search.index/1882ea84a648.js new file mode 100644 index 000000000..b23a7d247 --- /dev/null +++ b/pr/234/docs/search.index/1882ea84a648.js @@ -0,0 +1 @@ +rn_("ARMA9RYTAAABAAEAAQABABOAAqDAAAARyGFvAQICoLAAABKzoLAAABK0Y3Pzow8AAAEAUwABgQKwUBCjAAGgwAAAEoZoafOAAmVpI4ACoLAAABH6aXQBBgGwgBK6AAF2OzAAAAEAAAgAAwCHEAQAGxIAACwSAgB3AAAAhqCgAAASHKCQAAAR7rBQD2IAAQXgAvInBwAAAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/1a37a0ff7662.js b/pr/234/docs/search.index/1a37a0ff7662.js new file mode 100644 index 000000000..4e8191e1b --- /dev/null +++ b/pr/234/docs/search.index/1a37a0ff7662.js @@ -0,0 +1 @@ +rn_("AYECoGAAABG9sAARNQABYmkBAAD03g4AAAEA2wDoACOBArBQEc8AAWVzE4ACsAAHXgHGYXIBiQKwIBLMAAGgMAAAEtVpcAGFArAwElsAAbAgEiIAAWJ1I4ACsCARfQABaWy3AQAAhaAgAAARqhIBDDowAAABAAAAAAAWABAAAAB4BwIIAwgECAUIoAvNC84LzwvQCwQPsQ/pEeoRHBKOEtgS2RLbEuYS5xIJEw0T") \ No newline at end of file diff --git a/pr/234/docs/search.index/1a46991231d2.js b/pr/234/docs/search.index/1a46991231d2.js new file mode 100644 index 000000000..2ba0742e8 --- /dev/null +++ b/pr/234/docs/search.index/1a46991231d2.js @@ -0,0 +1 @@ +rn_("AQEA9S0JAAABAAEAAQABAPMAAWnzWgwAAKoGAQABBAKgIAAAEJSgMAAAEUBiaAHDDgAAo4AEoBAAAAxbsBAS8QABM2ducA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/1a8751cccd15.js b/pr/234/docs/search.index/1a8751cccd15.js new file mode 100644 index 000000000..33d36fd63 --- /dev/null +++ b/pr/234/docs/search.index/1a8751cccd15.js @@ -0,0 +1 @@ +rn_("AQsA9BASAAABAA0AAQABBAGwQBHTAAFlATsRAADzAAF0AbwSAAABDQDz0xIAAC8AAQABEADz+hIAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAPsRAABdEgUAI4YCsEASxwABZXMBBAD0mw8AAAEAFgABAPOAA2NydDOAhLBgD7YAAaAQAAAF6AEICQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/1acf55718c6c.js b/pr/234/docs/search.index/1acf55718c6c.js new file mode 100644 index 000000000..89f379182 --- /dev/null +++ b/pr/234/docs/search.index/1acf55718c6c.js @@ -0,0 +1 @@ +rn_("AQYA9FISAAABAEwAAQAjgAKhEAAAEvtjchOAAqAgAAAI9Gh5AQEBoGAAABLuc/KSEAAAAQAjgAKwIAlSAAFldQGAArAgDwwAAaCQAAASpmFlAQcBsEASwQABZfWJEgAAAQABAAEAAQABBAGwABGmAAFzOzAAAAEAAAsAAgASEgcA8xIDAEcAAASGoIAAABH4sQATAAABsEARbQABsIATDgABsFARqAABFQAW9MYNAAABAAEAAQCDAISgAAAAEnGwQBMEAAGwMBIkAAFIAAP1CQUAAGoLAQAFAAEAAQMBsEAS1gABdPJCEgAAAQAjhQKgQAAAEnBicwEBAPSOEAAAAQABAAEAE4ACoBAAAAkTZGy36AAAjKDQAAAR/qBQAAAQNaCQAAASGrBQEZEAAaAwAAAL7LeYBzswAAABAABSABAAwAEBAOsDAgD1BQAA2gYBAHoHAADxCAEA+wgAAAYJAAAzCQAAAww2AEwMAACdDwEAuA8BAPMPAQBqEAQATxEBAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/1c0669362394.js b/pr/234/docs/search.index/1c0669362394.js new file mode 100644 index 000000000..b6c94f988 --- /dev/null +++ b/pr/234/docs/search.index/1c0669362394.js @@ -0,0 +1 @@ +rn_("AQAA9N4OAAABANsA6AAjggKwUBHPAAFlcwGFArAwElsAAbAgEiIAAWJ1I4ACsCARfQABaWwzAAOwIBE1AAFidHU6MAAAAQAAAAAADwAQAAAAeAcCCAMIBAgFCAQPsQ8cEo4S2BLZEtsS5hLnEgkTDRMBDADzehIAAAEAAQABAwGwYBIgAAFi8jMRAAABABcGAACHsEAO3AABoHAAABE/sBAJAgABsJARzQABOAkDOjAAAAEAAAAAABwAEAAAAPQFQwdEB2IMYwxkDGgMaQxqDCYPJw8qDysPRQ9GD0cPSA9JD0oPSw+AD6wPrQ8dEkESzxLaEvcSBhM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/1c7b86fac1f5.js b/pr/234/docs/search.index/1c7b86fac1f5.js new file mode 100644 index 000000000..504982314 --- /dev/null +++ b/pr/234/docs/search.index/1c7b86fac1f5.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA69oDAAABF5ZXIBAAKgIAAAEf6wMBCfAAFpdPJ/EQAAAQABAwA7MAAAAQAABgABACkQBgABCAD0YxIAAAEAegAFABMBA7CQEr4AAbBQEbwBP2dscDswAAABAAAKAAUATw8CAKsRAQBUEgEAkRIBANASAQABgAKwMBCbAAGwMBCdAAFydgEEAbAAEUsAAWT0mRAAAAEA1QABALcFAAGHsFARTQDpsGARugABlAkDOzAAAAEAAAoABQBNDAIAyw0BANkPAABNEAMAshEAACOAAqAQAAAMTG5vAQMA80wPAAABAAEAI4ACoAAAAAufZHJjAYSgUAAAEEqgMAAAD3wUARDynwcAAAEAE4ADsGARZAABsAAOpwABY2dyVwAAAIWgsAAAEn+gQAAADcqgIAAAEGkQwAoBqQ4AAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/1d9d469499b8.js b/pr/234/docs/search.index/1d9d469499b8.js new file mode 100644 index 000000000..23874c39a --- /dev/null +++ b/pr/234/docs/search.index/1d9d469499b8.js @@ -0,0 +1 @@ +rn_("AQABsGASdAABZQGmEgAAAQYA9LQRAAABACwBAQABBAD0zREAAAEA8AABAPMAA2VyczowAAABAAAAAAAJABAAAAALCQwJDQkOEEIQMBExET4RbhJvEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/1e65bf627061.js b/pr/234/docs/search.index/1e65bf627061.js new file mode 100644 index 000000000..676e76c6f --- /dev/null +++ b/pr/234/docs/search.index/1e65bf627061.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABMKZgEGEgAAAQgA9aESAAABAAEAAQABAPMDAWb1cg8AAAEAAQABAAEA84MCYXMBAgKgIAAAEJSgMAAAEUBiaAHDDgAAI4ACoIAAABHIYXDzAAJobQH2BAAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/201202f6eb90.js b/pr/234/docs/search.index/201202f6eb90.js new file mode 100644 index 000000000..4394d47c1 --- /dev/null +++ b/pr/234/docs/search.index/201202f6eb90.js @@ -0,0 +1 @@ +rn_("AQgA9aESAAABAAEAAQABAPMAAWY7MAAAAQAABQACALIBAAByDwQAAQIAOzAAAAEAAAsAAgA7EgUA6BIFAPMGAW07MAAAAQAADQAEAEIMAAAyEAAAcxEFAKkSBQBjgAOwAAkJAAFlbXM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/20292df8ad95.js b/pr/234/docs/search.index/20292df8ad95.js new file mode 100644 index 000000000..c94b08a18 --- /dev/null +++ b/pr/234/docs/search.index/20292df8ad95.js @@ -0,0 +1 @@ +rn_("AYEDsGAQjAABoAAAABAjsSAS+AABYWRuARQA9RYTAAABAAEAAQABAAEGAbCAEroAAXY7MAAAAQAACAADAIcQBAAbEgAALBICAFcAAAGGoMAAABH6oKAAABIcoJAAABHusFAPYgABBeAC8icHAAABAAEGAPNgEQAAAQCTAAEAAaBgAAAO23IBEQQAANcBAIDFoDAAAAueAACKCg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/20f24dd90959.js b/pr/234/docs/search.index/20f24dd90959.js new file mode 100644 index 000000000..907f4ad6d --- /dev/null +++ b/pr/234/docs/search.index/20f24dd90959.js @@ -0,0 +1 @@ +rn_("AYICsCAQDwABsDAQewABZXABBQD01w4AAAEA0gABAFOAA7CQEKUAAWFpcgGBArCgEoAAAbCgEoIAAWNyxwAAAIWwUAxgAAGgIAAACVCgIAAAC6c8AAE6MAAAAQAAAAAACgAQAAAAdgF3AfwDDwcHCQgJXQ9eD8QPxQ+2EA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2196a484188a.js b/pr/234/docs/search.index/2196a484188a.js new file mode 100644 index 000000000..7a8075a88 --- /dev/null +++ b/pr/234/docs/search.index/2196a484188a.js @@ -0,0 +1 @@ +rn_("AQQA9H4PAAABAKECAQABAAD06QgAAAEAWgnXABcAAACHsEAP4AABsGASxQABoBAAAAwCsFASagABsDAQOQABsSAS5AABOMECOjAAAAEAAAAAAAoAEAAAAPwI/QgRCRIJmAuZC5oLSww5EXgSeRLzgAJlbw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/22338a595bbb.js b/pr/234/docs/search.index/22338a595bbb.js new file mode 100644 index 000000000..64bd899db --- /dev/null +++ b/pr/234/docs/search.index/22338a595bbb.js @@ -0,0 +1 @@ +rn_("AYQCoCAAAA/voDAAABBMaXQBAQA7MAAAAQAAdgACAHkHAABWCXUAYwADsAAB1QABZGVs9r0FAAABAAEATgr6AQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/2334fd1256bd.js b/pr/234/docs/search.index/2334fd1256bd.js new file mode 100644 index 000000000..20cd03061 --- /dev/null +++ b/pr/234/docs/search.index/2334fd1256bd.js @@ -0,0 +1 @@ +rn_("AYQCsCAQQgD8oJAAABKIbXABgAKgMAAAD92gUAAAEjVjcpcAAACFsCASBwABsMAStwABoGAAABCXEEAOOzAAAAEAABIABgAZBQgAlRABAIMRAQBSEgEAnxIBAA4TAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/243791893159.js b/pr/234/docs/search.index/243791893159.js new file mode 100644 index 000000000..95503f7e3 --- /dev/null +++ b/pr/234/docs/search.index/243791893159.js @@ -0,0 +1 @@ +rn_("AQcA9UcSAAABAAEAbAABAAEHAPOfDwAAAQABAAGAAqAAAAAHZ6AQAAAI82R48wEBZfIFDwAAAQBzAISwQAveAAEwCAH0FwUAAEYCmAgBAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/247e576b2cc4.js b/pr/234/docs/search.index/247e576b2cc4.js new file mode 100644 index 000000000..3231fa05a --- /dev/null +++ b/pr/234/docs/search.index/247e576b2cc4.js @@ -0,0 +1 @@ +rn_("AQYA9AcTAAABAAoAAQDzAgFyAXcSAADzAgFl9F8PAAABAJgAAQATgAKgUAAAEYVkdBMAA7CwEo8AAbBAEIUAAWFlcvfCDwAAAQBgABoAAQABAA8B") \ No newline at end of file diff --git a/pr/234/docs/search.index/2574b34a8660.js b/pr/234/docs/search.index/2574b34a8660.js new file mode 100644 index 000000000..2ceb82550 --- /dev/null +++ b/pr/234/docs/search.index/2574b34a8660.js @@ -0,0 +1 @@ +rn_("AYACsAAPDAABsCAL4AABZWkBiQKwIBLMAAGgMAAAEtVpcBMAA6AgAAARqqCAAAARvWVpdfWgCwAALQABAAEAAQABhgKwkBLvAAGgAAAAEU5iZAGAArAwEaIAAaAwAAAPy3J2cwCEoDAAABHvEAATOjAAAAEAAAAAAAgAEAAAAOcIFQkWCacLYQ9mDxEQEhCFEWOAA6AQAAAI3GRtdA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/27441501e1c3.js b/pr/234/docs/search.index/27441501e1c3.js new file mode 100644 index 000000000..00925f0b6 --- /dev/null +++ b/pr/234/docs/search.index/27441501e1c3.js @@ -0,0 +1 @@ +rn_("AQAA9CIPAACEAgEApQDzAAFzOzAAAAEAABkACAAEBQIArgsCANwOAQCODwEAyw8BAJ0QAQASEgcA8xIDACOAArEQEvgAAWF0") \ No newline at end of file diff --git a/pr/234/docs/search.index/27797ee166ea.js b/pr/234/docs/search.index/27797ee166ea.js new file mode 100644 index 000000000..fad5266cf --- /dev/null +++ b/pr/234/docs/search.index/27797ee166ea.js @@ -0,0 +1 @@ +rn_("AQIA9ZsQAAABAPgAAQA0AQEBAPfqDwAAAQABAHcCAQB6AAUAAQIA9IkPAAABAAEAAQATgAKg0AAAEs5jc/OAAmVvI4ACsAAHXwABbHTzAAJhcvKnDgAAAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/27f6cb7cd2c6.js b/pr/234/docs/search.index/27f6cb7cd2c6.js new file mode 100644 index 000000000..7432bfbb1 --- /dev/null +++ b/pr/234/docs/search.index/27f6cb7cd2c6.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABAABQDfBQAAtgsAAFYMAAD1DgwANRAAAAcEAACHoCAAABCUoDAAABANoOAAABIaoDAAABFAsHAO1QABoCAAAAXoysAIOzAAAAEAAPcAFQB5AQAA1wUAAOgFAADkCAAA6AgAAAQJAQCdCwAA0QsBAPILAQBZDAAAewzTAMMOAADSDgEAqQ8AAFEQAAB7EQEAOxIFAFgSAADTEgAA6BIFAAITAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/2849bc9335e1.js b/pr/234/docs/search.index/2849bc9335e1.js new file mode 100644 index 000000000..39a99fd6c --- /dev/null +++ b/pr/234/docs/search.index/2849bc9335e1.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAcAAgD8CwUAIRABAAECAPM9EAAAAQABAAEGAPT8EgAAAQABAAEA8wYBbfREEQAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/285e4fa908fe.js b/pr/234/docs/search.index/285e4fa908fe.js new file mode 100644 index 000000000..8295ab988 --- /dev/null +++ b/pr/234/docs/search.index/285e4fa908fe.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwCCEAIA8BEBAHoSAgABBwD2DhIAAAEAmQBSABYAAQABAAKgYAAAEYWwQBCFAAFhcvPCDwAAAQCLAQGAAqEQAAAS97CAEu8AAWl1dwgAAIixUBMSAAGACP4AFqOwcBJFAAGhMAAAEwYKQRvyPgkAAD8GAYECoFAAAA/foKAAABHMa3ABAwA7MAAAAQAA0wACAFIMAQDNDdEA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/295d133206ed.js b/pr/234/docs/search.index/295d133206ed.js new file mode 100644 index 000000000..f4024ec2d --- /dev/null +++ b/pr/234/docs/search.index/295d133206ed.js @@ -0,0 +1 @@ +rn_("AQwA8ywSAAABAAEAAQYA8zYQAAABAAEAAYICsGARxAABsNASwwABaXITgAKwEAxXAAFlaZcBAIOHsTAS3wABoFAAAA/UsJARngABsEAP0gABlBAH8wABYfQhCQAAAQDtBgEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/299e6d3a1674.js b/pr/234/docs/search.index/299e6d3a1674.js new file mode 100644 index 000000000..3ab12c11e --- /dev/null +++ b/pr/234/docs/search.index/299e6d3a1674.js @@ -0,0 +1 @@ +rn_("AQADsIASeAABsFAR6wABsEAR0wABYWJlOjAAAAEAAAAAAAkAEAAAAEEHQgdeDF8MwA4jD2IPYw87Ee4RAQADsCAQFQABoFAAABCnoEAAABKabHN28pMSAAABAAECAPSOEAAAAQABAAEAE4QCsCAQkgABYXPzgAJhZfOAAnJzAQEAOzAAAAEAAAYAAQAUEwYA8wYBZfTKEgAAAQAtAAEAEwcDsEAS8QABoGAAABJ2YXJ1AVwPAAAjgAKgEAAAC6FlcgEAADswAAABAAAeAA4AEwUBAMAFCAAOCQEAsQsBAFQMAQDKDQAAsw4CAMgOAADeDgEAug8BAM0PAABMEAAAnxABAKIQAAABAQA6MAAAAQAAAAAACgAQAAAA1gvXC2QPZQ/qD+sP7A9jEmQS3hLjEgEFAPRsEgAAAQBCAAEA8wQBY/KPEQAAAQABAwGgQAAAEpll8sMSAAABAAEEAPOUEQAAAQA0AQEEAbAAEZYAAWTySBEAAAEAZxIAAYmwoBKxAAGgUAAAEWOgMAAAD/CwYBLkAAGwcBJmAAG1yAQ6MAAAAQAAAAAACgAQAAAA6wgXCeAL4QvrDuwO7Q6XEMoRyxGHEhGBAg+wIBCSAAFhcwECAPNZEQAAAQABAHOABKAQAAAPfWJub3YBAQOwgBJ4AAGwUBHrAAGwQBHTAAFhYmX1XgwAAAEAxAIYArMAAQAA9bELAAABAAEDAQABAPOCAnN1IwADsGAQBQABoKAAABMbYnR39bIFAAABAAkNTwABAAGAArEAEuEAAbBAEAMAAW9wI4EDoKAAABIbsPAS0AABbXN0AQQBoKAAABMbd/ILEwAAAQABgAKgcAAAEqewwBKRAAFndxOAArCwEoQAAWVvAQ0A89MSAAAvAAEAARAA8/oSAAAWAAEAAQEBoGAAABFYcvXpBQAAAQAOAgoHAQABAwGgAAAADyJz864LAAABAAEAAQMA860LAAB5BAEAAYACoEAAABJEsDARgQABYXPzgANucnYBgAKwcBB5AAGwkBGtAAFoaQGAArBQD6cAAbAgC+oAAWJj1+8AAI2gIAAAB2yg4AAAEnew0BJqAAEX9Qs6MAAAAQAAAAAAFwAQAAAAwgHDAcQB6APpA/oIqQvoC+kL7AtHDEgMSQxKDMUNWw85EDoQexB8ELESshLFEsYSAQsA9JUSAAABAEYAAQABgAKgsAAAEs6gMAAAD+1sbhMAArCAEpsAAWVw83YSAAAhAAEAAQMBsEASwQABZfWJEgAAAQABAAEAAQABBgD0KBIAAAEAAQABAAEPAPT8EgAAAQABAAEAAQIAOzAAAAEAAAUAAQDoEgUA8w0BbTswAAABAAAFAAEAqRIFAAEEAPVKEQAADAABAHgAAQABAAKgEAAAECWw8BMEAAFla/Z/EgAABQABACIAEwABAAECAPSJDwAAAQABAAEA58sAAIywIA/xAAGgUAAAD9+xABLxAAGgoAAAEcy9tAM7MAAAAQAAIgAMAI4LAwDlDgAADA8BAPsPBwBHEAAAZBEBAG0RAQBxEQEAxBEBAN0RBAChEgQAChMAAAECADswAAABAAALAAIAOxIFAOgSBQDzAQFtOzAAAAEAAA0ABABCDAAAMhAAAHMRBQCpEgUAEwECoDAAAAxGZXPyrxAAAAEAAYkCsCASzAABoDAAABLVaXATAAOgIAAAEaqggAAAEb1laXX1oAsAAC0AAQABAAEAAYYCsJAS7wABoAAAABFOYmQBgAKwMBGiAAGgMAAAD8tydnMDhKAwAAAR7xAAEzowAAABAAAAAAAHABAAAADnCBUJFglhD2YPERASEIURAQYA9H4PAAABAKECAQABBwD0CRIAAAEAAQABAHcAAACGoHAAABIdoEAAABHBoHAAAA/uQ0AFOzAAAAEAAAwABQDXAQAARgkBAM8PAQBSEAUAMxEBAPd3AICLoGAAAA/utUEP") \ No newline at end of file diff --git a/pr/234/docs/search.index/29beed7d2cc8.js b/pr/234/docs/search.index/29beed7d2cc8.js new file mode 100644 index 000000000..05838e72c --- /dev/null +++ b/pr/234/docs/search.index/29beed7d2cc8.js @@ -0,0 +1 @@ +rn_("AYADsAASTQABsAASTwABsHASugABNDZhAQQA9AcTAAABAAoAAQAjgQKgQAAAECNtcAEBADswAAABAAAIAAMAYhEAAMgRAAAUEwYAxwEAAMWgQAAADz6gYAAAEnFABYAIOzAAAAEAABEABwDiDgEAhxAGABsSAAAsEgIAdxIAAMoSAQD4EgEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/2a0d8ab46fb3.js b/pr/234/docs/search.index/2a0d8ab46fb3.js new file mode 100644 index 000000000..e31ccb300 --- /dev/null +++ b/pr/234/docs/search.index/2a0d8ab46fb3.js @@ -0,0 +1 @@ +rn_("AQABsKARXgABZTswAAABAAAJAAIA6QcHAJoRAQABAAGgIAAADFltAbwFAAABgAKwoBFcAAGwQA7PAAFsbwEEAbAAEZYAAWTySBEAAAEAIwKEsKASsQABsEARlAABsHASZgABFYAAAYcSAAABAAGwgBKbAAFw8pcSAAABAEMAA6AgAAAQJaBAAAARSmRlc/PlDgAA3wIBAPOBAmVp1wIAAIawIBEuAAGw0BKfAAEXAQL08wQAAAEAAQCuAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2a464ed978c7.js b/pr/234/docs/search.index/2a464ed978c7.js new file mode 100644 index 000000000..b85205ddf --- /dev/null +++ b/pr/234/docs/search.index/2a464ed978c7.js @@ -0,0 +1 @@ +rn_("AQACoCAAABH+sDAQnwABaXTyfxEAAAEAAQMAOzAAAAEAAAYAAQApEAYAAQgA9GMSAAABAHoABQATAQOwkBK+AAGwUBG8AT9nbHA7MAAAAQAACgAFAE8PAgCrEQEAVBIBAJESAQDQEgEAAYACsDAQmwABsDAQnQABcnYBBAGwABFLAAFk9JkQAAABANUAAQC3BQABh7BQEU0A6bBgEboAAZQJAzswAAABAAAKAAUATQwCAMsNAQDZDwAATRADALIRAAAjgAKgEAAADExubwEDAPNMDwAAAQABACOAAqAAAAALn2RyYwCEoFAAABBKoDAAAA98FAEQ8p8HAAABAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2a6c1f20350c.js b/pr/234/docs/search.index/2a6c1f20350c.js new file mode 100644 index 000000000..839743411 --- /dev/null +++ b/pr/234/docs/search.index/2a6c1f20350c.js @@ -0,0 +1 @@ +rn_("AYACsHARZAABoLAAABJ/ZW8jAYSwcBCvAAGgYAAAEfmAEfIAEOEAMAM7MAAAAQAAGgAGAHEHAQDdCAUAcQwBABcQCQBLEAAA1xEFAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2acf01fb4c07.js b/pr/234/docs/search.index/2acf01fb4c07.js new file mode 100644 index 000000000..f479d0edf --- /dev/null +++ b/pr/234/docs/search.index/2acf01fb4c07.js @@ -0,0 +1 @@ +rn_("AQMA9msMAABvAlUAAQBuAgEAAQQAOzAAAAEAAAoABABnDwUAXhEBALARAQC0EgAAAYYCoAAAABHRoAAAABHSZHLzAgF2AfURAADzgAJlbycBAADHoDAAAA7ZoHAAABE6oIAAABKHoNAAABIxoLAAABKHQEUICTswAAABAAAJAAMA8QUCAC8HBADcCwEAAQYA9AcTAAABAAoAAQDzAgFyAXcSAADzAwFl9F8PAAABAJgAAQATgAKwsBKPAAFhZfOAAmRyAQAAOjAAAAEAAAAAAAoAEAAAADQJNQkLD+cR6BEOEg8SqBL6EhATERMTgAKgMAAAD31sdMOABKAAAAAPbaAAAAAPbjQ2YWU=") \ No newline at end of file diff --git a/pr/234/docs/search.index/2b385adf4f14.js b/pr/234/docs/search.index/2b385adf4f14.js new file mode 100644 index 000000000..4a23ce3c6 --- /dev/null +++ b/pr/234/docs/search.index/2b385adf4f14.js @@ -0,0 +1 @@ +rn_("AQEDsIASeAABsFAR6wABsEAR0wABYWJl9V4MAAABAMQCGAKzAAEAAPWxCwAAAQABAwEAAQDzgAJzdSOBArCAEfYAAXJ0QwCEsGAQBQABsIARkQABoKAAABMbAgEU9bIFAAABAAkNTwABAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2c4d0eda72da.js b/pr/234/docs/search.index/2c4d0eda72da.js new file mode 100644 index 000000000..e2cda3260 --- /dev/null +++ b/pr/234/docs/search.index/2c4d0eda72da.js @@ -0,0 +1 @@ +rn_("AQABsPATBAABa/R/EgAABQABACIAEwACsQAS8QABZG7yZBEAAAEAE4ACsBAL1gABZW8BBQA7MAAAAQAADQABAFsQDQABAgDz8g4AAAEAAQABAgD07g4AAAEAAQABAAEDADswAAABAAAIAAEAkA8IAAGAAqBAAAAP3bDAErcAAWVzAQEAOzAAAAEAABMAAQAODxMAI4ACsJASLwABaW03NgAAiqAQAAALrKBQAAAShrBwEm4AAaAgAAAMXZxVEfVjBwAAWQgBAAEAAQDzgAJsdA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/2da70c21552d.js b/pr/234/docs/search.index/2da70c21552d.js new file mode 100644 index 000000000..220bd3816 --- /dev/null +++ b/pr/234/docs/search.index/2da70c21552d.js @@ -0,0 +1 @@ +rn_("AQEDsIASeAABsFAR6wABsEAR0wABYWJl9V4MAAABAMQCGAKzAAEAAPWxCwAAAQABAwEAAQDzgAJzdSOBArCAEfYAAXJ0AYACsHARkQABoBAAAAxGbnpjAISwYBAFAAGgoAAAExsCART1sgUAAAEACQ1PAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/2e6dfbf22b19.js b/pr/234/docs/search.index/2e6dfbf22b19.js new file mode 100644 index 000000000..0c9a4c3f7 --- /dev/null +++ b/pr/234/docs/search.index/2e6dfbf22b19.js @@ -0,0 +1 @@ +rn_("AQkA89gSAAABADQAAQ0BoKAAABMbd/ILEwAAAQABgQKwgBLmAAGgYAAAEwlkczcBAIWFoEAAABK8sGASjgBNApAK") \ No newline at end of file diff --git a/pr/234/docs/search.index/309b4ef99850.js b/pr/234/docs/search.index/309b4ef99850.js new file mode 100644 index 000000000..753167d6f --- /dev/null +++ b/pr/234/docs/search.index/309b4ef99850.js @@ -0,0 +1 @@ +rn_("AQEBsFAOzwABY/TzBAAAAQABAK4AAQABoIAAABMKZgEGEgAA8wYBc/JtDAAA7QMzAAOgYAAAEu5jaXPykhAAAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/30a8ca0b3469.js b/pr/234/docs/search.index/30a8ca0b3469.js new file mode 100644 index 000000000..976709990 --- /dev/null +++ b/pr/234/docs/search.index/30a8ca0b3469.js @@ -0,0 +1 @@ +rn_("AQIA9G8QAAABAAEAAQABCgDzLBIAAAEAAQDzgQJtdAEGAPM2EAAAAQABAAGCArBgEcQAAbDQEsMAAWlyE4ACsBAMVwABZWkBBQD0bBIAAAEAQgABAPMEAWPyjxEAAAEAI4ICoFAAABFjZnQBAAGwABK6AAFk9I4LAAABAAEAAQDzgQJlaTcDAACIsTAS3wABoFAAAA/UsJARngABsEAP0gABlRAHAZoSAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/319143b9dcad.js b/pr/234/docs/search.index/319143b9dcad.js new file mode 100644 index 000000000..e1c86df44 --- /dev/null +++ b/pr/234/docs/search.index/319143b9dcad.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQBFDwYAAQMA9EcMAAABAAEAAQBjAAOgkAAAEe1ocHM6MAAAAQAAAAAADwAQAAAA3AXdBdkG+QjbC+cL1Q/WD9cP2A9LEUwRlhGXEboRuxE=") \ No newline at end of file diff --git a/pr/234/docs/search.index/31ec884fea72.js b/pr/234/docs/search.index/31ec884fea72.js new file mode 100644 index 000000000..25b00428d --- /dev/null +++ b/pr/234/docs/search.index/31ec884fea72.js @@ -0,0 +1 @@ +rn_("AQABoJAAABHtaPK6EQAAAQDzAgFk8iUHAAABAAECADswAAABAAAFAAEA6BIFAPMMAW07MAAAAQAABQABAKkSBQABAgDz6AgAAOkCAQABigKwABJNAAGwABJPAAE0NiOBAqAwAAAL9GVyAYACsEAQpQABUBhKAKk8Y3W3BgAAh7AQDzsAAbBQEsUAARFRCfXsBAAACwCtAAEA0AY=") \ No newline at end of file diff --git a/pr/234/docs/search.index/322c33796bd9.js b/pr/234/docs/search.index/322c33796bd9.js new file mode 100644 index 000000000..d43065ed7 --- /dev/null +++ b/pr/234/docs/search.index/322c33796bd9.js @@ -0,0 +1 @@ +rn_("AQEA89UFAAAWDAEAAQAAOzAAAAEAAAcAAwDOAQEA6gsBAAcPAwABgwKwcBIHAAGwgBI5AAFjcwEFAPS2EQAAAQCAAAEA8wgBY/PnDwAAAQABAAEAAaAwAAAQR3T0mw8AAAEAFgABAAGAAqAAAAAPbaAAAAAPbjQ28wMBdgFsDAAAlwAAAIWwoBGgAAGwIAuqAAGwgBGDAAEJBAY6MAAAAQAAAAAACQAQAAAAGgcbBxwHuAvkDiQQQRFCEUMRuRIBAAKwMBJbAAGwIBIiAAFidTowAAABAAAAAAANABAAAABXB1gHWQd+D38PBRAGEK0RrhG9EeQR5REgEiESU4CEsQASnwABsAAHYQABCAhEAQgAOzAAAAEAAAUAAQBmEQUA1w0AAIiwQA/1AAGgIAAADHW5AAs7MAAAAQAACQACABIHBwDxDwEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/341a074166cf.js b/pr/234/docs/search.index/341a074166cf.js new file mode 100644 index 000000000..40f4546ef --- /dev/null +++ b/pr/234/docs/search.index/341a074166cf.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQAXEAkAI4ADsGAR9gABsDAOwQABaXN0AQgA9aESAAABAAEAAQABAPMBAWb1cg8AAAEAAQABAAEAY4ADoDAAABHBZWd0") \ No newline at end of file diff --git a/pr/234/docs/search.index/3439f6b04774.js b/pr/234/docs/search.index/3439f6b04774.js new file mode 100644 index 000000000..7941e11c2 --- /dev/null +++ b/pr/234/docs/search.index/3439f6b04774.js @@ -0,0 +1 @@ +rn_("AQUBsEARVgABZfJxEQAAAQABAAA7MAAAAQAAEQAEAL4BAQCQDwgAdRADAFMRAgABCgGwQBLBAAFl9YkSAAABAAEAAQABAEcGAACHsCAP3wHtsMATBAABoCAAAAcPsDAHZQABGIkKOjAAAAEAAAAAAA4AEAAAAFUHVgdhB2IHIwmlC6YLqgurC/UL9gs6DDsMnBGdEQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/37041e27a69e.js b/pr/234/docs/search.index/37041e27a69e.js new file mode 100644 index 000000000..ae11829a6 --- /dev/null +++ b/pr/234/docs/search.index/37041e27a69e.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQDoEgUA8w0BbTswAAABAAAFAAEAqRIFAEMAhLAAEroAAbBAEVYAAaAAAAASdjgAAjswAAABAAAYAAYAjgsDAPsPBwBxEQEA3REEAKESBAAKEwAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/3766bd3b524f.js b/pr/234/docs/search.index/3766bd3b524f.js new file mode 100644 index 000000000..24a18b6a6 --- /dev/null +++ b/pr/234/docs/search.index/3766bd3b524f.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABBHc/W4CwAAbAQdAQEAAQATggKgIAAADHVlciOAA6BAAAALt7AwCQ4AAWVvdAECAPQQEgAAAQANAAEAE4ACsEAStwABYnYTgAKwAAdGAAFveAEKAPVHEgAAAQABAGwAAQABgQKgIAAAD82wUBFPAAFhbwEEAaAAAAASTHMBzA8AAGMDA7BQEDAAAWNlc/bsCAAAAQABACoAAQAhCAEBAPRHDAAAAQABAAEAI4ACsDARvAE/cnT3AQAAhrAQB0gAAQHAB/KxAQAAuA4=") \ No newline at end of file diff --git a/pr/234/docs/search.index/37fbc343ca2b.js b/pr/234/docs/search.index/37fbc343ca2b.js new file mode 100644 index 000000000..f55f23cf6 --- /dev/null +++ b/pr/234/docs/search.index/37fbc343ca2b.js @@ -0,0 +1 @@ +rn_("AQQA9nMMAAABAL8CAQAHAQEAAQoA82ARAAABAJMAAQEA9wcJAAABAFUGAQBmAAEA8QAjgAKwYBJWAAFpbwECAPQ6DAAAAQBhBQEAAQUA9GwSAAABAEIAAQD3CAAAiLCQEiYAAaAgAAAMPLCQEKkAAQTcA/fLBQAAGgYBAOwDAQC8AQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/38bf81f092c9.js b/pr/234/docs/search.index/38bf81f092c9.js new file mode 100644 index 000000000..2b4620704 --- /dev/null +++ b/pr/234/docs/search.index/38bf81f092c9.js @@ -0,0 +1 @@ +rn_("AQgA9FQSAAABAHsAAQAjgAKwUBHAAE1mbgEBAbBAEaAAAWU7MAAAAQAADAACAOkGCwDdDwAAM4CEsFASBwABsBASnQABCGAB") \ No newline at end of file diff --git a/pr/234/docs/search.index/39f1ae6853b3.js b/pr/234/docs/search.index/39f1ae6853b3.js new file mode 100644 index 000000000..fd6b72000 --- /dev/null +++ b/pr/234/docs/search.index/39f1ae6853b3.js @@ -0,0 +1 @@ +rn_("AQYA9CgSAAABAAEAAQABDwD0/BIAAAEAAQABAPOHAmhtAQQBsAARlgABZPJIEQAAAQAjh4SwoBKxAAGwQBGUAAGwcBJmAAEVgAABAAGgQAAAD29lOzAAAAEAAAYAAgBzBwQAAAgBABOAArBQDzYAAWRyAQkA9UcSAAABAAEAbAABALcCAICGsCARTwABb3d8QRchEUkB") \ No newline at end of file diff --git a/pr/234/docs/search.index/3ad40b841b32.js b/pr/234/docs/search.index/3ad40b841b32.js new file mode 100644 index 000000000..bf05fdf35 --- /dev/null +++ b/pr/234/docs/search.index/3ad40b841b32.js @@ -0,0 +1 @@ +rn_("AQkA89gSAAABADQAAYACsCASgAABoCAAAA7MaXkBCQGggAAAEwpmAQYSAAABAwGwABKOAE1zAUESAABDgAOwIA7IAQWwUBFPAAFhb3IBAgD1rg8AAAEAAQAIAgEA8wADaXR1OjAAAAEAAAAAAAwAEAAAAIQHFAkiD40PmBB6EaYRpxFFEkYSTBJ0EnUSAQYA9JgRAAABAGcBAQATAgKwYBGaAAFmbfJQDAAAAQAjggKwQBG+AAFlbAGAArBAD7IAAaAgAAASvGN0E4ACsLASHgABYWwBBgDz1A4AAMUAAQABBQD0bBIAAAEAQgABAPMCAWPyjxEAAAEAAYECsFAP8wABsCAOpwABY24BBQA7MAAAAQAADQACAFYMAAD1DgwAAQIAOzAAAAEAAAcAAQA2CQcA5wMAAAegMAAAC9iw0BKdAAEwYWZpa3J2OzAAAAEAAB8CBQC9AQAACwPTAHwHAACwCtMATw11AAEMAaCgAAATG3fyCxMAAAEAAQEA8+YOAAABAAEA84ACdHUBAwDzUxEAAAEAAQABAQD1BxAAAAEAAQABAAEAAQEA9m8HAAABAIoA4QaVAL0BY4ADsJASbgABYW10AQMBoEAAABLAZfLfEgAAAQATgAKwYBMOAAFlcgEDAaAAAAASTHMBzA8AAAECADowAAABAAAAAAAKABAAAAA0CTUJCw/nEegRDhIPEqgS+hIQExETgwAEoIAAABEysHAQMwABsCAP3ADSZm5ydgH5BwAAAYECoAAAAAkUoDAAAA7Mc3THAQCAxrAAEpsAAbCQETAAAaAQAAAHWwASiCgBAgA7MAAAAQAADQAGAD8PBQAwEAEAXBEBAGgSAQCzEgAA0hIAABOCArDwEw4AAWZnAYACsIAS5gABoGAAABMJZHMTgAKwwBIzAAFpbwEIAPWhEgAAAQABAAEAAQDzAgFm9XIPAAABAAEAAQABACOAA6AgAAAOxrCgEuQAAWRydPf7BwCPoDAAAA68l9cPOzAAAAEAABwBHQAIBAEABAUCALQFAgDZBQIA9wUBANQGBABKBwAAZAcAAPMHAwDsCAIAGAkBAK4LAgDZCwEA/AsFAFIMAQDNDdEA3A4BAFQPAgCODwEAnw8CAMsPAQAhEAEAShAAAJ0QAQA6EQAAbBEAAPYRAQASEgcA8xIDAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/3b9c9d813a13.js b/pr/234/docs/search.index/3b9c9d813a13.js new file mode 100644 index 000000000..92fbaa716 --- /dev/null +++ b/pr/234/docs/search.index/3b9c9d813a13.js @@ -0,0 +1 @@ +rn_("AQMA81MRAAABAAEAI4ECsSATDgABY2sBBAGgQAAAEKhl824MAAB1AwEAAQAA9cYPAAABAAEAAQAxAnOAhKAwAAASmQgwAQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/3c0affdb0268.js b/pr/234/docs/search.index/3c0affdb0268.js new file mode 100644 index 000000000..ac9c6c3be --- /dev/null +++ b/pr/234/docs/search.index/3c0affdb0268.js @@ -0,0 +1 @@ +rn_("AQUA9GwSAAABAEIAAQDzAwFj8o8RAAABACOAArBwEw4AAWhvAYICsAARbQABsAAQnwABZXNDAAOgoAAAErSgIAAAEf5laXTyfxEAAAEAAQIAOzAAAAEAAAYAAQApEAYAAQIBoEAAABKZZfLDEgAAAQDzgAJpbwEIAPRjEgAAAQB6AAUAEwEDsJASvgABsFARvAE/Z2xwOzAAAAEAAAoABQBPDwIAqxEBAFQSAQCREgEA0BIBAAGAArBAEU0A6aAgAAAP8GFpAYECsDAScgABoDAAABFjY2kBAwGwABGmAAFzOzAAAAEAAA0AAwCdEAEAEhIHAPMSAwABAwD1mxAAAAEA+AABADQB84ACcnYBAAD0SxEAAAEASgABAPMCAWT2mRAAAAEArgABACYAAQAjgAKgkAAAErNpbxOAArDwEwAAAWxvAQUA9M8SAAALAB0ADwAjgAKgYAAAEfhncyOAArCQErEAAWJs98cAAI2wYBLkAAGwcBJmAAGwYBG6AAGwUBGoAAG1yRc6MAAAAQAAAAAAHwAQAAAA5wXrCBcJ4AvhC00MTgxPDMYNxw3IDckNyw3MDekO6g7rDuwO7Q6jD6QP2Q/3D00QThBPEFAQlxCyEcoRyxGHEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/3c13ca3e4e61.js b/pr/234/docs/search.index/3c13ca3e4e61.js new file mode 100644 index 000000000..2fdda8efe --- /dev/null +++ b/pr/234/docs/search.index/3c13ca3e4e61.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAANQAAgCdCwAAewzTAPMAAW07MAAAAQAA1AACANYFAAD9BdMAAQMAOzAAAAEAAAcAAgCfDgUA1REBAPMAAWM7MAAAAQAAWAAmAPgECwCmBQEA9wgBAK0LAACzCwIAXAwAAGUMAgB2DAEAxQ4AAMoOAQDNDgEA0Q4AAEwPAgBSDwEAVw8BAG8PAADcDwAA4A8BACYQAQAzEAEAQBABAEQQAgCbEAEAqBACAK4QAABKEQAAUREBAFYRAQCUEQEAoBEDAL4RAQDNEQMA0xEBACQSAQA1EgAAmRIAAL4SBADHEgIA84ACcncBCQDz2BIAAAEANAABDAGgoAAAExt38gsTAAABAAEBAPPmDgAAAQABAPOAAnR1AYECsIAS5gABoGAAABMJZHM3AgCBhqBAAAASvLBgEo4ATbAwEoAAAQKQDgGIArAAEk0AAbAAEk8AATQ2M4ADoDAAABBDZG5zYwADoBAAAAUXYWVvAW4AAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/3c4b2624d9b8.js b/pr/234/docs/search.index/3c4b2624d9b8.js new file mode 100644 index 000000000..6012086db --- /dev/null +++ b/pr/234/docs/search.index/3c4b2624d9b8.js @@ -0,0 +1 @@ +rn_("AQIA9I4QAAABAAEAAQATAAOwIBCSAAGwgBKfAAFhc3QBTAwAAAEEAPPrDgAAAQABADOAhLBAEfYAAbBQEecAAUEABg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/3e6b1a75c307.js b/pr/234/docs/search.index/3e6b1a75c307.js new file mode 100644 index 000000000..94642ed38 --- /dev/null +++ b/pr/234/docs/search.index/3e6b1a75c307.js @@ -0,0 +1 @@ +rn_("AQQA9nMMAAABAL8CAQAHAQEAAQoA82ARAAABAJMAAQEA9wcJAAABAFUGAQBmAAEA8QAjgAKwYBJWAAFpbwECAPQ6DAAAAQBhBQEAAQUA9GwSAAABAEIAAQD3BAAAh6AgAAAMPLCQEKkAAQScA/fLBQAAGgYBAOwDAQC8AQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/3e8fa26efc53.js b/pr/234/docs/search.index/3e8fa26efc53.js new file mode 100644 index 000000000..5c9115957 --- /dev/null +++ b/pr/234/docs/search.index/3e8fa26efc53.js @@ -0,0 +1 @@ +rn_("AQEA9fsHAAABAAEAAQABAAEAAqAwAAAP5qCQAAAR/zBo9bEQAABYAQEAAQABABOAAqAwAAAOxWRyIwKEsBAI4wLXsCALlADkoDAAAA7hEgAY9dIFAAAnAAEAAQABADOAhLCgEVwAAbBADs8AARBJAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/3ea05a9a63fc.js b/pr/234/docs/search.index/3ea05a9a63fc.js new file mode 100644 index 000000000..00f354ab5 --- /dev/null +++ b/pr/234/docs/search.index/3ea05a9a63fc.js @@ -0,0 +1 @@ +rn_("AYUCsHASfQABsDAR4gABZXMTAAOwQA7NAAGgYAAAEEtiZXM7MAAAAQAAEAADAN4GCgA5DwEAgQ8DAAECAPPnBQAAAgkBAAEDAPXFDgAAjQABAAQAAQABCgGgQAAAEsBl8t8SAAABACMAA6AwAAAP5qCQAAAR/zBjaPWxEAAAWAEBAAEAAQDzgAJkcucAAACHsBAI4wLXsCALlADkoDAAAA7hsAAHSAABEkE4OzAAAAEAABAABgB0AAIA0gUAAPkFAwChBwMA4gcDAJ4LAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/3ecfb741bfaf.js b/pr/234/docs/search.index/3ecfb741bfaf.js new file mode 100644 index 000000000..9c25a1f41 --- /dev/null +++ b/pr/234/docs/search.index/3ecfb741bfaf.js @@ -0,0 +1 @@ +rn_("AQAA9AcTAAABAAoAAQABAAA7MAAAAQAABgACAPsRAABdEgUAI4gCsEASxwABZXMBDQGwQBLBAAFl9YkSAAABAAEAAQABAAEFADswAAABAAAIAAMAqxACAIoRBACIEgAA1wEAAIUBm9jyKeQUhALzPQwAAAEAAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/40410c9d55a4.js b/pr/234/docs/search.index/40410c9d55a4.js new file mode 100644 index 000000000..17efbca8b --- /dev/null +++ b/pr/234/docs/search.index/40410c9d55a4.js @@ -0,0 +1 @@ +rn_("AQQBsEASwQABZfWJEgAAAQABAAEAAQABAgA7MAAAAQAACwACADsSBQDoEgUA8wEBbTswAAABAAANAAQAQgwAADIQAABzEQUAqRIFAPMBAWXyrxAAAAEAM4CEsBAPZAABoCAAAAuhFEAE") \ No newline at end of file diff --git a/pr/234/docs/search.index/41c76bd7e9b0.js b/pr/234/docs/search.index/41c76bd7e9b0.js new file mode 100644 index 000000000..b6a100da1 --- /dev/null +++ b/pr/234/docs/search.index/41c76bd7e9b0.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgD7EQAAXRIFACOIArBAEscAAWVzAQQBsDAR1QABY/NEEAAAAQABAAEFADswAAABAAAIAAMAqxACAIoRBACIEgAA8wMDY2Vw8z0MAAABAAEAAQABsGASdAABZQGmEgAA8wIBc/YLCQAAAQABAAEHNAD8AAECAaBQAAAQQ3IB7g8AAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/42740cbde866.js b/pr/234/docs/search.index/42740cbde866.js new file mode 100644 index 000000000..1c4ed5d7b --- /dev/null +++ b/pr/234/docs/search.index/42740cbde866.js @@ -0,0 +1 @@ +rn_("AYICsEASvgABsGARtAABZXIBBQD0dw8AAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAANIO0w5REHsRfBFYEtMSAhMDEwEAAaFAAAAS+2f0gAcAAAEAAQABAMMAhKCwAAASQbBAD44AARQgBPUEBwAAPAX4AhUC6QBjgAOwMA8FAAFhZWk=") \ No newline at end of file diff --git a/pr/234/docs/search.index/435ef6b1a68d.js b/pr/234/docs/search.index/435ef6b1a68d.js new file mode 100644 index 000000000..a7eb72039 --- /dev/null +++ b/pr/234/docs/search.index/435ef6b1a68d.js @@ -0,0 +1 @@ +rn_("AQUA9GwSAAABAEIAAQDzAwFj8o8RAAABACOAArBwEw4AAWhvAYICsAARbQABsAAQnwABZXNDAAOgoAAAErSgIAAAEf5laXT1fxEAAAEAcgABAOEAAYECoEAAABH5sEARugABYWYBgAKwcBFkAAGgsAAAEn9lbwECADswAAABAAAGAAEAKRAGAAECAaBAAAASmWXywxIAAAEA84ACaW8BCAD0YxIAAAEAegAFABMBA7CQEr4AAbBQEbwBP2dscDswAAABAAAKAAUATw8CAKsRAQBUEgEAkRIBANASAQABgAKwQBFNAOmgIAAAD/BhaQGBArAwEnIAAaAwAAARY2NpAQMBsAARpgABczswAAABAAANAAMAnRABABISBwDzEgMAAQMA9ZsQAAABAPgAAQA0AfOAAnJ2AQAA9EsRAAABAEoAAQDzAgFk9pkQAAABAK4AAQAmAAEAI4ACoJAAABKzaW8TgAKw8BMAAAFsbwEFAPTPEgAACwAdAA8AI4ACoGAAABH4Z3MjgAKwkBKxAAFibPeXAwCPsHAQrwABsGAS5AABsHASZgABsFARqAABtfkXOzAAAAEAADoAFgDnBQAAcQcBAN0IBQDrCAAAFwkAAOALAQBNDAIAcQwBAMYNAwDLDQEA6Q4EAKMPAQDZDwAA9w8AABcQCQBLEAAATRADAJcQAACyEQAAyhEBANcRBQCHEgAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/440ad754dc7b.js b/pr/234/docs/search.index/440ad754dc7b.js new file mode 100644 index 000000000..ec7246f41 --- /dev/null +++ b/pr/234/docs/search.index/440ad754dc7b.js @@ -0,0 +1 @@ +rn_("AQcA9LIQAAABAAEAAQAjAQKgcAAAELFhcgF/BwAAAQMAOzAAAAEAAAcAAgCfDgUA1REBAPMAAmN0OzAAAAEAAFgAJgD4BAsApgUBAPcIAQCtCwAAswsCAFwMAABlDAIAdgwBAMUOAADKDgEAzQ4BANEOAABMDwIAUg8BAFcPAQBvDwAA3A8AAOAPAQAmEAEAMxABAEAQAQBEEAIAmxABAKgQAgCuEAAAShEAAFERAQBWEQEAlBEBAKARAwC+EQEAzREDANMRAQAkEgEANRIAAJkSAAC+EgQAxxICAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/441c00806b58.js b/pr/234/docs/search.index/441c00806b58.js new file mode 100644 index 000000000..c915c8163 --- /dev/null +++ b/pr/234/docs/search.index/441c00806b58.js @@ -0,0 +1 @@ +rn_("AQIBsEAS1gABdDswAAABAAANAAYAhhEDAOIRAQBCEgEAWRIBAMwSAQDvEgEAE4ACsDARrwEmaXBDggOgQAAAEnCwcBJ9AAFiZXM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/442973fdf353.js b/pr/234/docs/search.index/442973fdf353.js new file mode 100644 index 000000000..54406b959 --- /dev/null +++ b/pr/234/docs/search.index/442973fdf353.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgB1EAMAUxECAAEKAbBAEsEAAWX1iRIAAAEAAQABAAEA8wACaXP0qgsAAAEASgABACOBAqDgAAASqGV5") \ No newline at end of file diff --git a/pr/234/docs/search.index/44b01bde9d64.js b/pr/234/docs/search.index/44b01bde9d64.js new file mode 100644 index 000000000..5ff89bb6c --- /dev/null +++ b/pr/234/docs/search.index/44b01bde9d64.js @@ -0,0 +1 @@ +rn_("AQEA9QcQAAABAAEAAQABACOBArCQEm4AAWF0Q4ADsRAS3wABoIAAABIxY290EwACsAAStwABbnL2yAEAAAEAQQIBADoDuQo=") \ No newline at end of file diff --git a/pr/234/docs/search.index/44d1c75fe36d.js b/pr/234/docs/search.index/44d1c75fe36d.js new file mode 100644 index 000000000..2acdb446c --- /dev/null +++ b/pr/234/docs/search.index/44d1c75fe36d.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABLAZfLfEgAAAQABgAKwAAxXAAGggAAAEjJkcAEDAaAAAAAPJHMBvg4AAAECA7BgEcQAAbDQEsMAAaCAAAASUWlycwHOEgAA9wAAgIWhMAAAEt6QSQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/464be0b62530.js b/pr/234/docs/search.index/464be0b62530.js new file mode 100644 index 000000000..57377fa5f --- /dev/null +++ b/pr/234/docs/search.index/464be0b62530.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAHYAAgDKDwAAtxB1AAEDADswAAABAAB1AAEATw11AAECADswAAABAAAHAAEAxQsHAPMAA2JpdDswAAABAAB2AAIA8AQAACkFdQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/4683bd92a354.js b/pr/234/docs/search.index/4683bd92a354.js new file mode 100644 index 000000000..f71bfad31 --- /dev/null +++ b/pr/234/docs/search.index/4683bd92a354.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQAXEAkAAQIAOzAAAAEAABMAAQAODxMAE4GEoFAAAA/doBAAAAusoCAAAAxdHEAAE4ACsFAR9gABYW4zgAOwMA7BAAFpc3Q=") \ No newline at end of file diff --git a/pr/234/docs/search.index/46f1494df7bd.js b/pr/234/docs/search.index/46f1494df7bd.js new file mode 100644 index 000000000..72c595ebb --- /dev/null +++ b/pr/234/docs/search.index/46f1494df7bd.js @@ -0,0 +1 @@ +rn_("AQQA9DARAAABAD0BAQDzAAFsOzAAAAEAAAgAAwBMCQEAtg8BAAcQBAABAAGwYBJ0AAFl9NMLAAABAAEA0QYBBQD0tBEAAAEALAEBAAEHADswAAABAAAFAAEAABIFAPMCAXQ7MAAAAQAACAACAJULAAC7CwcA84ACYWUBgAKxEBLxAAGwEAvWAAFlbwEFADswAAABAAANAAEAWxANAAECAPPyDgAAAQABAAECAPTuDgAAAQABAAEAAQMAOzAAAAEAAAgAAQCQDwgAhwcAAIewoBIvAAGw0BK3AAGgUAAAEoaMFBH1YwcAAFkIAQABAAEA84ACbHQTgAOgIAAADECwoBKfAAFhZG4BgAKwgBGRAAGwoBH2AAFpdAEOAPSVEgAAAQBGAAEAE4ACsEAPOwABbHIBBgD0vA8AAAEAAQABAEcFAACHoNAAABK5sFAPOQABoAAAABH4sPASlwABBZED8uEOAACbAAEDAPTNEQAAAQDwAAEAAQgA9EQRAAABAAEAAQABBAGwAA/AAAFzAT0PAAABAgGgUAAAEZNjAUEMAAC3AgAAh7BADHYAAbAQEekAAaAAAAARyZUQAzowAAABAAAAAAANABAAAAAVBRYFzwXQBdEFRAxFDLkOug67DscOLxIwEjISAYACsCAJEQABoOAAABJlYmz3FgAAibAQBckAAbAgB30AAZEJRzswAAABAACEAAoAHAQAAAsJAgDKDwAADhAAAEIQAAC3EHUALhEDAD4RAADvEQAAbhIBAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/47698cc593eb.js b/pr/234/docs/search.index/47698cc593eb.js new file mode 100644 index 000000000..508443367 --- /dev/null +++ b/pr/234/docs/search.index/47698cc593eb.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABJMcwHMDwAAAYACsDAPYgABsFAPZAABYWkBgAKgsAAAEs6gMAAAD+1sbhcBAACFoCAAABE3oCAAAAjksIASmwABEJEEOjAAAAEAAAAAABAAEAAAAO0EIgUjBSQFJQUmBScFSgmuDq8OyRFwEnYSghKDEpcSmBIBAgA6MAAAAQAAAAAACgAQAAAANAk1CQsP5xHoEQ4SDxKoEvoSEBMREwEAADswAAABAAAGAAIA+xEAAF0SBQABAgGwMBHVAAFjOjAAAAEAAAAAABMAEAAAAGUMxQ5MD00PTg9SD1MPVw9YD9wPQBBEEEUQRhCuEM0RzhGZEr4SvxIBggKwMBKPAAGwQBAzAAFhZQECAbBAEsEAAWX1iRIAAAEAAQABAAEAxwcAAMewQBLHAAGggAAAETIATIgJ8vkHAACoAwGAAqCAAAAR7qCQAAARzGFrAQMA81MRAAABAAEAI4CEsSATDgABoAAAAAkUoDAAAA7MBAQGAQgA9RYTAAABAAEAAQABABOKArBAEvEAAWFykwCEoMAAABHIoQAAABJ/AUAG8n0HAAABAAEDAaBAAAAQqGXzbgwAAHUDAQABBQD0KBIAAAEAAQABAFOAA7AAEekAAWVzdAEGAPT8EgAAAQABAAEA8wgBbfREEQAAAQABAAEAAQIAOzAAAAEAAAUAAQDoEgUA8w0BbTswAAABAAAFAAEAqRIFAAEEAPd2DAAAAQDTBAwAAQB4AAEAAQMBgBHyABDhczswAAABAAAFAAEA1xEFACOCAqCQAAASvWlzAYACoLAAABKGsDAR6QABbnQBgAKggAAAEf+wQBCjAAFhZQGAAqAQAAAP5aAAAAAQJW5yAQkBoEAAABLAZfLfEgAAAQAjgAKgkAAAEjJpbMcjAAAKoDAAAA/msFARpAABsPATBAABsHAR/AABoNAAABLJMGJjZWhpa25vdjswAAABAAAYAAsAnwsAAMYPAwCxEAQA+hEAAAkSAwA5EgEAfxIAAIQSAQCbEgEApxIAALoSAQABAgKgsAAAErOgsAAAErRjc/OjDwAAAQBTAAEBArBQEKMAAaDAAAAShmhp8jkSAAABAPOAAmVpAQEBoJAAABH6c/SJDwAAAQABAAEAAQMBsAAPwAABcwE9DwAA84ADaGl0AQEBoFAAABGTYwFBDAAAAQUBsIASugABdjswAAABAAAIAAMAhxAEABsSAAAsEgIAY4ADsBAP8QABY2Rs97UHANCwkBEwAAGgUAAAD9+goAAAEhygEAAAB1tAP70pOzAAAAEAADUAFQAVBQEAzwUCACcHAQCOCwMARAwBALkOAgDHDgAA5Q4AAAwPAQD6DwgARxAAAGQRAQBtEQEAcREBAMQRAQDdEQQAJhIBAC8SAQAyEgAAoRIEAAoTAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/47e65f6bbe8e.js b/pr/234/docs/search.index/47e65f6bbe8e.js new file mode 100644 index 000000000..8ab8efb94 --- /dev/null +++ b/pr/234/docs/search.index/47e65f6bbe8e.js @@ -0,0 +1 @@ +rn_("AYICoFAAABFsoQAAABLuY3QjAAKwQA7KAAFldTswAAABAAA3ABYA3AYBAJsLAQBgDAEAzw4BAD8PBQBnDwUA2g8BADAQAQCrEAIAXBEDAIoRBACwEQEAthEBAMYRAQA3EgEAShIBAGgSAQBsEgEAiBIAAK8SAQCzEgEA0hIAAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/47f0dfc2bfd8.js b/pr/234/docs/search.index/47f0dfc2bfd8.js new file mode 100644 index 000000000..86e1a398f --- /dev/null +++ b/pr/234/docs/search.index/47f0dfc2bfd8.js @@ -0,0 +1 @@ +rn_("AYACsHAS7wABoAAAAAT2ZmcBAQA7MAAAAQAABwABAMULBwABAQGgcAAAEThhASQHAAAjgAKhAAAAEvdkbnOAhKBgAAARNwBBSA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/48120e13d7d1.js b/pr/234/docs/search.index/48120e13d7d1.js new file mode 100644 index 000000000..de4c73ab3 --- /dev/null +++ b/pr/234/docs/search.index/48120e13d7d1.js @@ -0,0 +1 @@ +rn_("AYMCsQATEAABsNATAgABa20BBAGgoAAAExt38gsTAAABAAGAAqCAAAASp7CwEoQAAWVvYwQDoGAAABJEZW5wAakLAAABCgDzaBIAAAEAaQATgAKiEAAAExtsb3cAAACFoGAAABEtoQAAABLUBIEDOzAAAAEAADsABADrBQUAXAcAAKUHMwBDEAAAQ4ADsIAQQgD8oAAAAAHTZGt0") \ No newline at end of file diff --git a/pr/234/docs/search.index/498bb15a8c92.js b/pr/234/docs/search.index/498bb15a8c92.js new file mode 100644 index 000000000..28c93dda8 --- /dev/null +++ b/pr/234/docs/search.index/498bb15a8c92.js @@ -0,0 +1 @@ +rn_("AQwBoKAAABMbd/ILEwAAAQABAQDz5g4AAAEAAQABiYSggAAAEwmgQAAAErygYAAAEtugkAAAEw0CgAoTgYSgoAAAElixoBMZAAGw0BK1AAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/234/docs/search.index/49c82ba58485.js b/pr/234/docs/search.index/49c82ba58485.js new file mode 100644 index 000000000..5efc0f26f --- /dev/null +++ b/pr/234/docs/search.index/49c82ba58485.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwCCEAIA8BEBAHoSAgABAAKgYAAAEYWwQBCFAAFhcvPCDwAAAQCLAQGAAqEQAAAS97CAEu8AAWl1NwQAgYexUBMSAAGACP4AFqOwcBJFAAGhMAAAEwYKQBsjgAKgMAAAETJudg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/4aa239b6a7ae.js b/pr/234/docs/search.index/4aa239b6a7ae.js new file mode 100644 index 000000000..c66788b49 --- /dev/null +++ b/pr/234/docs/search.index/4aa239b6a7ae.js @@ -0,0 +1 @@ +rn_("AQEBsIAR5AABdfInCQAAAQAjgAKwEAklAAEzNgECAPQhCQAAAQDtBgEAAQIA89MLAAABAAEAE4ACsBAJHwABaXUBAgA7MAAAAQAACwACADsSBQDoEgUA8wkBbTswAAABAAAMAAMAMhAAAHMRBQCpEgUAdyAAAIqgMAAAElGwMBBIAAGgcAAAEK6gIAAACSOwMBB7AAGwIAdeAcazhQo7MAAAAQAA/wEEALsBAADdARkBewcAAMwJ4wA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/4b421ea6441b.js b/pr/234/docs/search.index/4b421ea6441b.js new file mode 100644 index 000000000..4e505d29d --- /dev/null +++ b/pr/234/docs/search.index/4b421ea6441b.js @@ -0,0 +1 @@ +rn_("AQIBsEAS1gABdDswAAABAAANAAYAhhEDAOIRAQBCEgEAWRIBAMwSAQDvEgEAE4ACsDARrwEmaXBDggOgQAAAEnCwcBJ9AAFiZXMTAAKgIAAABddmbfZqAAAAAgcBALIBAQDsBg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/4d5fb1407792.js b/pr/234/docs/search.index/4d5fb1407792.js new file mode 100644 index 000000000..32a254e3c --- /dev/null +++ b/pr/234/docs/search.index/4d5fb1407792.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQDoEgUA8w0BbTswAAABAAAFAAEAqRIFACMDA7BAEVYAAaAAAAASdmVmczswAAABAAAUAAUA+w8HAHERAQDdEQQAoRIEAAoTAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/4e0bb34e56c4.js b/pr/234/docs/search.index/4e0bb34e56c4.js new file mode 100644 index 000000000..2de0a98e1 --- /dev/null +++ b/pr/234/docs/search.index/4e0bb34e56c4.js @@ -0,0 +1 @@ +rn_("AQMA80wPAAABAAEAAYACoLAAABKGsDAR6QABbnRDAAOgIAAAD+WwUBCjAAFlaGk7MAAAAQAABgADAJ8LAACyEAMAORIBAPOAAmRy") \ No newline at end of file diff --git a/pr/234/docs/search.index/4ec66add1e98.js b/pr/234/docs/search.index/4ec66add1e98.js new file mode 100644 index 000000000..2058e5a1b --- /dev/null +++ b/pr/234/docs/search.index/4ec66add1e98.js @@ -0,0 +1 @@ +rn_("AQQA87kOAAABAAEAAYACoFAAAA+poGAAABAOaXIBgAKgIAAAC/GgYAAAEA1laAEBAaAgAAAQDGLz7QsAAAEAAQATgQKgEAAAC/BhYwEGAPSYEQAAAQBnAQEAEwACsGARmgABZm3yUAwAAAEAI4ACoMAAABJMbnKnBwAAh6BAAAAOtrBADrcAATQAFzswAAABAAAXAAMAxQEAAO4DDQCECwgAAYADsHASBwABsDARvgABsIASOQABY3JzAQABsCASBwABbzowAAABAAAAAAAJABAAAACVEJYQgxGEEVISUxKfEqASDhMPEwGAArAgCQkAAbAwEcoAAXJ09wAAAMegIAAAB3qgYAAADtuwgBHKAAFABpII9tIBAAA/Aj0FAQAXCQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/4fbd4ea92e70.js b/pr/234/docs/search.index/4fbd4ea92e70.js new file mode 100644 index 000000000..b88ec409c --- /dev/null +++ b/pr/234/docs/search.index/4fbd4ea92e70.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgD7EQAAXRIFACOIArBAEscAAWVzAQQBsDAR1QABY/NEEAAAAQABAAEFADswAAABAAAIAAMAqxACAIoRBACIEgAA8wEDY2Vw8z0MAAABAAEAAQIA9AEJAABQALIGAQDzgAJucg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/50184a00a93c.js b/pr/234/docs/search.index/50184a00a93c.js new file mode 100644 index 000000000..93701e0da --- /dev/null +++ b/pr/234/docs/search.index/50184a00a93c.js @@ -0,0 +1 @@ +rn_("AYMCsQATEAABsNATAgABa20BCgDzaBIAAAEAaQATgAKiEAAAExtsbzMChKBgAAARLaEAAAAS1ASAAzswAAABAAA0AAIAXAcAAKUHMwA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/50f6f3198519.js b/pr/234/docs/search.index/50f6f3198519.js new file mode 100644 index 000000000..0d2fe4776 --- /dev/null +++ b/pr/234/docs/search.index/50f6f3198519.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAANcO2A6qD6sPKBClEKYQmBGZEQATARMBBADz1A4AAMUAAQDzAAFx93MMAAABAL8CAQCuAFkAAQDzAAJleTowAAABAAAAAAAMABAAAADWC9cLZA9lD+oP6w/sD2MSZBJyEnMS3hLjEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/510fd0e4bb9e.js b/pr/234/docs/search.index/510fd0e4bb9e.js new file mode 100644 index 000000000..08d94e0c5 --- /dev/null +++ b/pr/234/docs/search.index/510fd0e4bb9e.js @@ -0,0 +1 @@ +rn_("AQYA9JgRAAABAGcBAQATAgKwYBGaAAFmbfJQDAAAAQAjggKwQBG+AAFlbAEFAPRsEgAAAQBCAAEA8wMBY/KPEQAAAQABgQKgAAAACRSgMAAADsxzdBOAAqAQAAAHW25404CEsHATDgABkEAB") \ No newline at end of file diff --git a/pr/234/docs/search.index/516a9f043d12.js b/pr/234/docs/search.index/516a9f043d12.js new file mode 100644 index 000000000..6cb0b590e --- /dev/null +++ b/pr/234/docs/search.index/516a9f043d12.js @@ -0,0 +1 @@ +rn_("AQkA89gSAAABADQAAQ0BoKAAABMbd/ILEwAAAQABgQKwgBLmAAGgYAAAEwlkczcBAIaFoEAAABK8sGASjgBNApAK") \ No newline at end of file diff --git a/pr/234/docs/search.index/51a21faaeab8.js b/pr/234/docs/search.index/51a21faaeab8.js new file mode 100644 index 000000000..46505e73b --- /dev/null +++ b/pr/234/docs/search.index/51a21faaeab8.js @@ -0,0 +1 @@ +rn_("AQAEoAAAAA9xsCAQFQABoFAAABCnoEAAABKaZGxzdjswAAABAAASAAYA3wUAALYLAABWDAAA9Q4MADUQAACTEgEAAQIA9I4QAAABAAEAAQAThAKwIBCSAAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/234/docs/search.index/51a886ba80e8.js b/pr/234/docs/search.index/51a886ba80e8.js new file mode 100644 index 000000000..d461bf50a --- /dev/null +++ b/pr/234/docs/search.index/51a886ba80e8.js @@ -0,0 +1 @@ +rn_("AYACoJAAABH5oBAAAAXeaW8BCQDz2BIAAAEANAABDAGgoAAAExt38gsTAAABAAEBAPPmDgAAAQABAPOAAnR1AYECsIAS5gABoGAAABMJZHM3AgCDhqBAAAASvLBgEo4ATbAwEoAAAQKQDgESAPTzEgAAAQABAAEAAQDGoFAAABFsoAAAAA9xsCAQFQABoFAAABCnoQAAABLuoEAAABKaAAMCCzswAAABAAAUAAcA3wUAALYLAABWDAAA9Q4MADUQAACBEQEAkxIBAAEBAbCwEuEAAWw7MAAAAQAADAADADUHCABvDAEAMQ8BAAEBAaAwAAAP8Gjz6wgAACwAgAcBAgD0jhAAAAEAAQABABOBArAgEJIAAWFz84ICbm/zgIYVAQM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/54064ee22fd2.js b/pr/234/docs/search.index/54064ee22fd2.js new file mode 100644 index 000000000..5b89374b1 --- /dev/null +++ b/pr/234/docs/search.index/54064ee22fd2.js @@ -0,0 +1 @@ +rn_("AYACsJAQQgD8sAAL8gABbHQBgAKwcBIHAAGwgBI5AAFjcwEAADowAAABAAAAAAAJABAAAACVEJYQgxGEEVISUxKfEqASDhMPE/OAAmRlJwEAgIWg0AAAEnewEAUQAAGgMAAAD1oCCAs=") \ No newline at end of file diff --git a/pr/234/docs/search.index/54679bb88e4d.js b/pr/234/docs/search.index/54679bb88e4d.js new file mode 100644 index 000000000..adf0b1f7a --- /dev/null +++ b/pr/234/docs/search.index/54679bb88e4d.js @@ -0,0 +1 @@ +rn_("AQEDsIASeAABsFAR6wABsEAR0wABYWJlOjAAAAEAAAAAAAkAEAAAAEEHQgdeDF8MwA4jD2IPYw87Ee4RAQAAOzAAAAEAAB4ADgATBQEAwAUIAA4JAQCxCwEAVAwBAMoNAACzDgIAyA4AAN4OAQC6DwEAzQ8AAEwQAACfEAEAohAAAAEAAbBAEc8AAWXzRxAAACYBAQCXAQCAhbCQEuQAAaBgAAAP7hABCw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/54aa7ed4bc9a.js b/pr/234/docs/search.index/54aa7ed4bc9a.js new file mode 100644 index 000000000..4618cc56b --- /dev/null +++ b/pr/234/docs/search.index/54aa7ed4bc9a.js @@ -0,0 +1 @@ +rn_("AQEA9+oPAAABAAEAdwIBAHoABQABAQKgQAAAEsmgMAAAD/BlaPXrCAAALADJAgEAtgQBAgD0jhAAAAEAAQABABOBArAgEJIAAWFz84ACbm8BAgD0iQ8AAAEAAQABABMAAqDQAAASzmNz8gwPAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/5527af3a0275.js b/pr/234/docs/search.index/5527af3a0275.js new file mode 100644 index 000000000..c9df35942 --- /dev/null +++ b/pr/234/docs/search.index/5527af3a0275.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQApEAYAAQAA9wcJAAABAFUGAQBmAAEA8QABggKgIAAAD++gMAAAEExpdPOAA2FncwEBAaBAAAASmWXywxIAAAEAI4ACsFASVgABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/5583dbff6ecd.js b/pr/234/docs/search.index/5583dbff6ecd.js new file mode 100644 index 000000000..e82bfb311 --- /dev/null +++ b/pr/234/docs/search.index/5583dbff6ecd.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA8AEAAAAHYMdwzgD+EPqBCpEKoQShFREVIRVhFXEaARoRHPEdARAQkA8z0QAAABAAEAAQoA81kRAAABAAEAAQYA9QcQAAABAAEAAQABAPOAAmRucwCEsSASnQABABED8qQRAAABAAENAPYOEgAAAQCZAFIAFgABAAGAArCgEiQAAaAQAAALuWN1AQEA9DMSAAABAEkAAQDzAAFl8lIJAAABAPcAAIDFsDAPjgABAAKoCA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/558b25ba5cd4.js b/pr/234/docs/search.index/558b25ba5cd4.js new file mode 100644 index 000000000..c485056fe --- /dev/null +++ b/pr/234/docs/search.index/558b25ba5cd4.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAANIO0w6pD1EQexF8EVgS0xICEwMTAYACsOASUgABsDASagABZHDDgISxQBMSAAGgkAAAEhwCgAk=") \ No newline at end of file diff --git a/pr/234/docs/search.index/579abe8832f0.js b/pr/234/docs/search.index/579abe8832f0.js new file mode 100644 index 000000000..911005d25 --- /dev/null +++ b/pr/234/docs/search.index/579abe8832f0.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAANMAAgBSDAEAzQ3RAAEBAaAAAAARyXP1FQUAALIJaAMBAAIAAYACoCAAAA69oDAAABF5ZXITAQKgIAAAEGlwdQGpDgAAAQECoGAAABI1oOAAABKIZXQ7MAAAAQAACAABABkFCAABgAOwIBEuAAGwsBFcAAGwsBFeAAFiY3PzgMUAAQoK") \ No newline at end of file diff --git a/pr/234/docs/search.index/57a002b9f9d0.js b/pr/234/docs/search.index/57a002b9f9d0.js new file mode 100644 index 000000000..7b2868e60 --- /dev/null +++ b/pr/234/docs/search.index/57a002b9f9d0.js @@ -0,0 +1 @@ +rn_("AQIBsEAS1gABdDswAAABAAANAAYAhhEDAOIRAQBCEgEAWRIBAMwSAQDvEgEAE4ACsDARrwEmaXBDgQOgQAAAEnCwcBJ9AAFiZXMTgAOwMBLkAAGwIAcQAAFlbHM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/57da63a54625.js b/pr/234/docs/search.index/57da63a54625.js new file mode 100644 index 000000000..e746a52e7 --- /dev/null +++ b/pr/234/docs/search.index/57da63a54625.js @@ -0,0 +1 @@ +rn_("AQQA9IUPAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAA1w7YDqoPqw8oEKUQphCYEZkRABMBE/MBAXnychIAAAEAAQADoKAAABK0oCAAABH+sDAQnwABZWl08n8RAAABAAEDADswAAABAAAGAAEAKRAGAAEIAPRjEgAAAQB6AAUAEwEDsJASvgABsFARvAE/Z2xwOzAAAAEAAAoABQBPDwIAqxEBAFQSAQCREgEA0BIBAAGAArAwEJsAAbAwEJ0AAXJ2AQIBsAARSwABZPSZEAAAAQDVAAEAI4ECoJAAABKzaW8BBwD0zxIAAAsAHQAPAHcWAAGJsFAScgABsFARTQDpsGARugABtQkDOjAAAAEAAAAAAA0AEAAAAE0MTgxPDMsNzA2jD6QP2Q/3D00QThBPEFAQshFDgAOgIAAADEOgEAAADExtbm8BAwD0TA8AAAEAAQDyAAGAAqCwAAAShrAwEekAAW50QwADoCAAAA/lsFAQowABZWhpOzAAAAEAAAYAAwCfCwAAshADADkSAQDzgAJkcocLAAAKgAG1ABABgAG4ABABoFAAABBKoFAAABK5oDAAAA9xoDAAAA98MDFjZWlvc3R1d/WfBwAAAQAfB3kCLQE=") \ No newline at end of file diff --git a/pr/234/docs/search.index/57ec46f6e8db.js b/pr/234/docs/search.index/57ec46f6e8db.js new file mode 100644 index 000000000..61c64398c --- /dev/null +++ b/pr/234/docs/search.index/57ec46f6e8db.js @@ -0,0 +1 @@ +rn_("AQAA9OkIAAABAFoJ1wAXAAAAybBAD+AAAbBgEsUAAaAQAAAMArBQEmoAAbAwEDkAAaAAAAALxLEgEuQAAbBAD94B1QBOsAk6MAAAAQAAAAAADQAQAAAA/Aj9CBEJEgmYC5kLmgtLDDkRWRFaEVsReBJ5Eg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/58c1c76c1b23.js b/pr/234/docs/search.index/58c1c76c1b23.js new file mode 100644 index 000000000..684c50fed --- /dev/null +++ b/pr/234/docs/search.index/58c1c76c1b23.js @@ -0,0 +1 @@ +rn_("AQABsIARygABdvROCQAAAQAXCQEAE4ACsDAP2gABbG4BgAKgAAAAD22gAAAAD240NvMDAXYBbAwAABMAAqBAAAAQR2Fz97gLAAAsA0ABHQEBAAEAdgEBAAKwMBJbAAGwIBIiAAFidTowAAABAAAAAAALABAAAABXB1gHWQd+D38PBRAGEK0RrhG9ESASIRLzgAJkdBOAAqAgAAAMdWVyAQcBoEAAABKZZfLDEgAAAQABBwD01Q8AAAEAAQABAAEBAPVBCQAAAQABAAEAAQABAwGgkAAAEnFl8qwOAAABANcAAACFoFAAAA9asUATFAABgSkA8q4FAAABAPOAAmx0lwEAgIWxMBKbAAGxEBLFAAERRQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/592306864b13.js b/pr/234/docs/search.index/592306864b13.js new file mode 100644 index 000000000..e888e1748 --- /dev/null +++ b/pr/234/docs/search.index/592306864b13.js @@ -0,0 +1 @@ +rn_("AYACsHAQOQABsCAStwABYWQBBgD0xhEAAAEAgwABACOAArCgEp0AAXN5g4CEsGAQjAABoAAAABAjsSAS+AABCSAB84ACZWk=") \ No newline at end of file diff --git a/pr/234/docs/search.index/59a125e0550b.js b/pr/234/docs/search.index/59a125e0550b.js new file mode 100644 index 000000000..b1815cb84 --- /dev/null +++ b/pr/234/docs/search.index/59a125e0550b.js @@ -0,0 +1 @@ +rn_("AQABoIAAABMKZvLmCAAAIAkBBwD13REAAAEAAQABAAEAI4ACoFAAABAoYW/zAgJmczswAAABAAAIAAQAWgcAAIUHBQBtDAAAWhAAABOAA7CAD/EAAaBAAAALnmFlbw==") \ No newline at end of file diff --git a/pr/234/docs/search.index/5af7c9ebff9c.js b/pr/234/docs/search.index/5af7c9ebff9c.js new file mode 100644 index 000000000..521f7453f --- /dev/null +++ b/pr/234/docs/search.index/5af7c9ebff9c.js @@ -0,0 +1 @@ +rn_("AQ0A9UcSAAABAAEAbAABAAGAAqEQAAASf7BwEo8AAW5yAQcA9HUQAAABAAEAAQAzAASgIAAACRCw4BJ0AAExZWly8+QDAAB3A5gBAQACsDARfQABsCARNQABYnUBBA8AACOEArAwETMAAWRzAQIA8/UIAAABAFIA84ACaXQBGADzGBMAAAEAAQABgAKwsBJFAAGgwAAAEflldfOBAmNtARABsEASwQABZfWJEgAAAQABAAEAAQABBAD0hQ8AAAEAAQABAAGBAqAgAAAP5bBAEekAAWVp84ICZXQBBAGgYAAAEVhy8gIPAAABAPOAA2FlbwEJAPZ7EQAAAQDcAHsALwABABOAArBgD50AAWRuAQYA9cYPAAABAAEAAQAxAgEDAaBwAAARqmfyRgkAAAEAAQkA89gSAAABADQAAYECsIAS5gABoGAAABMJZHMXAQCIhbDQEwsAAaBAAAASvLBgEo4ATQKQCgEEAPQzEgAAAQBJAAEAU4ADsJATBAABZWhvAQABoEAAABBAZQG/DgAAI4ECsFAP9QABa3R3/AEAjaGQAAAS+7CwEbgAAaCgAAARev2BDzswAAABAAAZAAgAaQAAAG8AAwCqBQEARgcDABsJAwDFCwcAOw8BADIRAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/5da06bd3cb28.js b/pr/234/docs/search.index/5da06bd3cb28.js new file mode 100644 index 000000000..a5204fa1b --- /dev/null +++ b/pr/234/docs/search.index/5da06bd3cb28.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAUAAQAAEgUAAQAA9fcLAAABAAEAAQABAPOAAmdrE4ACsLASVgABYW8BgAKgIAAAC42wYBAhAAFlaQGFArBwEn0AAbAwEeIAAWVzEwADsEAOzQABoGAAABBLYmVzOzAAAAEAABAAAwDeBgoAOQ8BAIEPAwABAwD0Ug8AAAEABAABAAELAaBAAAASwGXy3xIAAAEA84ACZHLzAAJlbzswAAABAAAHAAIAoQcDAOIHAwABAgA7MAAAAQAACAABAIQLCADzgAJvdAEDADswAAABAADUAAIAfAcAALAK0wABAgGgcAAAEK5pOzAAAAEAAOQAAgB7BwAAzAnjABOAAqBAAAALtnJ1AQQA9MYRAAABAIMAAQABAwDzYBEAAAEAkwDzgAJhb9cHAACIsEAMZgABsTAS3AABNIEWOzAAAAEAAB0ADAAOBQEAugUBAJULAAC7CwcA3gsBANcOAQCqDwEAKBAAAG8QAwClEAEAmBEBAAATAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/5dba02cd6a4e.js b/pr/234/docs/search.index/5dba02cd6a4e.js new file mode 100644 index 000000000..b43ab4bf9 --- /dev/null +++ b/pr/234/docs/search.index/5dba02cd6a4e.js @@ -0,0 +1 @@ +rn_("AYMCsCAQQgD8oJAAABKIbXABggKwoBKAAAGwoBKCAAFjcgECAaBAAAAQQWUBJQ8AADOAhLAgD9IAAbAgDrEAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/234/docs/search.index/5e7543ac82c6.js b/pr/234/docs/search.index/5e7543ac82c6.js new file mode 100644 index 000000000..2a1e3e29a --- /dev/null +++ b/pr/234/docs/search.index/5e7543ac82c6.js @@ -0,0 +1 @@ +rn_("AYkCsCASzAABoDAAABLVaXATAAOgIAAAEaqggAAAEb1laXX1oAsAAC0AAQABAAEAAYYCsJAS7wABoAAAABFOYmQBAgD0ogsAAAEAAgMBAAEAADswAAABAAAGAAIA+xEAAF0SBQAjgQKwQBLHAAFlc1OAA7AACR0AAWV4eQGAArAgEaIAAbAACRsAAXJ4pwAAAMWwwBIkAAGwQAxUAAGgMAAAD8sABIAL8qgFAAABAHMAhKAwAAAR7xAAEzowAAABAAAAAAAIABAAAADnCBUJFgmnC2EPZg8REBIQhRE=") \ No newline at end of file diff --git a/pr/234/docs/search.index/5fb178f0c084.js b/pr/234/docs/search.index/5fb178f0c084.js new file mode 100644 index 000000000..9e291a145 --- /dev/null +++ b/pr/234/docs/search.index/5fb178f0c084.js @@ -0,0 +1 @@ +rn_("AQEA9G4HAABmB8UAAQABBQD0thEAAAEAgAABAPMCAWPz5w8AAAEAAQBjAASgMAAAC7mgcAAAEJhlbnN281kPAACFANUB") \ No newline at end of file diff --git a/pr/234/docs/search.index/6015e8720a21.js b/pr/234/docs/search.index/6015e8720a21.js new file mode 100644 index 000000000..b4dd2ce8e --- /dev/null +++ b/pr/234/docs/search.index/6015e8720a21.js @@ -0,0 +1 @@ +rn_("AQMBgBHyABDhczswAAABAAAFAAEA1xEFACOCAqCQAAASvWlzAYACsKAScgABsBASIgABY24BhAKwoBKAAAGwoBKCAAFjcgECADswAAABAAAGAAEARQ8GABOCAqAgAAASZWV1AYEDsBARwgABsDASnQABsCAQhQABYWNzAQMA9EcMAAABAAEAAQABgAOwEBBYAAGgwAAAEsmwEAkEAAFucHUBgAKwMA8FAAGhYAAAEvthZQGAA7BQD/EAAaCwAAAShrAwEekAAWFudAEIAPPmEQAABwASABOAArBAEKMAAWFlAQABoCAAAA9vcgElEAAAAQAA9DMSAAABAEkAAQCnAACAhaAwAAAPzqAQAAAP5bCgEo8AAQkgIQEJAaBAAAASwGXy3xIAAAEAQ4CEoCAAABLOoJAAABIyoTAAABLeEEkAAYACsCASWwABsEARpAABaXkBAQKgMAAAEYWgQAAAEcFncjswAAABAAAHAAIAzw8BAFIQBQABgAKwABJNAAGwABJPAAE0NgEGAPQHEwAAAQAKAAEA8wICcnYBdxIAAPMCAWX0Xw8AAAEAmAABAPOAAmR0560/ABKgMAAAD+agQAAADFqw8BMEAAGwcBH8AAEwYWJjZWdoaWtsbm9wcnN0dXY7MAAAAQAA5gA/ABIFAADcBQEA4wUDANkGAAA0BwAAVwcCAGcHAABzBwQAeQcAANkHAAAACAEA6QgBAO8IAQD5CAAAUgkBAFYJdQCSCwEAnwsAAKgLAADbCwAA5wsAAKoOAQDgDgAAPg8AAHEPAAB+DwEAwg8BAMYPAwDVDwMABRABACMQAAA9EAIAgRAAAKEQAACxEAQAPxEAAEsRAQBOEQAAWBEAAGMRAACWEQEArREBALQRAQC6EQEAvREAAMARAADREQAA5BEBAPkRAQAJEgQAIBIBADESAAAzEgEAORIBAEQSAABxEgAAfRICAIQSAQCbEgEApxIAALoSAQDhEgEAGxMAAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/60416c1e7d76.js b/pr/234/docs/search.index/60416c1e7d76.js new file mode 100644 index 000000000..18a866489 --- /dev/null +++ b/pr/234/docs/search.index/60416c1e7d76.js @@ -0,0 +1 @@ +rn_("AQYA9E0QAAABAAEAAQABAgA7MAAAAQAADAAFAJsLAQBnDwUAXhEBALARAQC0EgAA84ACZm4BAQA7MAAAAQAABwACAJ8OBQDVEQEA84ACZG4BAQDzPgcAAAEAAQABhgKgAAAAEdGgAAAAEdJkcvMBAXYB9REAAEMAA7CAEXEAAbCwEuEAAWtscjswAAABAAAMAAMANQcIAG8MAQAxDwEAAQIA9fsHAAABAAEAAQABAPMAhBRBAAFlAAAA") \ No newline at end of file diff --git a/pr/234/docs/search.index/604a3991077d.js b/pr/234/docs/search.index/604a3991077d.js new file mode 100644 index 000000000..956246c7c --- /dev/null +++ b/pr/234/docs/search.index/604a3991077d.js @@ -0,0 +1 @@ +rn_("AQEBsIAR5AABdfInCQAAAQAjgAKwEAklAAEzNgEGAPPEDwAAAQDxABOAArAADrEAAW91AQMA81kRAAABAAEAI4ACsAAQewABaGkBAAA7MAAAAQAAEwABAIsHEwAjgAKgMAAADEthZfOAAnJ0E4ACsDASJgABYWwBAAGggAAAEwpm8uYIAAAgCQEHAPXdEQAAAQABAAEAAQAjgAKgUAAAEChhb/MCAmZzOzAAAAEAAAgABABaBwAAhQcFAG0MAABaEAAAE4ACoEAAAAueYW8BAwGwQBLWAAF08kISAAABACOFAqBAAAAScGJzEwACoFAAABA1Ymb38QgAAAEAQQBqBgEAVQABABOBAqBQAAAQrmd0AYACoEAAABChoCAAABJRYWkBAgD0IQkAAAEA7QYBAAECAPPTCwAAAQABABOAA7AQCR8AAbAwEoIAAWl1eQECADswAAABAAALAAIAOxIFAOgSBQDzCAFtOzAAAAEAAAwAAwAyEAAAcxEFAKkSBQAjgAKgoAAAEWJucre1AQCNoUAAABLjsDAQSAABoCAAAAkjsCAHXgHGt5UOOzAAAAEAAAACBQC7AQAA3QEZAR0HAAB7BwAAzAnjAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/6059e72d2543.js b/pr/234/docs/search.index/6059e72d2543.js new file mode 100644 index 000000000..c735e4564 --- /dev/null +++ b/pr/234/docs/search.index/6059e72d2543.js @@ -0,0 +1 @@ +rn_("AQIA9G8QAAABAAEAAQABgAKwEBCFAAEpvu19LMhlcwEKAPMsEgAAAQABAAGAAqAwAAARWLAAEcIAAWNk1wEAgIWwMBKdAAEFEAY=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6192a27f0e53.js b/pr/234/docs/search.index/6192a27f0e53.js new file mode 100644 index 000000000..3d9cac6fe --- /dev/null +++ b/pr/234/docs/search.index/6192a27f0e53.js @@ -0,0 +1 @@ +rn_("AQQA9tgSAAABAA0AAQAiAAQAAQMA81MRAAABAAEAI4ECsSATDgABY2sTgAKgMAAAEpluclMAA6BAAAALp2VvcvTwDwAAUAGDAQEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/61f88a01075d.js b/pr/234/docs/search.index/61f88a01075d.js new file mode 100644 index 000000000..b009217e1 --- /dev/null +++ b/pr/234/docs/search.index/61f88a01075d.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABMKZgEGEgAAE4ACoOAAABLUYnABAwGwABKOAE1z86wPAAABAJQCAYACsEARTwABoBAAAA/icHIBgQOgAAAAEEewAA7IAQWgEAAADsBlc3XzgANhb3IBAgD1rg8AAAEAAQAIAgEAAQQAOzAAAAEAAAkAAQAXEAkAAYECoJAAABJasFARPAABYnejgISwUBJ0AAGwkBGwAAFAIANXAwAAhqBwAAASUaBAAAASuTBBDDowAAABAAAAAAA3ABAAAAATBRQFwAXBBcIFwwXEBcUFxgXHBcgFhAcOCQ8JFAmxC7IL0wvUC9ULVAxVDMoNsw60DrUOyA7eDt8OIg+ND5sPnA+yD7MPug+7D80PTBCYEJ8QoBCiEHoRphGnEUUSRhJMEnQSdRKmEgcTCBMSExMT") \ No newline at end of file diff --git a/pr/234/docs/search.index/62e97f3b5f23.js b/pr/234/docs/search.index/62e97f3b5f23.js new file mode 100644 index 000000000..c180a6a25 --- /dev/null +++ b/pr/234/docs/search.index/62e97f3b5f23.js @@ -0,0 +1 @@ +rn_("AQABoIAAABMKZvLmCAAAIAkBBwD13REAAAEAAQABAAEAI4ACoFAAABAoYW/zAAJmczswAAABAAAIAAQAWgcAAIUHBQBtDAAAWhAAABOAAqCQAAARYmR5") \ No newline at end of file diff --git a/pr/234/docs/search.index/63d0f2065b1f.js b/pr/234/docs/search.index/63d0f2065b1f.js new file mode 100644 index 000000000..f83de5abd --- /dev/null +++ b/pr/234/docs/search.index/63d0f2065b1f.js @@ -0,0 +1 @@ +rn_("AYkCsCASzAABoDAAABLVaXATAAOgIAAAEaqggAAAEb1laXX1oAsAAC0AAQABAAEAAYYCsJAS7wABoAAAABFOYmQBgAKwMBGiAAGgMAAAD8tydnMChKAwAAAR7xAAEzowAAABAAAAAAAHABAAAADnCBUJFglhD2YPERASEIURAQMBsDARuAABc/OfDwAAAQABAAEAADswAAABAAAKAAMANhACACgSAwD8EgMA0wCEsCAIBgABEBgB9fcLAAABAAEAAQABAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/6417a7048837.js b/pr/234/docs/search.index/6417a7048837.js new file mode 100644 index 000000000..692bbc4f8 --- /dev/null +++ b/pr/234/docs/search.index/6417a7048837.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABKZZfLDEgAAAQABBAGwABGWAAFk8kgRAAABAKcAAAKFsKASsQABsEARlAABsHASZgABFYgAAYcSAAABAAGwgBKbAAFw8pcSAAABAEMAA6AgAAAQJaBAAAARSmRlc/PlDgAA3wIBAPOAAmVp") \ No newline at end of file diff --git a/pr/234/docs/search.index/64aa132e56e9.js b/pr/234/docs/search.index/64aa132e56e9.js new file mode 100644 index 000000000..6cfd5a1a1 --- /dev/null +++ b/pr/234/docs/search.index/64aa132e56e9.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAANcO2A6qD6sPKBClEKYQmBGZEQATARPzAQF58nISAAABACMAA6BAAAAQQKBQAAASuWVvc/K/DgAAeQI=") \ No newline at end of file diff --git a/pr/234/docs/search.index/64f30fe8b248.js b/pr/234/docs/search.index/64f30fe8b248.js new file mode 100644 index 000000000..4db274e0e --- /dev/null +++ b/pr/234/docs/search.index/64f30fe8b248.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwCCEAIA8BEBAHoSAgABAAKgYAAAEYWwQBCFAAFhcvPCDwAAAQCLAQGAAqEQAAAS97CAEu8AAWl1NwQAgoexUBMSAAGACP4AFqOwcBJFAAGhMAAAEwYKQBsTgAKwEBGRAAFpcg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/67a6347fe3ae.js b/pr/234/docs/search.index/67a6347fe3ae.js new file mode 100644 index 000000000..85b88e284 --- /dev/null +++ b/pr/234/docs/search.index/67a6347fe3ae.js @@ -0,0 +1 @@ +rn_("AYkCsCASzAABoDAAABLVaXATAAOgIAAAEaqggAAAEb1laXX1oAsAAC0AAQABAAEAAYYCsJAS7wABoAAAABFOYmQBgAKwMBGiAAGgMAAAD8tydnMAhKAwAAAR7xAAEzowAAABAAAAAAAHABAAAADnCBUJFglhD2YPERASEIUR") \ No newline at end of file diff --git a/pr/234/docs/search.index/68314aeade4e.js b/pr/234/docs/search.index/68314aeade4e.js new file mode 100644 index 000000000..fe31d3c97 --- /dev/null +++ b/pr/234/docs/search.index/68314aeade4e.js @@ -0,0 +1 @@ +rn_("AQgA9FISAAABAEwAAQABAgGgYAAAEu5z8pIQAAABAAGAArAgDwwAAaCQAAASpmFlAQcBsEASwQABZfWJEgAAAQABAAEAAQABAwGwQBLWAAF08kISAAABACOFAqBAAAAScGJzAQIA9I4QAAABAAEAAQA3dAAAi7BAEiQAAaBQAAAQNaCQAAASGrBQEZEAAaAwAAAL7LOYBzswAAABAABPAA4A6wMCAPUFAADaBgEA8QgBAPsIAAAGCQAAMwkAAAMMNgBMDAAAnQ8BALgPAQDzDwEAahAEAE8RAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/683f4785394c.js b/pr/234/docs/search.index/683f4785394c.js new file mode 100644 index 000000000..d5701122a --- /dev/null +++ b/pr/234/docs/search.index/683f4785394c.js @@ -0,0 +1 @@ +rn_("AQQBsAARpgABczswAAABAAALAAIAEhIHAPMSAwBHAAAFhqCAAAAR+LEAEwAAAbBAEW0AAbCAEw4AAbBQEagAARUAFvTGDQAAAQABAAEAQwCEoCAAAAkToNAAABH+oDAAAAj0FQAE88ABAAABALkF") \ No newline at end of file diff --git a/pr/234/docs/search.index/69af465a5e19.js b/pr/234/docs/search.index/69af465a5e19.js new file mode 100644 index 000000000..0c39b738e --- /dev/null +++ b/pr/234/docs/search.index/69af465a5e19.js @@ -0,0 +1 @@ +rn_("AQkA89gSAAABADQAAQwBoKAAABMbd/ILEwAAAQABAQDz5g4AAAEAAQDzgAJ0dQGBArCAEuYAAaBgAAATCWRzNwIAgIagQAAAErywYBKOAE2wMBKAAAECkA4BAAGwUA/aAAFj9GgHAAABAAEA3wHDgISgEAAAC/SgUAAAEToMQAQ=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6a4dbb0f44b4.js b/pr/234/docs/search.index/6a4dbb0f44b4.js new file mode 100644 index 000000000..57b906fb2 --- /dev/null +++ b/pr/234/docs/search.index/6a4dbb0f44b4.js @@ -0,0 +1 @@ +rn_("AQABoDAAAAxAaQHvEQAAAQABsDAQEwABbvJlBwAAAQABAQDz2QcAANEGAQATAAKgUAAAD4Blc/XfAwAAAQABAAEAAQABhAKwoBKAAAGwoBKCAAFjcvMBAXTyoRAAAMIAAQMBsGASxQABZvWYCwAAAQABAN4GAQDzgAJhZfOAhAAoBQ==") \ No newline at end of file diff --git a/pr/234/docs/search.index/6ae1dbe7be1c.js b/pr/234/docs/search.index/6ae1dbe7be1c.js new file mode 100644 index 000000000..8ab8e8d7e --- /dev/null +++ b/pr/234/docs/search.index/6ae1dbe7be1c.js @@ -0,0 +1 @@ +rn_("AQACoDAAAA/moJAAABH/MGj1sRAAAFgBAQABAAEAE4ACoDAAAA7FZHIjAISwEAjjAtewIAuUAOSgMAAADuESABj10gUAACcAAQABAAEAAQIA9AcTAAABAAoAAQABAgDzVA8AAAEAAQDzgAJpcgEBArBAECYAAaBQAAAR7mVz88UNAAB0AgEAAQsA9g4SAAABAJkAUgAWAAEA84ACbHTTgISgkAAAEcwBRAQ=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6bf9d14a8094.js b/pr/234/docs/search.index/6bf9d14a8094.js new file mode 100644 index 000000000..a77cf44e6 --- /dev/null +++ b/pr/234/docs/search.index/6bf9d14a8094.js @@ -0,0 +1 @@ +rn_("AQIBsIARygABdvROCQAAAQAXCQEA8wABYwF5EQAAAQICoLAAABKzoLAAABK0Y3Pzow8AAAEAUwABgQKwUBCjAAGgwAAAEoZoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6dcb10ceafda.js b/pr/234/docs/search.index/6dcb10ceafda.js new file mode 100644 index 000000000..4f2c5ebed --- /dev/null +++ b/pr/234/docs/search.index/6dcb10ceafda.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAAUAAQAAEgUA8wUBdDswAAABAAAIAAIAlQsAALsLBwATgISwMA82AAGgoAAAEjWgMAAAEnAAQUg=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6e2902148b8e.js b/pr/234/docs/search.index/6e2902148b8e.js new file mode 100644 index 000000000..ca97f330e --- /dev/null +++ b/pr/234/docs/search.index/6e2902148b8e.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAYAAgDrBQUAQxAAAAEEAbAAEaYAAXM7MAAAAQAACwACABISBwDzEgMARwAAAYaggAAAEfixABMAAAGwQBFtAAGwgBMOAAGwUBGoAAEVABb0xg0AAAEAAQABAAEFAPPrDgAAAQABAGOAA7BAD2QAAW1ubwGBArAADFQAAbAQD2IAAXN18wACYWk7MAAAAQAACQAEAG4HAADUDgAAmQ8BAAASBQABgAOwABJNAAGwABJPAAGwcBK6AAE0NmEBBAD0BxMAAAEACgABACOBAqBAAAAQI21wAQIAOzAAAAEAAA4ABABxBwEAcQwBABcQCQBLEAAAAQEAOzAAAAEAAAgAAwBiEQAAyBEAABQTBgABAQA7MAAAAQAACgADADYQAgAoEgMA/BIDACOAAqAwAAAPPmRn1wMAAMagYAAAEnFARYAIOzAAAAEAABMACADiDgEAExABAIcQBgAbEgAALBICAHcSAADKEgEA+BIBAAEDAPQBCQAAUACyBgEAE4ACsHASmwABb3IBBAGwsBLhAAFs9G8MAAABAMECAQABAgA7MAAAAQAADAADAOQIAAA7EgUA6BIFAAEBAPMoDwAAAQAOAiOAAqCwAAASmmd6AQIAOjAAAAEAAAAAAAkAEAAAAJUQlhCDEYQRUhJTEp8SoBIOEw8T8wABb/JUCQAAAQABAISgQAAADGWwcBHnAAGwIAv1AAGwUA+sAAEQBQI7MAAAAQAACAADAAoFAwC3BQIAhRABAAGAAqAgAAAP7bAgEpsAAWR0AYQCsLAS5AABsAASRQABYXPTgISgsAAAEs5AKAQBgAKgMAAAEKewYA7pAAFockf/AQDNoCAAAAXToCAAAAXUsHASOQABA2c0BzswAAABAAA0ABcA7QQAACIFBQBKCQAAQgwAAK4OAQCFDwMAMhAAAGIRAABzEQUAyBEBAOYRAADtEQAA/xEAABASAQAaEgAAHhIBAHASAAB2EgAAghIBAJcSAQCpEgUAvRIAABQTBgA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/6f777c411721.js b/pr/234/docs/search.index/6f777c411721.js new file mode 100644 index 000000000..6eaa95c10 --- /dev/null +++ b/pr/234/docs/search.index/6f777c411721.js @@ -0,0 +1 @@ +rn_("AQMBgBHyABDhczswAAABAAAFAAEA1xEFACOCAqCQAAASvWlzBwEAhMWwUBGkAAGgoAAAEjKwcBH8AAGg0AAAEsmAARgI") \ No newline at end of file diff --git a/pr/234/docs/search.index/6fa98d7155d9.js b/pr/234/docs/search.index/6fa98d7155d9.js new file mode 100644 index 000000000..17c94e27c --- /dev/null +++ b/pr/234/docs/search.index/6fa98d7155d9.js @@ -0,0 +1 @@ +rn_("AYMCoFAAAA/foKAAABHMa3ABAAGwQBEwAAFs9EwJAAABAGkGAQABCAD0RBEAAAEAAQABAAEEAbAAD8AAAXMBPQ8AAAECAaBQAAARk2MBQQwAALMAhLBADHYAAZUAADswAAABAAAIAAQAFgUAAM8FAgBEDAEAuQ4CACcBAICFsDAJEQABoBAAABH4sCAHfQABkQBEARAA9PMSAAABAAEAAQABAwD0TAkAAAEAaQYBAPOAAnJ1AQACsNASMwABoDAAAA68Ymr2tAUAAAEAAQBBAAEAUgEBAAGwgBJSAAF0OzAAAAEAADkABAD1BQAA+wgAAAYJAAADDDYAAQkA81kRAAABAAEA84ADY2d0AYMCoJAAABKToJAAABKUMjMBgAKgUAAAEr2ggAAAEfVjZfMAAnN08ksHAAABAAEAAaCwAAASNWL10hEAAMMAAQBGAAEAI4ACoAAAABHRZHIjgAKgUAAAEodjZfOAAml5AQQA9QcQAAABAAEAAQABAPMBAW4BRQcAAAEBADswAAABAAAJAAIA6QcHAJoRAQABBgD0KBIAAAEAAQABAAEPAPT8EgAAAQABAAEA84ECaG0BAAGwQBLBAAFl9YkSAAABAAEAAQABAFOAA6AgAAAQmGxudNdtAIDLoCAAABLusGAPpQABsEAO6QABwBs4Cg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/71761381578a.js b/pr/234/docs/search.index/71761381578a.js new file mode 100644 index 000000000..1b4260cad --- /dev/null +++ b/pr/234/docs/search.index/71761381578a.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwCCEAIA8BEBAHoSAgABAAKgYAAAEYWwQBCFAAFhcvPCDwAAAQCLAQGAAqEQAAAS97CAEu8AAWl1NwQAgYexUBMSAAGACP4AFqOwcBJFAAGhMAAAEwYKQBsBAAA7MAAAAQAAdQABAE8NdQABAgD2DhIAAAEAmQBSABYAAQAjgQKgIAAAD9xydvOAAmVvI4ACoCAAABEyZHQBAAA7MAAAAQAA1QADAPYFAAAICNMAAgwAAPOAA2Rudg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/724bf250813c.js b/pr/234/docs/search.index/724bf250813c.js new file mode 100644 index 000000000..f0df50497 --- /dev/null +++ b/pr/234/docs/search.index/724bf250813c.js @@ -0,0 +1 @@ +rn_("AQACoDAAAA/moJAAABH/MGj1sRAAAFgBAQABAAEAE4ACoDAAAA7FZHIjAYSwEAjjAtewIAuUAOSgMAAADuESABj10gUAACcAAQABAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/739826fcec2f.js b/pr/234/docs/search.index/739826fcec2f.js new file mode 100644 index 000000000..bdf761abf --- /dev/null +++ b/pr/234/docs/search.index/739826fcec2f.js @@ -0,0 +1 @@ +rn_("AQUA9GwSAAABAEIAAQDzBAFj8o8RAAABACOCAqBQAAARY2Z0AQABsAASugABZPSOCwAAAQABAAEA84ACZWkTgAKw4BLFAAF0dg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/74051be7e32a.js b/pr/234/docs/search.index/74051be7e32a.js new file mode 100644 index 000000000..dcd17052c --- /dev/null +++ b/pr/234/docs/search.index/74051be7e32a.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABKToJAAABKUMjMBgAKgUAAAEr2ggAAAEfVjZfMAAnN08ksHAAABAAEAAaCwAAASNWL10hEAAMMAAQBGAAEAI4ACsAAO4ALxZHIBAAGwgBKbAAFw8pcSAAABAEMAA6AgAAAQJaBAAAARSmRlc/PlDgAA3wIBABOCAqAgAAASh2Vp84ACY2UBBAA7MAAAAQAACQAEAD8JAQB3DwMAtA8BAFYSAQDzgANlaXk=") \ No newline at end of file diff --git a/pr/234/docs/search.index/740bdb0f158e.js b/pr/234/docs/search.index/740bdb0f158e.js new file mode 100644 index 000000000..dd5423820 --- /dev/null +++ b/pr/234/docs/search.index/740bdb0f158e.js @@ -0,0 +1 @@ +rn_("AQ0A9UcSAAABAAEAbAABAAEAArAwEX0AAbAgETUAAWJ1AQQPAAAjhAKwMBEzAAFkcwECAPP1CAAAAQBSAPOAAml0ARgA8xgTAAABAAEAAYACsLASRQABoMAAABH5ZXXzgQJjbQEFAaBgAAARWHLyAg8AAAEAAQkA9nsRAAABANwAewAvAAEAE4ACsGAPnQABZG4BAwGgcAAAEapn8kYJAAABAAEJAPPYEgAAAQA0AAGBArCAEuYAAaBgAAATCWRzFwEAiYWw0BMLAAGgQAAAErywYBKOAE0CkApnXgAAi7BwD/UAAbCwEbgAAaCgAAARerDwEnQAAW2BD/RvAAAAAQABAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/74722d2b5b62.js b/pr/234/docs/search.index/74722d2b5b62.js new file mode 100644 index 000000000..a501174c2 --- /dev/null +++ b/pr/234/docs/search.index/74722d2b5b62.js @@ -0,0 +1 @@ +rn_("AQUA9G8MAAABAMECAQABAAD04gsAAAEAAQBfAAECADowAAABAAAAAAAJABAAAADSDtMOqQ9REHsRfBFYEtMSAhMDE8cBAACGsDAJBAABoEAAABCosIAQlQABGEAHOjAAAAEAAAAAABcAEAAAAG8HcAf6BxAJPwlACW4MwQ7CDtsOcA93D3gPeQ96D7QPtQ/jD+QPLRFWElcSgBKBEg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/749bbd73b08e.js b/pr/234/docs/search.index/749bbd73b08e.js new file mode 100644 index 000000000..9109b512f --- /dev/null +++ b/pr/234/docs/search.index/749bbd73b08e.js @@ -0,0 +1 @@ +rn_("AYACoAAAAA9toAAAAA9uNDYBgAOwABJNAAGwABJPAAGwcBK6AAE0NmEBBAD0BxMAAAEACgABACOBAqBAAAAQI21wAQEAOzAAAAEAAAgAAwBiEQAAyBEAABQTBgDHAQACxaBAAAAPPqBgAAAScUAFgAg7MAAAAQAAEQAHAOIOAQCHEAYAGxIAACwSAgB3EgAAyhIBAPgSAQDzAAJldjowAAABAAAAAAANABAAAADQAdEB4QVsDKwOrQ7GDtkOXw9gD/gP+Q+PEpASAQMCsEARogABoDAAABHvZXf0YQ8AAAUAqwABABOBAqBwAAASHWdvI4ACsFAO4gABZHSTAISgEAAADz6xIBLKAAERIAH0jhAAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/74bdadfc1d31.js b/pr/234/docs/search.index/74bdadfc1d31.js new file mode 100644 index 000000000..4d6a08bc4 --- /dev/null +++ b/pr/234/docs/search.index/74bdadfc1d31.js @@ -0,0 +1 @@ +rn_("AQMA8+ILAAABAAEAE4ACsDAL5QABaW8BAQGwUA/aAAFj9GgHAAABAAEA3wEjgQKgUAAAD6JjbgEEADswAAABAAAHAAMAghACAPARAQB6EgIAAQEA9KsRAAABAOUAAQDzCQFpAdkPAADXAgAAh7DAEekAAaBQAAAOsKEQAAASuRQBFzswAAABAAAjAAEAegEjAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/74fafa2468b8.js b/pr/234/docs/search.index/74fafa2468b8.js new file mode 100644 index 000000000..bebe89b35 --- /dev/null +++ b/pr/234/docs/search.index/74fafa2468b8.js @@ -0,0 +1 @@ +rn_("AQIA9WoQAAABAAEAAQABAAGAArBgEZwAAaAgAAAP0W95AQABoMAAABKaafTmEQAABwASABsAAQMAOzAAAAEAAAUAAQAAEgUAAQYA9PwSAAABAAEAAQABAgGgQAAAEcFyOzAAAAEAAAcAAgDPDwEAUhAFABOAArAgEFgAAWFvZwcAAIewgBHAAE2woBImAAEMMAc7MAAAAQAADgAEABUEBQCkCwAAfRADAEQRAwCjgISg8AAAEjagUAAADziUAQA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/7561fbf6279c.js b/pr/234/docs/search.index/7561fbf6279c.js new file mode 100644 index 000000000..f8045a7e7 --- /dev/null +++ b/pr/234/docs/search.index/7561fbf6279c.js @@ -0,0 +1 @@ +rn_("AQEA9+oPAAABAAEAdwIBAHoABQABAgD0iQ8AAAEAAQABABOAAqDQAAASzmNz84ACZW8jgQKwAAdfAAFsdAEAAPTjBQAAAQABAAEAU4ADoFAAABMbZG5y") \ No newline at end of file diff --git a/pr/234/docs/search.index/75aa6f082a69.js b/pr/234/docs/search.index/75aa6f082a69.js new file mode 100644 index 000000000..8501a3b83 --- /dev/null +++ b/pr/234/docs/search.index/75aa6f082a69.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQA/DwUAMBABAKsQAgBcEQEAihEEAGgSAQCIEgAAsxIAANISAAAjgAOh8AAAExuw8BMOAAFjZmcjgAKwUBImAAFtbgEFAPNoEgAAAQBpAPMEAWMBShAAAAEBAbBwErcAAXA7MAAAAQAACQACANoHBwClDwEAAQIAOzAAAAEAAAsAAgA7EgUA6BIFAPMBAW07MAAAAQAADQAEAEIMAAAyEAAAcxEFAKkSBQATAAKgMAAADEZlc/KvEAAAAQABggKwcBKXAAGgQAAAD+Jpc/cBAICGsJASgAABkEkI") \ No newline at end of file diff --git a/pr/234/docs/search.index/75bce28ef9fb.js b/pr/234/docs/search.index/75bce28ef9fb.js new file mode 100644 index 000000000..8f8928edb --- /dev/null +++ b/pr/234/docs/search.index/75bce28ef9fb.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABHJc/THDgAAaAMBAAIAAQECsGASnQABsEAS1gABZHQ7MAAAAQAADQAGAIYRAwDiEQEAQhIBAFkSAQDMEgEA7xIBAAEBA7BwEK8AAbCAEWQAAYAR8gAQ4W1uczswAAABAAALAAIA3QgFANcRBQCjgISgsAAAEjGxcBMOAAEIQIIBAQA7MAAAAQAABgABABQTBgDzBgFl9MoSAAABAC0AAQATAQOwQBLxAAGgYAAAEnZhcnUBXA8AAFcBAACFsRASnQABsDARrwEmEYEIOzAAAAEAAAYAAgD7EQAAXRIFAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/7880cad733fe.js b/pr/234/docs/search.index/7880cad733fe.js new file mode 100644 index 000000000..466658e8c --- /dev/null +++ b/pr/234/docs/search.index/7880cad733fe.js @@ -0,0 +1 @@ +rn_("AYECoGAAABG9sAARNQABYmkBAAD03g4AAAEA2wDoACOCArBQEc8AAWVzAYkCsCASzAABoDAAABLVaXABhQKwMBJbAAGwIBIiAAFidSOAArAgEX0AAWlstwEAAIWgIAAAEaoSAQw6MAAAAQAAAAAAFAAQAAAAeAcCCAMIBAgFCKALzQvOC88L0AsED7EPHBKOEtgS2RLbEuYS5xIJEw0TAQUA83oSAAABAAEAQ4YDsJAS7wABoAAAABFOYmR3AQMA89wOAAABAO4AAQIA9qILAAABAAIDAQBWAwEAAQAAOzAAAAEAAAYAAgD7EQAAXRIFACOBArBAEscAAWVzU4ADsAAJHQABZXh5AYACsCARogABsAAJGwABcngBgAKgQAAADFqwQA8FAAFnbFcDAADGsMASJAABsEAMVAABAAaACzowAAABAAAAAAAHABAAAACoBakFawzaDi8PMA+eEZ8RAQMBsGASIAABYvIzEQAAAQA3DAAAiaBwAAARP7AQCQIAAbCQEc0AAbBgDygAAaAwAAAR7ziJEzowAAABAAAAAAAlABAAAAD0BUMHRAfnCBUJFgmnC2IMYwxkDGgMaQxqDCYPJw8qDysPRQ9GD0cPSA9JD0oPSw9hD2YPgA+sD60PERASEIURHRJBEs8S2hL3EgYT") \ No newline at end of file diff --git a/pr/234/docs/search.index/794cab3d1528.js b/pr/234/docs/search.index/794cab3d1528.js new file mode 100644 index 000000000..15c09c30e --- /dev/null +++ b/pr/234/docs/search.index/794cab3d1528.js @@ -0,0 +1 @@ +rn_("AQMBoAAAAA8ic/OuCwAAAQABAAECAPOtCwAAeQQBACMAAqBAAAAMXGVy8gcFAAABAAGAAqBAAAASRLAwEYEAAWFzAQIBoEAAABLAZfLfEgAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/234/docs/search.index/79753bcfbf4f.js b/pr/234/docs/search.index/79753bcfbf4f.js new file mode 100644 index 000000000..66908fdfd --- /dev/null +++ b/pr/234/docs/search.index/79753bcfbf4f.js @@ -0,0 +1 @@ +rn_("AYQCoCAAAA/voDAAABBMaXQBAQA7MAAAAQAAdgACAHkHAABWCXUA8wACZWzzDRAAAPoBAQABAgA7MAAAAQAA1AACAJ0LAAB7DNMAxwAAAIWwsBG+AAGgEAAAEjGwMAx5AAEUMAI7MAAAAQAAAAELAKABBgDWBQAA/QXTAE0HBwAsDwIAWxANAIIQAgA8EQEAqBEBAPARAQB6EgIA") \ No newline at end of file diff --git a/pr/234/docs/search.index/79c04eb28188.js b/pr/234/docs/search.index/79c04eb28188.js new file mode 100644 index 000000000..46a3400b9 --- /dev/null +++ b/pr/234/docs/search.index/79c04eb28188.js @@ -0,0 +1 @@ +rn_("AQIA9WoQAAABAAEAAQABAAGAArBgEZwAAaAgAAAP0W95AQABoMAAABKaafTmEQAABwASABsAAQYA9PwSAAABAAEAAQABAgGgQAAAEcFyOzAAAAEAAAcAAgDPDwEAUhAFABOAArAgEFgAAWFvZwMAAIawgBHAAE2woBImAAEMMAY7MAAAAQAACAADAKQLAAB9EAMARBEDAPOAAmVp") \ No newline at end of file diff --git a/pr/234/docs/search.index/7b2046dab109.js b/pr/234/docs/search.index/7b2046dab109.js new file mode 100644 index 000000000..4625edf38 --- /dev/null +++ b/pr/234/docs/search.index/7b2046dab109.js @@ -0,0 +1 @@ +rn_("AYMCsQATEAABsNATAgABa20BCgDzaBIAAAEAaQATgAKiEAAAExtsbzMBhKBgAAARLaEAAAAS1ASAAzswAAABAAA0AAIAXAcAAKUHMwA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/7b3aaaa369a3.js b/pr/234/docs/search.index/7b3aaaa369a3.js new file mode 100644 index 000000000..f066612af --- /dev/null +++ b/pr/234/docs/search.index/7b3aaaa369a3.js @@ -0,0 +1 @@ +rn_("AQQA9M8SAAALAB0ADwABAgGwQBFRAAFlAcQOAAABgAKgkAAAEpOgkAAAEpQyM5cCAADGoCAAAAvxoGAAABANsGAMcQABAWQAAzswAAABAAAGAAIAKQcFAMMLAAA=") \ No newline at end of file diff --git a/pr/234/docs/search.index/7bb9fa36e896.js b/pr/234/docs/search.index/7bb9fa36e896.js new file mode 100644 index 000000000..5b02c02bf --- /dev/null +++ b/pr/234/docs/search.index/7bb9fa36e896.js @@ -0,0 +1 @@ +rn_("AQMA9msMAABvAlUAAQBuAgEAAQQAOzAAAAEAAAoABABnDwUAXhEBALARAQC0EgAAAYYCoAAAABHRoAAAABHSZHLzAgF2AfURAADzgAJlbycBAADHoDAAAA7ZoHAAABE6oIAAABKHoNAAABIxoLAAABKHQEUICTswAAABAAAJAAMA8QUCAC8HBADcCwEAAQYA9AcTAAABAAoAAQDzAgFyAXcSAADzAwFl9F8PAAABAJgAAQATgAKwsBKPAAFhZfOAAmRyAQAAOjAAAAEAAAAAAAoAEAAAADQJNQkLD+cR6BEOEg8SqBL6EhATERMTgAKgMAAAD31sdPOAAmFl") \ No newline at end of file diff --git a/pr/234/docs/search.index/7bcd03fe6a97.js b/pr/234/docs/search.index/7bcd03fe6a97.js new file mode 100644 index 000000000..33959219c --- /dev/null +++ b/pr/234/docs/search.index/7bcd03fe6a97.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAADcAAgAGCQAAAww2AAGAArBQD5sAAbCwEhAAAWFsYwAEsEAOrgABoBAAAA9aYnBydjswAAABAAAGAAEAKRAGAAGAArCAD94B1bDgEnIAAWlyAQMBsEAQJgABZQHFDQAAAQUAOzAAAAEAAAYAAQBFDwYAAQMA9EcMAAABAAEAAQDzAAJwczowAAABAAAAAAANABAAAADcBd0F2Qb5CNsL5wvVD9YP1w/YD0sRTBGWEZcRVwAAAIWwUA+NAe2wUA7JALKgQAAAEEtIgAM6MAAAAQAAAAAACwAQAAAA8QSfBaAFoQWiBbYOmRCaEEgRSRFvEXAR0wCEoGAAAAxZEAgG83ABAAABAMsK") \ No newline at end of file diff --git a/pr/234/docs/search.index/7c75104910e5.js b/pr/234/docs/search.index/7c75104910e5.js new file mode 100644 index 000000000..3a6494d5e --- /dev/null +++ b/pr/234/docs/search.index/7c75104910e5.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABKZZfLDEgAAAQABBQD0shAAAAEAAQABAAGDArAAD8IAAbBQEfAAAWR3E4ACoKAAABJZZXYBAAD03g4AAAEA2wDoACOCArBQEc8AAWVzAYUCsDASWwABsCASIgABYnUjgAKwIBF9AAFpbDMAA7AgETUAAWJ0dTowAAABAAAAAAAPABAAAAB4BwIIAwgECAUIBA+xDxwSjhLYEtkS2xLmEucSCRMNEwEMAPN6EgAAAQABAAEDAbBgEiAAAWLyMxEAAAEAFwYAAIewQA7cAAGgcAAAET+wEAkCAAGwkBHNAAE4CQM6MAAAAQAAAAAAHAAQAAAA9AVDB0QHYgxjDGQMaAxpDGoMJg8nDyoPKw9FD0YPRw9ID0kPSg9LD4APrA+tDx0SQRLPEtoS9xIGEwEDAPS0EQAAAQAsAQEA84ACY213AQCAxbGAExYAAUCBBAg=") \ No newline at end of file diff --git a/pr/234/docs/search.index/7d2ea3138a91.js b/pr/234/docs/search.index/7d2ea3138a91.js new file mode 100644 index 000000000..de85512ad --- /dev/null +++ b/pr/234/docs/search.index/7d2ea3138a91.js @@ -0,0 +1 @@ +rn_("AQACoCAAABH+sDAQnwABaXTyfxEAAAEAAQMAOzAAAAEAAAYAAQApEAYAAQgA9GMSAAABAHoABQATAQOwkBK+AAGwUBG8AT9nbHA7MAAAAQAACgAFAE8PAgCrEQEAVBIBAJESAQDQEgEAAYACsDAQmwABsDAQnQABcnYBBAGwABFLAAFk9JkQAAABANUAAQC3BQABh7BQEU0A6bBgEboAAZQJAzswAAABAAAKAAUATQwCAMsNAQDZDwAATRADALIRAAAjgAKgEAAADExubwEDAPNMDwAAAQABACOAAqAAAAALn2RyYwGEoFAAABBKoDAAAA98FAEQ8p8HAAABABOAArBgEWQAAWNnE4ADoLAAABJ/oEAAAA3KZW9z") \ No newline at end of file diff --git a/pr/234/docs/search.index/7d7822a13b6a.js b/pr/234/docs/search.index/7d7822a13b6a.js new file mode 100644 index 000000000..c516bc567 --- /dev/null +++ b/pr/234/docs/search.index/7d7822a13b6a.js @@ -0,0 +1 @@ +rn_("AQQBsAARpgABczswAAABAAALAAIAEhIHAPMSAwBHAAAEhqCAAAAR+LEAEwAAAbBAEW0AAbCAEw4AAbBQEagAARUAFvTGDQAAAQABAAEA") \ No newline at end of file diff --git a/pr/234/docs/search.index/7d862dad6f48.js b/pr/234/docs/search.index/7d862dad6f48.js new file mode 100644 index 000000000..1df34991e --- /dev/null +++ b/pr/234/docs/search.index/7d862dad6f48.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHYAAgB5BwAAVgl1AAGEArBwEpcAAaBAAAAP4mlzAQcA88QPAAABAPEAI4ACoLAAABFiYXQBAAOwMAkJAAGwMBIHAAGgIAAAB3phZG8B0gEAALcBAICFsAASagABABgV") \ No newline at end of file diff --git a/pr/234/docs/search.index/7dc3252609e4.js b/pr/234/docs/search.index/7dc3252609e4.js new file mode 100644 index 000000000..5330eea6b --- /dev/null +++ b/pr/234/docs/search.index/7dc3252609e4.js @@ -0,0 +1 @@ +rn_("AQkA89gSAAABADQAAYACsCASgAABoCAAAA7MaXkBCQGggAAAEwpmAQYSAAABAwGwABKOAE1zAUESAAAjgAKgIAAADshhcgECAPWuDwAAAQABAAgCAQDzAANpdHU6MAAAAQAAAAAACwAQAAAAhAcUCSIPjQ96EaYRpxFFEkYSTBJ0EnUSAYACsEAPsgABoCAAABK8Y3QTgAKwsBIeAAFhbAEMAaCgAAATG3fyCxMAAAEAAQEA8+YOAAABAAEA84ACdHUBAwDzUxEAAAEAAQABAQD1BxAAAAEAAQABAAEAI4ACsJASbgABYXQBBAGgQAAAEsBl8t8SAAABAAECADswAAABAAALAAUAPw8FAFwRAQBoEgEAsxIAANISAAATggKw8BMOAAFmZwGBArCAEuYAAaBgAAATCWRzAQgA9aESAAABAAEAAQABAPMCAWb1cg8AAAEAAQABAAEAI4ADoCAAAA7GsKAS5AABZHJ0d+8BAI2wEBKbAAGw4BKdAAGX1Q47MAAAAQAAAgESAAgEAQAEBQIA2QUCANQGBADzBwMArgsCANkLAQBSDAEAzQ3RANwOAQCODwEAnw8CAMsPAQBKEAAAnRABAGwRAAASEgcA8xIDAA==") \ No newline at end of file diff --git a/pr/234/docs/search.index/7f27b460fa5c.js b/pr/234/docs/search.index/7f27b460fa5c.js new file mode 100644 index 000000000..bb921189b --- /dev/null +++ b/pr/234/docs/search.index/7f27b460fa5c.js @@ -0,0 +1 @@ +rn_("AQABoDAAABBHdPSbDwAAAQAWAAEAAYACoAAAAA9toAAAAA9uNDbzAwF2AWwMAAAXAQAAh7CgEaAAAbAgC6oAAbBgEZwAAbCAEYMAAaAgAAAP0QlERjowAAABAAAAAAAJABAAAAAaBxsHHAe4C+QOJBBBEUIRQxG5Eg==") \ No newline at end of file diff --git a/pr/234/docs/search.index/7f728bb3feca.js b/pr/234/docs/search.index/7f728bb3feca.js new file mode 100644 index 000000000..0f1a42443 --- /dev/null +++ b/pr/234/docs/search.index/7f728bb3feca.js @@ -0,0 +1 @@ +rn_("AYACsFASfQABoAAAAAjzY3QBAQA7MAAAAQAABQABAAASBQABAwD0XAwAANkFkgABAAGAAqAQAAAL9KBQAAAROmNkAQIA8+gIAADpAgEA8wABbfP3BAAArQABAAEAAbAwEcIAAXLyZwcAABoJ5wcAgIewMA7JALIpICM=") \ No newline at end of file diff --git a/pr/234/docs/search.index/alias/a762150c532c.js b/pr/234/docs/search.index/alias/a762150c532c.js new file mode 100644 index 000000000..f829e4636 --- /dev/null +++ b/pr/234/docs/search.index/alias/a762150c532c.js @@ -0,0 +1 @@ +rd_("") \ No newline at end of file diff --git a/pr/234/docs/search.index/crateNames/8a381fcf2bd4.js b/pr/234/docs/search.index/crateNames/8a381fcf2bd4.js new file mode 100644 index 000000000..59242fbfb --- /dev/null +++ b/pr/234/docs/search.index/crateNames/8a381fcf2bd4.js @@ -0,0 +1 @@ +rd_("ebenchdbulkkcertificatemdata_transferjiroh_quinnA`iroh_quinn_protoniroh_quinn_udpdperfjquinn_perfAaset_up_connection") \ No newline at end of file diff --git a/pr/234/docs/search.index/desc/f10ce68b8c31.js b/pr/234/docs/search.index/desc/f10ce68b8c31.js new file mode 100644 index 000000000..478d8e3ac --- /dev/null +++ b/pr/234/docs/search.index/desc/f10ce68b8c31.js @@ -0,0 +1 @@ +rd_("AmData flows in both directions0CdThe CE codepoint, signalling that congestion was \xe2\x80\xa600AjContains the success valueBeThe PathId of this path.AoGet the identity of this stream0AfWhich path is now openAjWhich path has been closedB`Which path had its state droppedBaPath for which the error occurredAmPath which has changed statusCfPath over which the observed address was reported, \xe2\x80\xa6AlWhich stream is now readableAlWhich stream is now writableAnWhich stream has been finishedAmWhich stream has been stoppedAnType of the Long header packetBcExperimental! Use at your own risk.0ClWhether a stream communicates data in both directions or \xe2\x80\xa60AhContains the error valueAoThe largest representable valueAlThe maximum path ID allowed.10ClConnects to a QUIC perf server and maintains a specified \xe2\x80\xa6BmData flows only from the stream\xe2\x80\x99s initiator0AnWhich directions data flows in0CaDirectionality for which streams have been openedCdDirectionality for which streams are newly availableCjExplicit congestion notification bits to set on the packet0CeThe Explicit Congestion Notification bits for the \xe2\x80\xa61B`The current best RTT estimation.BeThe amount of I/O operations executed0BcLength of QUIC packet being decodedAlLength of the packet payload0BmThe number of bytes the associated buffer hasBbSet a custom TokenLog0CaMinimum RTT registered so far for this estimator.ClConstruct with an approximate maximum memory usage and a \xe2\x80\xa6CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6DiCreates a new QlogConfig that writes a qlog trace to the \xe2\x80\xa6CiCreates a new qlog factory that writes files into the \xe2\x80\xa6D`Create a default config with a particular reset_keyCmCreate a default config with a particular handshake token \xe2\x80\xa6CkCreate a default config with a particular cryptographic \xe2\x80\xa6AmConstruct cid from byte arrayoConstruct emptyAeCreate a new StreamIdDjConstruct a state using the given config and current time \xe2\x80\xa600:8AeCreate a new endpointDcCreate a new instance of FixedLengthConnectionIdParserBdCreate a generator with a random key:987DgBegin decoding a QUIC packet from bytes, returning any \xe2\x80\xa67BkConstruct an error with a code and a reasonCgInitialize Random CID generator with a fixed CID length87666CkGet SystemTime::now() or the mocked equivalentAhLook up the current time01ChCurrent best estimate of this connection\xe2\x80\x99s latency \xe2\x80\xa6000ChThe ECT(0) codepoint, indicating that an endpoint is \xe2\x80\xa600ChThe ECT(1) codepoint, indicating that an endpoint is \xe2\x80\xa600CaA complete set of keys for a certain packet space0CbA Long packet header, as used during the handshakeAcAn open (Multi)PathAb(Multi)Path eventsB`An error occurred during readingCeWhether an endpoint was the initiator of a connection0nThe 0 path id.0CaThe address observed by the remote over this pathCjThe source address of the datagram(s) contained in the \xe2\x80\xa6mType of errorBkCurrent congestion window of the connection0gPayloadAoReturns the argument unchanged.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000BaCalls U::from(self).0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dNextCeMethod for opening a sealed message dataB`Open a single stream if possible1CgReturns the Path structure of an open pathAhPing the remote endpointCkCheck whether the timer has expired, and register to be \xe2\x80\xa6BaReturns application-facing eventsBgRead data contiguously from the stream.AoRead from the given recv streamCcDecode TransportParameters from bufferBiReceive an unreliable, unordered datagramBlMethod for sealing message data0CiQueue an unreliable, unordered datagram for immediate \xe2\x80\xa6CbSends a Transmit on the given socket.CmNumber of address validation tokens sent to a client when \xe2\x80\xa60BmThe side of the connection (client or server)ChLook up whether we\xe2\x80\x99re the client or server of this \xe2\x80\xa6AlMethod for signing a message0CdAmount of data written to the caller-supplied buffer0hSpin bitAcStop accepting dataBoStop 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 stream0CcInfallible encoding and decoding of QUIC primitivesCiThe RFC8312 congestion controller, as widely used for TCP0Borustls reports protocol errors using this type.0DbErrors encountered while decoding TransportParametersCdErrors that the nat traversal state might encounter.BeEvents of interest to the applicationCiEvent emitted when the client receives ADD_ADDRESS or \xe2\x80\xa6A`Any other error.0CcThe peer abandoned transmitting data on this streamCnThe peer is unable to continue processing this connection, \xe2\x80\xa6CdThe peer abandoned transmitting data on this stream.1AeA Retry packet headerCdA short packet header, as used during the data phaseBiConstruct a fresh Controller0AiThe contents of the chunkCiThe total amount of bytes which have been transferred \xe2\x80\xa6BjThe amount of bytes which had been written210BnUnderlying error describing reason for failureBaClose the connection immediately.CnClose all of this endpoint\xe2\x80\x99s connections immediately and \xe2\x80\xa6A`Closes this pathAnClose a connection immediatelyAgThe error that occurredBcFrame type that triggered the errorCcDistinguishes streams of the same initiator and \xe2\x80\xa60CjReturns whether the codepoint is a CE, signalling that \xe2\x80\xa60AgKey for encrypting data0BgParse a connection id from given bufferAhReturns all known paths.BlUpdate the timer to expire at iBbClose the send stream immediately.BeAbandon transmitting data on a streamCnRespond with a retry packet, requiring the client to retry \xe2\x80\xa60CiDrive future to completion in the backgroundCeShow connection stats the at the end of the benchmarkAmReturns connection statisticsBiReturns relevant stats from this Endpoint1BcTitle to record in the qlog capture0CmWrite a buffer into this stream, returning how many bytes \xe2\x80\xa6AmSend data on the given streamCcEncode TransportParameters into bufferC`Future produced by Endpoint::acceptClPaths marked as backup will only be used if there are no \xe2\x80\xa6CeChunks returned from RecvStream::read().AmThe initiator of a connection0AdRun as a perf clientAfA path has been closedCcFuture produced by Connection::open_biAjA new path has been openedChOne or more new streams has been opened and might be \xe2\x80\xa6ChId representing different paths when using multipath \xe2\x80\xa60AbCoding result typeAlThe acceptor of a connection0AdRun as a perf servermStream eventsAiAn integer less than 2^620CfGet the next incoming connection attempt from a clientClAttempt to accept this incoming connection (an error may \xe2\x80\xa6CcAccept a remotely initiated stream of a certain \xe2\x80\xa61C`The amount of full chunks which had been written0BlAllows to configure the desired cipher suiteCiHelper to construct an endpoint for use with outgoing \xe2\x80\xa6CcWait for the connection to be closed for any reasonAfCoding related traits.BnAccess the configuration used by this endpointChTraits and implementations for the QUIC cryptography \xe2\x80\xa6BjCreate QUIC error code from TLS alert codeBoTLS configuration used for incoming connections210BkAn underlying crypto (e.g. TLS) layer errorCgDecode a plain header from given buffer, with given \xe2\x80\xa6DiDecode a Self from the provided buffer, if the buffer is \xe2\x80\xa6ChThe destination IP address which was encoded in this \xe2\x80\xa6CoAppend the encoding of self to the provided bufferCmNotify the peer that no more data will ever be written to \xe2\x80\xa6CnFinish a send stream, signalling that no more data will be \xe2\x80\xa6B`Process an incoming UDP datagramAfHeader protection keys0ClIgnore this incoming connection attempt, not sending any \xe2\x80\xa60C`Potentially store a token for later one-time use0CfPerform NSS-compatible TLS key logging to the file \xe2\x80\xa6AhThe offset in the stream0B`Packet deliveries were confirmed0AfPacket protection keys0lRandom valueBcHuman-readable reason for the close000BhHuman-readable explanation of the reasonBeReason that the connection was closedAjSwitch to a new UDP socketBgReject this incoming connection attempt0AgKey for decrypting data0AmTLS interface based on rustls0CnHelper to construct an endpoint for use with both incoming \xe2\x80\xa6BkOptional source IP address for the datagram00CgThe current local PathStatus of this path.B`The new status set by the remoteCfThe size of a single datagram in the associated bufferCgStatistics about UDP datagrams received on a connection0CjStatistics about UDP datagrams transmitted on a connection0AnMethod for verifying a message0CcNumber of ack-eliciting bytes that may be in flight0CaA key for sealing data with AEAD-based algorithms0A`Send would blockCcNo more data is currently available on this stream.CjThe peer is not able to accept additional data, or the \xe2\x80\xa6BfA catch-all error for unlikely errors.0BlA key for signing with HMAC-based algorithms0AhAn Initial packet headerBnA pair of keys for bidirectional communication0BhA simple, standard congestion controller0CdFuture produced by Connection::open_uniCkAbstracts I/O and timer operations for runtime independenceBfA cryptographic session (commonly TLS)0CcThe peer is no longer accepting data on this streamClThe peer is no longer accepting data on this stream, and \xe2\x80\xa6CkThe peer is no longer accepting data on this stream. No \xe2\x80\xa6CgThe peer asked us to stop sending on an outgoing streamAaAccess to streamsCaThe stream is larger than the user-supplied limitClIndicates how many bytes and chunks had been transferred \xe2\x80\xa60l0-RTT packetCjReturns the length of a CID for connections created by \xe2\x80\xa60CdProvide the length of dst_cid in short header packetCcThe total number of clients which should be createdAlConnect to a remote endpointAeInitiate a connectionCgDecrypt the packet payload with the given packet numberBcDecrypt the given packet\xe2\x80\x99s header10ClConstructs a value with arbitrary fields, intended to be \xe2\x80\xa6DjConstruct an event that indicating that a Connection will \xe2\x80\xa6BkThe destination connection ID of the packetBkThe destination Connection ID of the packetAiDestination Connection ID00000CgEncrypt the packet payload with the given packet numberBcEncrypt the given packet\xe2\x80\x99s header10Abiroh NAT TraversalCbCheck if this stream has been opened during 0-RTT.CmRetrieve implementation-specific metrics used to populate \xe2\x80\xa60CnThe maximum UDP payload size guaranteed to be supported by \xe2\x80\xa60BbOne or more packets were just sent0BmInitiate a new outgoing bidirectional stream.CaReturns the path ID of the new path being opened.AdSource Connection ID0000CiCompletes when the peer stops the stream or reads the \xe2\x80\xa6CjCheck if this stream was stopped, get the reason if it wasCcThe total number of streams which should be createdAlProvide control over streamsCeThe length of the AEAD tag appended to packets on \xe2\x80\xa60CdUpgrade the handle to a full ConnectionAkSet the QUIC version to use0lQUIC versionB`The version that was unsupported11CeFuture produced by Connection::accept_biAfAn unreliable datagramBdDatagram support is disabled locally0A`A QUIC endpoint.BcThe main entry point to the libraryChA finished stream has been fully acknowledged or stoppedCkAn incoming connection for which the server has not yet \xe2\x80\xa60BoLong packet types with uniform header structureB`The largest encoded value length0Cmthe connection is being closed abruptly in the absence of \xe2\x80\xa60CeFuture returned by Connection::on_closedClFuture produced by crate::Connection::open_pathCmA currently open stream likely has data or errors waiting \xe2\x80\xa6CkMetadata for a single buffer filled with bytes received \xe2\x80\xa6BkResponse generated directly by the endpointCfIdentifier for a stream within a particular connection0CnCommunication with the peer has lapsed for longer than the \xe2\x80\xa60ChResponsible for limiting clients\xe2\x80\x99 ability to reuse \xe2\x80\xa60ClThe datagram is larger than the connection can currently \xe2\x80\xa60AbAn outgoing packet00CmStatistics about UDP datagrams transmitted or received on \xe2\x80\xa60CnA formerly write-blocked stream might be ready for a write \xe2\x80\xa6AkCongestion algorithm to useAhContents of the datagramBoMark the read data as consumed from the stream.C`Statistics about frames received on a connection0CcStatistics about frames transmitted on a connection0DhConstructs cid by reading len bytes from a Buf0DkCreates a new qlog factory that writes files into QLOGDIR, \xe2\x80\xa60BfCreate a generator with a specific keyBjConstruct a VarInt infallibly0BeSucceeds iff x < 2^620AeWhat type we receivedAoWhat handshake type we received10CbWhether 0-RTT is/was possible during the handshakeCmSpecifies the time to wait after completing MTU discovery \xe2\x80\xa60CcReturns Self for use in down-casting to extract \xe2\x80\xa60EhReturns true if the Connection associated with this handle \xe2\x80\xa6CmDuration after an address validation token was issued for \xe2\x80\xa60CeThe local IP address which was used when the peer \xe2\x80\xa60000ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BnInitiate a new outgoing unidirectional stream.BcGet the priority of the send streamAlGet the priority of a streamCfThe negotiated application protocol, if ALPN is in use0Aeqlog output directoryBbOptional response to transmit backCiOptional seed to be used internally for random number \xe2\x80\xa60AlSlow start threshold (bytes)0BeSucceeds iff x < 2^6200000DaSends a Transmit on the given socket without any \xe2\x80\xa6DkQuickly determine whether cid could have been generated by \xe2\x80\xa600BoAll remaining state for a path has been removedCfFuture produced by Connection::accept_uniCdPaths marked with as available will be used when \xe2\x80\xa6CnAt least one new stream of a certain directionality may be \xe2\x80\xa6ClConfiguration for the Bbr congestion controller0BkThe connection was successfully establishedAoAPI to control datagram trafficAaA QUIC frame type0A`Handshake packetBcKeys used to protect packet headers0CiCatch-all error for problems while decoding transport \xe2\x80\xa6CeNat traversal attempt failed due to a multipath errorBdKeys used to protect packet payloads0BmErrors that might trigger a path being closedAnApplication events about pathsBiStatistics related to a transmission path0AeA read error occurredBmErrors that arise from reading from a stream.C`Errors triggered when reading from a recv streamCiAn object for asynchronously writing to an associated \xe2\x80\xa6BmAccept the next incoming bidirectional streamBbDuplicate the controller\xe2\x80\x99s state0BdThe amount of UDP datagrams observedAaControl datagrams1BeCreate new object from the given bits00BoWhich side of a connection initiated the stream0CmConvert into a 0-RTT or 0.5-RTT connection at the cost of \xe2\x80\xa6BoShorthand for self == Side::Client0B`Whether the connection is closedBoShorthand for self == Side::Server0CbWhether it is legal to respond with a retry packet0CdWhether to allow clients to migrate to new addresses0CdConstruct a timer that will expire at iCjWait for the connection to be closed without keeping a \xe2\x80\xa6CjOpens an additional path if the multipath extension is \xe2\x80\xa6A`Opens a new pathBmPing the remote endpoint over a specific pathCiAttempts to read from the stream into the provided bufferCeReceive UDP datagrams, or register to be woken if \xe2\x80\xa6CkSend a UDP datagram, or register to be woken if sending \xe2\x80\xa6ClPrivate key used to send authenticated connection resets \xe2\x80\xa60BmGenerate the integrity tag for a retry packet0BgA stable identifier for this connectionCePrivate key used to authenticate data included in \xe2\x80\xa60BlThe position of a token in the packet bufferCgTransport configuration to use for incoming connections0CjWait for all connections on the endpoint to be cleanly \xe2\x80\xa6BoWrite a buffer into this stream in its entiretyCiAbstract implementation of an async timer for runtime \xe2\x80\xa6BoNumber of UDP packets to send/receive at a timeCkError indicating that a path has not been opened or has \xe2\x80\xa6BnThe path is already closed or was never opened1BeIn-progress connection attempt futureAbA QUIC connection.CeProtocol state and logic for a single QUIC connectionCeCommon interface for different congestion controllers0CkNumber of frames transmitted or received of each frame type0BoThe connection ID was not recognized by the \xe2\x80\xa6AnThe QUIC-MULTIPATH path statusBeConfiguration for qlog trace logging.0BnA stream that can only be used to receive dataAaAccess to streamsChErrors that arise while waiting for a stream to be resetDhError for attempting to retry an Incoming which already \xe2\x80\xa60BkA stream that can only be used to send data3BmObject to get current SystemTime0AdA rustls TLS session0CkResponsible for storing validation tokens received from \xe2\x80\xa60AeA borrowed UDP socketBjErrors that arise from writing to a streamBoErrors triggered while writing to a send streamBoAccept the next incoming uni-directional streamCkWhether the implementation is permitted to set the spin \xe2\x80\xa60BmCloses a path by sending a PATH_ABANDON frameCdLogic for controlling the rate at which data is sent0BfWhether to print connection statisticsBnClass of error as encoded in the specificationB`Application-specific reason code10BgError code supplied by the peer See \xe2\x80\xa6AoError code supplied by the peerBcType of frame that caused the close0AiExtract the integer value0BfQUIC transport protocol implementationCnWhether there is no longer any need to keep the connection \xe2\x80\xa6DkDetermine whether this is the last event a Connection will \xe2\x80\xa6CiLook up the local IP address and port used by this socketDgGet the local SocketAddr the underlying socket is bound toBeThe amount of bytes lost on this path0CjPad UDP datagrams carrying application data to current \xe2\x80\xa60AgReturns path statistics0CjThe final path stats, they are no longer available via \xe2\x80\xa6C`Attempt to write bytes from buf into the stream.AmRead the next segment of dataCkRead an exact number of bytes contiguously from the stream.BnSets the PathStatus of this path.BoEpoch qlog event times are recorded relative to0ClError type for attempting to accept an IncomingBjErrors in the configuration of an endpoint0AeGeneric crypto errors0CnConfiguration for the Cubic congestion controller0CdReasons why attempting to finish a stream might failCnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60AnValue exceeds supported bounds0D`Constructs a QlogConfig for individual connections.0AhA Quinn runtime for smolB`Application events about streamsCbAccept this incoming connection using a custom \xe2\x80\xa6CdLargest UDP payload size the path currently supportsClStorage size required for the largest packet that can be \xe2\x80\xa61BiDescription to record in the qlog capture0BjThe socket this datagram should be sent to00BkStarting guess for maximum UDP payload sizeCkThe initial value to be used as the maximum UDP payload \xe2\x80\xa60ChUDP payload size that the network must be capable of \xe2\x80\xa6BjThe RTT used before an RTT sample is taken0BfThe initial round-trip-time (in msecs)CeThe amount of concurrent streams which should be usedDdPackets are acked in batches, all with the same now \xe2\x80\xa60AdPacing rate (bits/s)0DiA broadcast receiver of PathEvents for all paths in this \xe2\x80\xa6DfGets the local PathStatus for a known PathIdAnRead the next segments of dataCkConvenience method to read all remaining data into a bufferAlProvide control over streamsBoThe sample size used for this key\xe2\x80\x99s algorithm01CiMaximum number of bytes to transmit to a peer without \xe2\x80\xa600BoThe server name specified by the client, if any0BmObject to get current SystemTime0BdSet a custom TokenStore0CaNumber of bytes to transmit from client to serverCnSpecifies the upper bound to the max UDP payload size that \xe2\x80\xa60ChReturns a weak reference to the inner connection struct.DgCreate a server config with the given crypto::ServerConfig0CeSets a prefix to the filename of the generated files.0DbWrite a single Bytes into this stream in its entiretyBbAn ADD_ADDRESS frame was received.BfConfiguration for outgoing connectionsCaClient-side configuration for the crypto protocol10CgThe stream has already been stopped, finished, or resetBmThe stream has already been finished or resetCmError indicating that a stream has not been opened or has \xe2\x80\xa6CjThe stream has not been opened or was already stopped, \xe2\x80\xa6CfThe stream has not been opened or has already been \xe2\x80\xa6CmThe stream has not been opened or was already finished or \xe2\x80\xa63CgErrors in the parameters being used to create a new \xe2\x80\xa60BkProtocol-level identifier for a connection.0CkWe couldn\xe2\x80\x99t decrypt a message. This is invariably fatal.0BjExplicit congestion notification codepoint00CnWe couldn\xe2\x80\x99t encrypt a message because it was larger than \xe2\x80\xa60BhParameters that are semantically invalidCaThis is the last path, which can not be abandonedAmIroh\xe2\x80\x99s nat traversal eventsDhNull implementation of TokenLog, which never accepts tokens0ClReceived an observation of our external address from the \xe2\x80\xa6CiFuture produced by Connection::read_datagramBiThe remote changed the status of the pathBlRTT estimation for a particular network pathCnFuture produced by Connection::send_datagram_waitBiParameters governing incoming connectionsCaServer-side configuration for the crypto protocol10CmErrors that arise while monitoring for a send stream stop \xe2\x80\xa6AiA Quinn runtime for TokioC`Returns the lifetime of generated Connection IDs0BlIf the connection is closed, the reason why.CjConnect to a remote endpoint using a custom configuration.AlConservative estimate of RTTBnGet the 0-RTT keys if available (clients only)0AgWhich types we expectedB`Which handshake type we expected10AcGenerates a new CID0ClThe number of segments to read when GRO is enabled. Used \xe2\x80\xa6DeProcess ConnectionEvents generated by the associated \xe2\x80\xa6EaProcess EndpointEvents emitted from related ConnectionsCgCreate the initial set of keys given the client\xe2\x80\x99s \xe2\x80\xa6000BgThe amount of packets lost on this path0DcMaximum number of Incoming to allow to exist at a time0CjWhether datagrams might get fragmented into multiple parts0CmWhether transmitted datagrams might get fragmented by the \xe2\x80\xa6CkThe original destination CID when initiating the connectionCiThe original destination connection ID sent by the clientDhReturns the next time at which handle_timeout should be \xe2\x80\xa6D`Configures qlog capturing by setting a QlogFactory.0CkThe segment size if this transmission contains multiple \xe2\x80\xa600ChThe number of streams that may have unacknowledged data.BgThe amount of packets sent on this path0CbSet the lifetime of CIDs created by this generator0BcSet the priority of the send streamAlSet the priority of a streamCnInitialize a QUIC-compatible TLS client configuration with \xe2\x80\xa6000DgWrite a slice of Bytes into this stream, returning how \xe2\x80\xa6AmSend data on the given streamCkWe received a fatal alert. This means the peer is unhappy.0BhBloom filter-based TokenLog0CmThe connection could not be created because not enough of \xe2\x80\xa6000CaEvent resulting from processing a single datagramBlEvents sent from a Connection to an EndpointAoStatistics on Endpoint activityBnThe stream finished before all bytes were readBlAuthentication data for (rustls) TLS session0Ccreceived an invalid Retry Token in a client Initial0AlThe packet header is invalidBkThe local application closed the connectionAgPath was closed locally1DaThe datagram may result in starting a new ConnectionD`Configuration for the NewReno congestion controller0BjDecodes a QUIC packet\xe2\x80\x99s invariant headerCgErrors triggered when opening a recv stream for readingCaDefault implementation of TimeSource0CgError indicating that the provided buffer was too smallChFor clients, if the peer accepted the 0-RTT data packetsAbAck Frequency modeBoSupply a custom connection ID generator factory0DgCreate a UdpSender that can register a single task for \xe2\x80\xa6CaNumber of bytes to transmit from server to clientAmGet the Incoming0CnThe max_ack_delay we will request the peer to use0CgDisable packet encryption/decryption (for debugging \xe2\x80\xa6CkThe known MTU for the current network path has been updated0BbCryptographic identity of the peerBgGet the peer\xe2\x80\x99s identity, if availableDhFor the rustls TlsSession, the Any type is \xe2\x80\xa610CnAttempts to read from the stream into the provided buffer, \xe2\x80\xa6AkReturns packets to transmitDjConfigures qlog capturing through the QLOGDIR environment \xe2\x80\xa60AoReceive an application datagramDfTransmit data as an unreliable, unordered application \xe2\x80\xa6CmWhether to implement fair queuing for send streams having \xe2\x80\xa60BfLength of sign\xe2\x80\x99s output0BnStart a client session with this configurationBnStart a server session with this configuration10BdA REMOVE_ADDRESS frame was received.CgAbstract implementation of a UDP socket for runtime \xe2\x80\xa6BgErrors triggered when abandoning a pathAgThe connection was lost00000ChGlobal configuration for the endpoint, affecting all \xe2\x80\xa60Cithe endpoint encountered an internal error and cannot \xe2\x80\xa60FbBasic adapter to let Incoming be await-ed like a ConnectingCeThe peer sent us a TLS message with invalid contents.0Amno viable network path exists0DhNull implementation of TokenStore, which does not store \xe2\x80\xa60CiThe peer deviated from the standard TLS protocol. The \xe2\x80\xa60BmErrors that arise from reading from a stream.C`Errors from RecvStream::read_to_endCaIndicates whether a frame needs to be transmittedCmThe peer violated the QUIC specification as understood by \xe2\x80\xa60CiTransport-level errors occur when a peer violates the \xe2\x80\xa6C`Tokio-compatible UDP socket with some useful \xe2\x80\xa6AfDerive AEAD using hkdf0BnCreate a client endpoint and client connectionAgGet a session referenceDiReturns a QlogConfig for a connection, if logging should \xe2\x80\xa60AiProcess timer expirationsBjParameters negotiated during the handshake0CfGet data negotiated during the handshake, if available0AiInitial congestion windowCiDefault limit on the amount of outstanding data in bytes.001000CjUniform interface to send and receive UDP packets with \xe2\x80\xa6DdReturns true until the connection is fully established.CeWhether the connection is in the process of being \xe2\x80\xa61BfVerify the integrity of a retry packet0CnSpecifies the minimum MTU change to stop the MTU discovery \xe2\x80\xa60BdCompute keys for the next key update0BkConfigures qlog capturing into a directory.0AlRead bytes of handshake data0BeWhether to use the unordered read APIClMaximum number of bytes the peer may transmit across all \xe2\x80\xa600CkCompletes when the stream has been reset by the peer or \xe2\x80\xa6CiCheck whether this stream has been reset by the peer, \xe2\x80\xa6AhThe peer\xe2\x80\x99s UDP address00BgThe peer\xe2\x80\x99s UDP address for this path.1CeSaturating integer addition. Computes self + rhs, \xe2\x80\xa6000ChSaturating integer subtraction. Computes self - rhs, \xe2\x80\xa60CkMaximum reordering in time space before time based loss \xe2\x80\xa60ChReason given by the transport for closing the connection0BfReasons why a connection might be lost0CiThe datagram is redirected to its ConnectionBlEvents sent from an Endpoint to a ConnectionAeConnection statistics0BiStatistics for the currently open streamsAcPlain packet headerBkEnables writing qlog traces to a directory.0CfError for when a validation token may have been reused0C`Parameters governing the core QUIC state machine0CbThe peer doesn\xe2\x80\x99t implement any supported version0CdFuture that completes when a connection is fully \xe2\x80\xa6CbThis was a 0-RTT stream and the server rejected it000CjAutomatically select an appropriate runtime from those \xe2\x80\xa6CkWhether to accept QUIC packets containing any value for \xe2\x80\xa60CdMaximum number of incoming packets that may fail \xe2\x80\xa60CnThe interface index of the interface on which the datagram \xe2\x80\xa6AjSwitch to a new UDP socketCiCreates a server endpoint which runs on the given runtimeD`Sets the PathStatus for a known PathIdCfSee proto::TransportConfig::send_window()BoSee TransportConfig::send_window()BmReturns whether a frame should be transmittedD`Convert t into the socket type used by this runtimeCdWrites handshake bytes into the given buffer and \xe2\x80\xa60CiReason given by an application for closing the connection0CbThe peer\xe2\x80\x99s QUIC stack aborted the connection \xe2\x80\xa60DjInternal identifier for a Connection currently associated \xe2\x80\xa6CdOne or more application datagrams have been receivedCaThe endpoint can no longer create new connections0Cnreceived a STREAM frame or a RESET_STREAM frame containing \xe2\x80\xa60ClSpecific failure cases from keys_match or a \xe2\x80\xa60A`key update error0CaCurrent limits do not allow us to open more pathsClThe peer doesn\xe2\x80\x99t support a protocol version/feature we \xe2\x80\xa60BjA QUIC-compatible TLS client configuration0BjA QUIC-compatible TLS server configuration0EfTokenStore implementation that stores up to N tokens per \xe2\x80\xa60CgAn endpoint (local or remote) tried to add too many \xe2\x80\xa6CaPath could not be validated and will be abandonedBcA Version Negotiation packet headerCiRecord that the token was used and, ideally, return a \xe2\x80\xa60CjCurrent state of the congestion control algorithm, for \xe2\x80\xa6CcCurrent state of this connection\xe2\x80\x99s congestion \xe2\x80\xa6BaUpdate traffic keys spontaneously0BoLow-level protocol logic for the QUIC protoocolCnThe maximum amount of segments which can be transmitted if \xe2\x80\xa6CnMaximum duration of inactivity to accept before timing out \xe2\x80\xa60CeGet the number of connections that are currently openBmNumber of connections that are currently openCiOpens a new path if no path exists yet for the remote \xe2\x80\xa6CjOpens a new path only if no path to the remote address \xe2\x80\xa6CiMaximum reordering in packet number space before FACK \xe2\x80\xa60CfGet the size of the socket receive bufferAlReceive buffer size in bytesCcGet the size of the socket send bufferAiSend buffer size in bytesBiSet a custom TransportConfig000ChConfiguration for sending and handling validation tokens0CnCreate a server config with the given certificate chain to \xe2\x80\xa60DdWrite a slice of Bytes into this stream in its entiretyCfthe application or application protocol caused the \xe2\x80\xa60AnThe peer closed the connection0B`Constructs controllers on demand0BdCommon congestion controller metrics0AlA pseudo random key for HKDF0BcThe given server name was malformed0AcPacket decode errorBmErrors that can arise when sending a datagram0CcThe peer does not support receiving datagram frames0BcCongestion events on the connection0AiCongestion window (bytes)0ClCompute the maximum size of datagrams that may be passed \xe2\x80\xa6BoBytes available in the outgoing datagram bufferClReplace the server configuration, affecting new incoming \xe2\x80\xa60Cmthe endpoint has reached the confidentiality or integrity \xe2\x80\xa60CkParameters for controlling the peer\xe2\x80\x99s acknowledgement \xe2\x80\xa60DjThe max_fragment_size value supplied in configuration was \xe2\x80\xa60Bmthe server refused to accept a new connection0BjParse connection id in short header packetCjOne or more application datagrams have been sent after \xe2\x80\xa6Ckreceived more data than permitted in advertised data limits0AoThe TLS handshake was confirmedBjThe connection\xe2\x80\x99s handshake data is readyCeAttempted an ordered read following an unordered read0AnWe saw an invalid certificate.0BcParameters governing MTU discovery.0BnNot enough addresses to complete the operationCkdetected an error with protocol compliance that was not \xe2\x80\xa60Cjreceived a frame for a stream identifier that exceeded \xe2\x80\xa60Cnreceived a frame for a stream that was not in a state that \xe2\x80\xa60AnError from setting path statusAjTransport-level error code0ChThe local endpoint does not support the QUIC version \xe2\x80\xa6CkError indicating that the specified QUIC version is not \xe2\x80\xa61C`Packet uses a QUIC version that is not supported1CiMaximum quantity of out-of-order crypto layer data to \xe2\x80\xa60C`Create a VarInt without ensuring it\xe2\x80\x99s in range0ChCumulative number of Quic handshakes ignored on this \xe2\x80\xa6CiMinimum interval between outgoing stateless reset packets0CjConstruct with an approximate maximum memory usage and \xe2\x80\xa60ChCumulative number of Quic handshakes refused on this \xe2\x80\xa6AnReturns the remote path statusDfTransmit data as an unreliable, unordered application \xe2\x80\xa6CiSee proto::TransportConfig::receive_window()CbSee TransportConfig::receive_window()B`Override supported QUIC versions0CiOverride the instant relative to which all events are \xe2\x80\xa60BkNo remote CIDs available to open a new pathCkTransport parameters used to negotiate connection-level \xe2\x80\xa6CmThe certificate verifier doesn\xe2\x80\x99t support the given type \xe2\x80\xa60CfThe operation is not allowed for this endpoint\xe2\x80\x99s \xe2\x80\xa6CiCumulative number of Quic handshakes accepted by this \xe2\x80\xa6CjSpecifies the amount of time that MTU discovery should \xe2\x80\xa60CiIf the 0-RTT-encrypted data has been accepted by the peer0BgWait for the handshake to be confirmed.CgPeriod of inactivity before sending a keep-alive packet0CiThe amount of PLPMTUD probe packets lost on this path \xe2\x80\xa60BlPackets were deemed lost or marked congested0CgCumulative number of Quic handshakes sent from this \xe2\x80\xa6BjReturns the path\xe2\x80\x99s remote socket addressCnThe number of remotely initiated open streams of a certain \xe2\x80\xa6CiThe amount of PLPMTUD probe packets sent on this path \xe2\x80\xa60Bireceived a frame that was badly formatted0CkThis function doesn\xe2\x80\x99t work until the TLS handshake is \xe2\x80\xa60CkWe received a TLS message that isn\xe2\x80\x99t valid right now. \xe2\x80\xa60CiThe remote SocketAddr supplied was malformedCkThe remote address for the path is not supported by the \xe2\x80\xa61ChThe initial cipher suite (AES-128-GCM-SHA256) is not \xe2\x80\xa60BdPaths can only be opened client-sideDmError returned when constructing a VarInt from a value >= \xe2\x80\xa60CeA handle to some connection internals, use with care.DkSpecifies the ACK frequency config (see AckFrequencyConfig \xe2\x80\xa60CiThe number of times a black hole was detected in the path0CgMaximum number of received bytes to buffer for each \xe2\x80\xa60AeIs multipath enabled?ClMaximum number of datagrams that might be described by a \xe2\x80\xa60CkMaximum UDP payload size accepted from peers (excluding \xe2\x80\xa60AmMax UDP payload size in bytesDkSpecifies the MTU discovery config (see MtuDiscoveryConfig \xe2\x80\xa60AmReturn endpoint-facing eventsCkThe preferred IPv4 address that will be communicated to \xe2\x80\xa60CkThe preferred IPv6 address that will be communicated to \xe2\x80\xa60ChThe reordering threshold we will request the peer to use0ClDuration after a retry token was issued for which it\xe2\x80\x99s \xe2\x80\xa60C`Sets the keep_alive_interval for a specific pathDfResize the receive buffer of socket to bytesDcResize the send buffer of socket to bytesBfThe peer\xe2\x80\x99s QUIC transport parametersBdQUIC connection transport parameters1CaGenerates connection IDs for incoming connections0CdAn incoming connection did not support any known \xe2\x80\xa60BjNo default client configuration was set up0BiPath abandoned due to unstable interfaces0ClConfiguration for sending and handling validation tokens \xe2\x80\xa60CmMaximum number of packets that may be sent using a single \xe2\x80\xa60CiCounter for the number of bytes currently used in the \xe2\x80\xa6ChHandle a change in the local address, i.e. an active \xe2\x80\xa6CkReduction in congestion window when a new loss event is \xe2\x80\xa60DdMaximum number of datagrams that a Transmit may encode.0DjA broadcast receiver of iroh_hp::Events for updates about \xe2\x80\xa6CjGet the address observed by the remote over the given pathCiMaximum number of bytes the peer may transmit without \xe2\x80\xa600Chreceived more data in CRYPTO frames than can be buffered0B`The extension was not negotiatedCbWe failed to figure out what time it currently is.0CbWe failed to acquire random bytes from the system.0CnError indicating that this operation requires multipath to \xe2\x80\xa6BnThe extension was not negotiated with the peerBnHeader of an Initial packet, before decryptionCgDerive keying material from this connection\xe2\x80\x99s TLS \xe2\x80\xa6EdFill output with output.len() bytes of keying material \xe2\x80\xa60ClCurrent number of remotely initiated streams that may be \xe2\x80\xa6ClTrack changed on our external address as reported by the \xe2\x80\xa6ClTrack changes on our external address as reported by the \xe2\x80\xa6CnCreate a client configuration that trusts the platform\xe2\x80\x99s \xe2\x80\xa60CmCreate a client configuration that trusts specified trust \xe2\x80\xa60BkThe peer didn\xe2\x80\x99t give us any certificates.0BkThe peer sent an oversized record/fragment.0CkThe ack-eliciting threshold we will request the peer to use0BiWhether there are any pending retransmitsCdWhether the Multipath for QUIC extension is enabled.C`Sets the keep_alive_interval for a specific pathBoSet a custom ValidationTokenConfig0BmPath abandoned at the application\xe2\x80\x99s request0CjGet the current value of max_udp_payload_size0ChConfigure how to populate the destination CID of the \xe2\x80\xa60CjConstruct an endpoint with arbitrary configuration and \xe2\x80\xa6CfWhether the socket address that is initiating this \xe2\x80\xa60CmModify the number of open paths allowed when multipath is \xe2\x80\xa6Cmthe number of connection IDs provided by the peer exceeds \xe2\x80\xa60CbError returned by Session::export_keying_material.0CiA provided certificate revocation list (CRL) was invalid.0CmPath abandoned due to no available connection IDs for the \xe2\x80\xa60Clreceived transport parameters that were badly formatted, \xe2\x80\xa60CiRegisters one address at which this endpoint might be \xe2\x80\xa6ClAdd addresses the local endpoint considers are reachable \xe2\x80\xa6ClMaximum number of outgoing application datagram bytes to \xe2\x80\xa60CiSet the client configuration used by connectBmSets the max_idle_timeout for a specific pathBfThe QUIC protocol version implemented.BoBytes available in the outgoing datagram bufferDhMaximum number of received bytes to buffer for all Incoming0DaVariant of max_concurrent_bidi_streams affecting \xe2\x80\xa60ClModify the number of remotely initiated streams that may \xe2\x80\xa6CnCreate a client configuration that trusts the platform\xe2\x80\x99s \xe2\x80\xa60CkGenerates 8-byte connection IDs that can be efficiently \xe2\x80\xa6CkAn error occurred while handling Encrypted Client Hello \xe2\x80\xa60CkPath abandoned due to resource limitations in the transport0CiGenerates purely random connection IDs of a specified \xe2\x80\xa6ChWhether to use \xe2\x80\x9cGeneric Segmentation Offload\xe2\x80\x9d to \xe2\x80\xa60CmMaximum number of incoming bidirectional streams that may \xe2\x80\xa60ClMaximum 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/234/docs/search.index/entry/1a5b5718bf93.js b/pr/234/docs/search.index/entry/1a5b5718bf93.js new file mode 100644 index 000000000..a1b265e87 --- /dev/null +++ b/pr/234/docs/search.index/entry/1a5b5718bf93.js @@ -0,0 +1 @@ +rd_("Ah[3753,15,3754,0,109,0,0]Ak[4473,15,4474,4474,110,0,0]Ai[3753,15,3754,0,3925,0,0]Al[4473,15,4474,1845,3926,0,0]Al[4201,15,4202,4202,3927,0,0]Al[4473,15,1755,1755,1521,0,0]Ao[1304,13,1305,1305,3784,2989,0]Ao[3753,13,3754,3532,4515,2989,0]Ao[3753,13,3754,3879,4162,2989,0]Ao[3753,13,3754,3879,2997,2989,0]Ao[3753,13,3754,3879,3174,2989,0]Ao[3753,13,3754,3879,4161,2989,0]Ao[3753,13,3754,3883,3191,2989,0]Ao[3753,13,3754,3883,3952,2989,0]Al[3753,13,3754,0,1528,2989,0]Al[3753,13,3754,0,4660,2989,0]Al[3753,13,3754,0,1263,2989,0]Al[3753,13,3754,0,1514,2989,0]Al[3753,13,3754,0,2994,2989,0]Al[3753,13,3754,0,1918,2989,0]Al[3753,13,3754,0,3912,2989,0]Al[3753,13,3754,0,4252,2989,0]Al[3753,13,3754,0,3795,2989,0]Al[3753,13,3754,0,3787,2989,0]Al[3753,13,3754,0,2979,2989,0]Al[3753,13,3754,0,4250,2989,0]Al[3753,13,3754,0,4425,2989,0]Al[3753,13,3754,0,3918,2989,0]Al[3753,13,3754,0,3921,2989,0]Al[3753,13,3754,0,3925,2989,0]Al[3753,13,3754,0,4566,2989,0]Ak[3753,13,3754,0,457,2989,0]Ak[3753,13,3754,0,109,2989,0]Al[3753,13,3754,0,2307,2989,0]Al[3753,13,1759,0,4802,2989,0]Al[3753,13,1834,0,1281,2989,0]Ao[4473,13,4474,1867,1529,2989,0]Ao[4473,13,4474,1867,4661,2989,0]Ao[4473,13,4474,3013,1264,2989,0]Ao[4473,13,4474,3022,4516,2989,0]Ao[4473,13,4474,1459,1515,2989,0]Ao[4473,13,4474,1459,3168,2989,0]Ao[4473,13,4474,1459,2993,2989,0]Ao[4473,13,4474,1459,4565,2989,0]Ao[4473,13,4474,1459,3147,2989,0]Ao[4473,13,4474,1475,2995,2989,0]Ao[4473,13,4474,1025,2998,2989,0]Ao[4473,13,4474,1025,4066,2989,0]Ao[4473,13,4474,1030,1919,2989,0]Ao[4473,13,4474,1030,3192,2989,0]Ao[4473,13,4474,1030,3794,2989,0]Ao[4473,13,4474,2051,3806,2989,0]Ao[4473,13,4474,2051,4163,2989,0]Ao[4473,13,4474,2051,3916,2989,0]Ao[4473,13,4474,3533,4253,2989,0]Ao[4473,13,4474,3533,2990,2989,0]Ao[4473,13,4474,3533,4136,2989,0]Ao[4473,13,4474,3134,3796,2989,0]Ao[4473,13,4474,1757,3788,2989,0]Ao[4473,13,4474,1449,2980,2989,0]Ao[4473,13,4474,1449,4251,2989,0]Ao[4473,13,4474,1449,4426,2989,0]Ao[4473,13,4474,2330,4430,2989,0]Ao[4473,13,4474,2330,3919,2989,0]Ao[4473,13,4474,1819,2293,2989,0]Ao[4473,13,4474,1819,4514,2989,0]Ao[4473,13,4474,1845,3922,2989,0]Ao[4473,13,4474,1845,3926,2989,0]Al[4473,13,4474,0,4167,2989,0]Al[4473,13,4474,0,4567,2989,0]An[4473,13,4474,4474,458,2989,0]An[4473,13,4474,4474,110,2989,0]Ao[4473,13,4474,4474,2308,2989,0]Ao[4473,13,1755,1755,4070,2989,0]Ao[4473,13,4702,4702,4604,2989,0]Ao[4473,13,4702,4702,1283,2989,0]Ao[4473,13,1760,1760,4803,2989,0]Al[4473,13,1835,0,1282,2989,0]Ao[4201,13,4202,4202,3927,2989,0]Ak[3753,13,3754,1001,452,0,0]Al[3753,13,3754,3879,3172,0,0]Al[3753,13,3754,3883,3178,0,0]Ai[4473,14,2991,0,1508,0,0]Ai[4473,14,2991,0,1501,0,0]Ai[4473,14,2991,0,2963,0,0]Ai[4473,14,2991,0,4054,0,0]Ai[4473,14,2991,0,3939,0,0]Ai[4473,14,2991,0,3936,0,0]Ai[4473,14,3805,0,2301,0,0]Ai[4473,14,3805,0,2322,0,0]Ai[4473,14,3805,0,2288,0,0]Ai[4473,14,3805,0,1908,0,0]Ae[1304,7,1305,0,0,0,0]Ah[4473,14,4260,0,449,0,0]Ag[3753,5,3527,125,0,0,0]Ag[4473,5,3528,125,0,0,0]Al[1002,15,1003,1003,4601,0,0]Ae[3773,5,3774,0,0,0,0]Ah[3753,6,3754,4474,0,0,0]Ae[4473,6,4474,0,0,0,0]Al[4473,15,1755,1755,1521,0,0]Ai[3753,19,3754,0,1528,0,0]Ai[3753,19,3754,0,1514,0,0]Al[4473,19,4474,1867,1529,0,0]Al[4473,19,4474,1459,1515,0,0]Ae[1304,5,1305,0,0,0,0]Ae[1002,5,1749,0,0,0,0]Ae[1002,5,1840,0,0,0,0]Ah[3753,15,3754,0,109,0,0]Ak[4473,15,4474,4474,110,0,0]Ak[3753,13,3754,0,1528,120,0]Ak[3753,13,3754,0,1514,120,0]Ak[3753,13,3754,0,3795,120,0]Ak[3753,13,3754,0,3921,120,0]Aj[3753,13,3754,0,457,120,0]Aj[3753,13,3754,0,109,120,0]Ak[3753,13,3754,0,2307,120,0]An[4473,13,4474,1867,1529,120,0]An[4473,13,4474,3022,4516,120,0]An[4473,13,4474,1459,1515,120,0]An[4473,13,4474,1025,2998,120,0]An[4473,13,4474,1025,4066,120,0]An[4473,13,4474,1030,3192,120,0]An[4473,13,4474,3134,3796,120,0]An[4473,13,4474,2330,4430,120,0]An[4473,13,4474,1819,4514,120,0]An[4473,13,4474,1845,3922,120,0]Am[4473,13,4474,4474,458,120,0]Am[4473,13,4474,4474,110,120,0]An[4473,13,4474,4474,2308,120,0]Ai[3753,13,3754,0,2307,0,0]Al[4473,13,4474,4474,2308,0,0]Ai[4473,14,3805,0,1509,0,0]Ai[4473,14,3805,0,2969,0,0]Ai[3753,14,3754,0,2316,0,0]Al[4473,14,4474,4474,2317,0,0]Al[4201,14,4202,4202,2305,0,0]Al[4201,14,4202,4202,2318,0,0]An[1304,13,1305,1305,117,1271,0]Ao[1304,13,1305,1305,3784,1271,0]Ao[1304,13,1473,1473,4164,1271,0]Ao[3813,13,3814,3814,4744,1271,0]Ao[3753,13,3754,3532,3149,1271,0]Ao[3753,13,3754,3532,3151,1271,0]Ao[3753,13,3754,3532,4663,1271,0]Dd[3753,13,3754,3532,4515,1886,0,\"impl-Display-for-SendDatagramError\"]Db[3753,13,3754,3532,4515,1271,0,\"impl-Debug-for-SendDatagramError\"]Ao[3753,13,3754,2329,2286,1271,0]Ao[3753,13,3754,2329,4046,1271,0]Ao[3753,13,3754,2356,2290,1271,0]Cm[3753,13,3754,2356,3175,1886,0,\"impl-Display-for-RetryError\"]Ck[3753,13,3754,2356,3175,1271,0,\"impl-Debug-for-RetryError\"]Ao[3753,13,3754,2356,4150,1271,0]An[3753,13,3754,1001,452,1271,0]Ao[3753,13,3754,3879,3172,1271,0]Da[3753,13,3754,3879,4162,1886,0,\"impl-Display-for-ReadToEndError\"]Co[3753,13,3754,3879,4162,1271,0,\"impl-Debug-for-ReadToEndError\"]Cj[3753,13,3754,3879,2997,1271,0,\"impl-Debug-for-ReadError\"]Cl[3753,13,3754,3879,2997,1886,0,\"impl-Display-for-ReadError\"]Ck[3753,13,3754,3879,3174,1271,0,\"impl-Debug-for-ResetError\"]Cm[3753,13,3754,3879,3174,1886,0,\"impl-Display-for-ResetError\"]Co[3753,13,3754,3879,4161,1271,0,\"impl-Debug-for-ReadExactError\"]Da[3753,13,3754,3879,4161,1886,0,\"impl-Display-for-ReadExactError\"]Ao[3753,13,3754,1485,3953,1271,0]Ao[3753,13,3754,1042,3804,1271,0]Ao[3753,13,3754,3883,3178,1271,0]Cm[3753,13,3754,3883,3191,1886,0,\"impl-Display-for-WriteError\"]Ck[3753,13,3754,3883,3191,1271,0,\"impl-Debug-for-WriteError\"]Co[3753,13,3754,3883,3952,1886,0,\"impl-Display-for-StoppedError\"]Cm[3753,13,3754,3883,3952,1271,0,\"impl-Debug-for-StoppedError\"]Cd[3753,13,3754,0,1528,1271,0,\"impl-Debug-for-VarInt\"]Cf[3753,13,3754,0,1528,1886,0,\"impl-Display-for-VarInt\"]Dd[3753,13,3754,0,4660,1886,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3753,13,3754,0,4660,1271,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3753,13,3754,0,1263,1271,0]Cd[3753,13,3754,0,1514,1271,0,\"impl-Debug-for-PathId\"]Cf[3753,13,3754,0,1514,1886,0,\"impl-Display-for-PathId\"]Al[3753,13,3754,0,2319,1271,0]Al[3753,13,3754,0,3157,1271,0]Al[3753,13,3754,0,2994,1271,0]Al[3753,13,3754,0,4256,1271,0]Al[3753,13,3754,0,1918,1271,0]Cj[3753,13,3754,0,3912,1271,0,\"impl-Debug-for-ClosedStream\"]Cl[3753,13,3754,0,3912,1886,0,\"impl-Display-for-ClosedStream\"]Cm[3753,13,3754,0,4252,1271,0,\"impl-Debug-for-ConnectionError\"]Co[3753,13,3754,0,4252,1886,0,\"impl-Display-for-ConnectionError\"]Al[3753,13,3754,0,4262,1271,0]Al[3753,13,3754,0,4269,1271,0]Al[3753,13,3754,0,4535,1271,0]Al[3753,13,3754,0,4551,1271,0]Al[3753,13,3754,0,3795,1271,0]Al[3753,13,3754,0,4145,1271,0]Al[3753,13,3754,0,3946,1271,0]Al[3753,13,3754,0,4717,1271,0]Al[3753,13,3754,0,3906,1271,0]Ci[3753,13,3754,0,3787,1271,0,\"impl-Debug-for-ConfigError\"]Ck[3753,13,3754,0,3787,1886,0,\"impl-Display-for-ConfigError\"]Cg[3753,13,3754,0,2979,1271,0,\"impl-Debug-for-FrameType\"]Ci[3753,13,3754,0,2979,1886,0,\"impl-Display-for-FrameType\"]Co[3753,13,3754,0,4250,1886,0,\"impl-Display-for-ConnectionClose\"]Cm[3753,13,3754,0,4250,1271,0,\"impl-Debug-for-ConnectionClose\"]Cn[3753,13,3754,0,4425,1271,0,\"impl-Debug-for-ApplicationClose\"]D`[3753,13,3754,0,4425,1886,0,\"impl-Display-for-ApplicationClose\"]Cl[3753,13,3754,0,3918,1886,0,\"impl-Display-for-ConnectError\"]Cj[3753,13,3754,0,3918,1271,0,\"impl-Debug-for-ConnectError\"]Cj[3753,13,3754,0,3921,1271,0,\"impl-Debug-for-ConnectionId\"]Cl[3753,13,3754,0,3921,1886,0,\"impl-Display-for-ConnectionId\"]Al[3753,13,3754,0,3925,1271,0]Db[3753,13,3754,0,4566,1886,0,\"impl-Display-for-TransportErrorCode\"]D`[3753,13,3754,0,4566,1271,0,\"impl-Debug-for-TransportErrorCode\"]Al[3753,13,3754,0,4449,1271,0]Ak[3753,13,3754,0,457,1271,0]Cb[3753,13,3754,0,109,1886,0,\"impl-Display-for-Dir\"]C`[3753,13,3754,0,109,1271,0,\"impl-Debug-for-Dir\"]Ch[3753,13,3754,0,2307,1886,0,\"impl-Display-for-StreamId\"]Cf[3753,13,3754,0,2307,1271,0,\"impl-Debug-for-StreamId\"]Al[3753,13,3754,0,2316,1271,0]Ak[3753,13,3527,0,103,1271,0]Al[3753,13,3527,0,2972,1271,0]Al[3753,13,3527,0,1268,1271,0]Al[3753,13,3527,0,3792,1271,0]Al[3753,13,3527,0,1897,1271,0]Al[3753,13,3527,0,4059,1271,0]Al[3753,13,3527,0,4507,1271,0]Al[3753,13,1759,0,4802,1271,0]Al[3753,13,1759,0,3790,1271,0]Al[3753,13,1759,0,4570,1271,0]Db[3753,13,1834,0,4656,1271,0,\"impl-Debug-for-NoInitialCipherSuite\"]Dd[3753,13,1834,0,4656,1886,0,\"impl-Display-for-NoInitialCipherSuite\"]Ce[3753,13,1834,0,1281,1886,0,\"impl-Display-for-Error\"]Cc[3753,13,1834,0,1281,1271,0,\"impl-Debug-for-Error\"]Ao[4473,13,4474,3533,3152,1271,0]Ao[4473,13,4474,2330,2287,1271,0]Ao[4473,13,4474,2330,2291,1271,0]Cg[4473,13,4474,1867,1529,1271,0,\"impl-Debug-for-VarInt\"]Ci[4473,13,4474,1867,1529,1886,0,\"impl-Display-for-VarInt\"]De[4473,13,4474,1867,4661,1271,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Dg[4473,13,4474,1867,4661,1886,0,\"impl-Display-for-VarIntBoundsExceeded\"]Ao[4473,13,4474,3013,1264,1271,0]Db[4473,13,4474,3022,4516,1271,0,\"impl-Debug-for-SendDatagramError\"]Dd[4473,13,4474,3022,4516,1886,0,\"impl-Display-for-SendDatagramError\"]Cg[4473,13,4474,1459,1515,1271,0,\"impl-Debug-for-PathId\"]Ci[4473,13,4474,1459,1515,1886,0,\"impl-Display-for-PathId\"]Ao[4473,13,4474,1459,3942,1271,0]Ao[4473,13,4474,1459,3168,1271,0]Ao[4473,13,4474,1459,2993,1271,0]Dc[4473,13,4474,1459,4565,1271,0,\"impl-Debug-for-SetPathStatusError\"]De[4473,13,4474,1459,4565,1886,0,\"impl-Display-for-SetPathStatusError\"]Ck[4473,13,4474,1459,3147,1271,0,\"impl-Debug-for-ClosedPath\"]Cm[4473,13,4474,1459,3147,1886,0,\"impl-Display-for-ClosedPath\"]Ao[4473,13,4474,1475,2320,1271,0]Ao[4473,13,4474,1475,3158,1271,0]Ao[4473,13,4474,1475,2995,1271,0]Ao[4473,13,4474,1475,4257,1271,0]Cl[4473,13,4474,1025,2998,1886,0,\"impl-Display-for-ReadError\"]Cj[4473,13,4474,1025,2998,1271,0,\"impl-Debug-for-ReadError\"]D`[4473,13,4474,1025,4066,1886,0,\"impl-Display-for-ReadableError\"]Cn[4473,13,4474,1025,4066,1271,0,\"impl-Debug-for-ReadableError\"]Ao[4473,13,4474,1030,1919,1271,0]Cm[4473,13,4474,1030,3192,1886,0,\"impl-Display-for-WriteError\"]Ck[4473,13,4474,1030,3192,1271,0,\"impl-Debug-for-WriteError\"]Cn[4473,13,4474,1030,3794,1886,0,\"impl-Display-for-FinishError\"]Cl[4473,13,4474,1030,3794,1271,0,\"impl-Debug-for-FinishError\"]Ao[4473,13,4474,2051,3806,1271,0]Ao[4473,13,4474,2051,4163,1271,0]Co[4473,13,4474,2051,3916,1886,0,\"impl-Display-for-ClosedStream\"]Cm[4473,13,4474,2051,3916,1271,0,\"impl-Debug-for-ClosedStream\"]Db[4473,13,4474,3533,4253,1886,0,\"impl-Display-for-ConnectionError\"]D`[4473,13,4474,3533,4253,1271,0,\"impl-Debug-for-ConnectionError\"]Cj[4473,13,4474,3533,2990,1271,0,\"impl-Debug-for-PathError\"]Cl[4473,13,4474,3533,2990,1886,0,\"impl-Display-for-PathError\"]Da[4473,13,4474,3533,4136,1886,0,\"impl-Display-for-ClosePathError\"]Co[4473,13,4474,3533,4136,1271,0,\"impl-Debug-for-ClosePathError\"]Ao[4473,13,4474,3533,1286,1271,0]Ao[4473,13,4474,1021,4263,1271,0]Ao[4473,13,4474,3134,4270,1271,0]Ao[4473,13,4474,3134,4536,1271,0]Ao[4473,13,4474,3134,4552,1271,0]Ao[4473,13,4474,3134,3796,1271,0]Ao[4473,13,4474,1757,4146,1271,0]Ao[4473,13,4474,1757,3948,1271,0]Ao[4473,13,4474,1757,4718,1271,0]Ao[4473,13,4474,1757,3908,1271,0]Cn[4473,13,4474,1757,3788,1886,0,\"impl-Display-for-ConfigError\"]Cl[4473,13,4474,1757,3788,1271,0,\"impl-Debug-for-ConfigError\"]Cl[4473,13,4474,1449,2980,1886,0,\"impl-Display-for-FrameType\"]Cj[4473,13,4474,1449,2980,1271,0,\"impl-Debug-for-FrameType\"]Db[4473,13,4474,1449,4251,1886,0,\"impl-Display-for-ConnectionClose\"]D`[4473,13,4474,1449,4251,1271,0,\"impl-Debug-for-ConnectionClose\"]Dc[4473,13,4474,1449,4426,1886,0,\"impl-Display-for-ApplicationClose\"]Da[4473,13,4474,1449,4426,1271,0,\"impl-Debug-for-ApplicationClose\"]Ao[4473,13,4474,1449,2280,1271,0]Ao[4473,13,4474,2330,4430,1271,0]Co[4473,13,4474,2330,3919,1886,0,\"impl-Display-for-ConnectError\"]Cm[4473,13,4474,2330,3919,1271,0,\"impl-Debug-for-ConnectError\"]Ao[4473,13,4474,2330,3782,1271,0]Ck[4473,13,4474,2330,3176,1271,0,\"impl-Debug-for-RetryError\"]Cm[4473,13,4474,2330,3176,1886,0,\"impl-Display-for-RetryError\"]Ao[4473,13,4474,1819,4062,1271,0]Ao[4473,13,4474,1819,4261,1271,0]Ao[4473,13,4474,1819,4743,1271,0]Ao[4473,13,4474,1819,2293,1271,0]Dd[4473,13,4474,1819,4514,1886,0,\"impl-Display-for-PacketDecodeError\"]Db[4473,13,4474,1819,4514,1271,0,\"impl-Debug-for-PacketDecodeError\"]Ao[4473,13,4474,1845,4255,1271,0]Ao[4473,13,4474,1845,4045,1271,0]Cm[4473,13,4474,1845,3922,1271,0,\"impl-Debug-for-ConnectionId\"]Co[4473,13,4474,1845,3922,1886,0,\"impl-Display-for-ConnectionId\"]Ao[4473,13,4474,1845,3926,1271,0]Cc[4473,13,4474,0,4167,1271,0,\"impl-Debug-for-Error\"]Ce[4473,13,4474,0,4167,1886,0,\"impl-Display-for-Error\"]Cb[4473,13,4474,0,4567,1271,0,\"impl-Debug-for-Code\"]Cd[4473,13,4474,0,4567,1886,0,\"impl-Display-for-Code\"]Ao[4473,13,4474,4075,3161,1271,0]Ao[4473,13,4474,4075,4836,1271,0]Ao[4473,13,4474,4597,4450,1271,0]An[4473,13,4474,4474,458,1271,0]Ce[4473,13,4474,4474,110,1886,0,\"impl-Display-for-Dir\"]Cc[4473,13,4474,4474,110,1271,0,\"impl-Debug-for-Dir\"]Ck[4473,13,4474,4474,2308,1886,0,\"impl-Display-for-StreamId\"]Ci[4473,13,4474,4474,2308,1271,0,\"impl-Debug-for-StreamId\"]Ao[4473,13,4474,4474,2317,1271,0]Cn[4473,13,1755,1755,4070,1271,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4473,13,1755,1755,4070,1886,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4473,13,4702,4702,4604,1271,0]Ch[4473,13,4702,4702,1283,1886,0,\"impl-Display-for-Error\"]Cf[4473,13,4702,4702,1283,1271,0,\"impl-Debug-for-Error\"]Ao[4473,13,1760,1760,4803,1271,0]Ao[4473,13,1760,1760,3791,1271,0]Ao[4473,13,1760,1760,4573,1271,0]Dg[4473,13,1835,1835,4657,1886,0,\"impl-Display-for-NoInitialCipherSuite\"]De[4473,13,1835,1835,4657,1271,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cc[4473,13,1835,0,1282,1271,0,\"impl-Debug-for-Error\"]Ce[4473,13,1835,0,1282,1886,0,\"impl-Display-for-Error\"]Am[4473,13,3528,125,104,1271,0]An[4473,13,3528,125,2973,1271,0]Ao[4473,13,3528,1444,1269,1271,0]Ao[4473,13,3528,1444,3793,1271,0]Ao[4473,13,3528,2378,1898,1271,0]Ao[4473,13,3528,2378,4060,1271,0]Ao[4473,13,3528,3528,4508,1271,0]Ch[4473,13,2023,2023,1284,1886,0,\"impl-Display-for-Error\"]Cf[4473,13,2023,2023,1284,1271,0,\"impl-Debug-for-Error\"]Ao[4473,13,2023,2023,1287,1271,0]An[4201,13,4202,368,4168,1271,0]Ao[4201,13,4202,4202,2305,1271,0]Ao[4201,13,4202,4202,2318,1271,0]Ao[4201,13,4202,4202,3927,1271,0]Al[4473,13,4474,1459,3942,0,0]Ai[3753,14,3754,0,2319,0,0]Al[4473,14,4474,1475,2320,0,0]Al[4473,13,4474,1819,4062,0,0]Al[4473,14,4474,1819,4743,0,0]Ah[4473,14,4260,0,449,0,0]Al[4201,14,4202,4202,2305,0,0]Ai[3753,13,3754,0,4717,0,0]Al[4473,13,4474,1757,4718,0,0]8Al[1304,13,1473,1473,4164,0,0]Al[3813,13,3814,3814,4744,0,0]Ai[3753,13,3754,0,4037,0,0]Al[3753,13,3754,2329,2286,0,0]Ai[3753,13,3754,0,3169,0,0]Ai[3753,13,3754,0,4262,0,0]Ai[3753,13,3754,0,4145,0,0]Ai[3753,13,3754,0,3946,0,0]Ai[3753,13,3754,0,3906,0,0]Ai[3753,13,3754,0,3921,0,0]Ai[3753,13,3754,0,4449,0,0]Ai[3753,13,3754,0,2307,0,0]Ah[3753,13,3527,0,103,0,0]Ai[3753,13,3527,0,1268,0,0]Ai[3753,13,3527,0,1897,0,0]Al[4473,13,4474,4279,4038,0,0]Al[4473,13,4474,1021,3170,0,0]Al[4473,13,4474,2330,2287,0,0]Al[4473,13,4474,1819,4860,0,0]Al[4473,13,4474,4075,4831,0,0]Al[4473,13,4474,1021,4263,0,0]Al[4473,13,4474,1757,4146,0,0]Al[4473,13,4474,1757,3948,0,0]Al[4473,13,4474,1757,3908,0,0]Al[4473,13,4474,1819,4062,0,0]Al[4473,13,4474,1845,3922,0,0]Ai[4473,13,4474,0,4167,0,0]Al[4473,13,4474,4075,4836,0,0]Al[4473,13,4474,4597,4450,0,0]Al[4473,13,4474,4474,2308,0,0]Aj[4473,13,3528,125,104,0,0]Al[4473,13,3528,1444,1269,0,0]Al[4473,13,3528,2378,1898,0,0]Ak[4201,13,4202,368,4168,0,0]Al[1002,13,4004,4004,4788,0,0]Al[1002,13,4004,4004,4789,0,0]Aj[3753,13,3754,0,457,116,0]Am[4473,13,4474,4474,458,116,0]Ai[3753,12,3754,0,3184,0,0]Al[3753,13,3754,2043,1905,0,0]0Ao[3753,13,3754,1485,3953,1905,0]Al[3753,13,3754,0,4068,3184,0]Al[4473,12,4474,1757,3185,0,0]Ao[4473,13,4474,1757,4069,3185,0]Al[3753,13,3754,3532,3151,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,14,4474,1475,2995,0,0]Ae[1002,7,1749,0,0,0,0]Ae[1002,7,1840,0,0,0,0]Ae[3753,4,3754,0,0,0,0]Ai[3753,15,3754,0,3925,0,0]Al[4473,15,4474,1845,3926,0,0]Al[4201,15,4202,4202,3927,0,0]210Ah[3753,5,1759,1760,0,0,0]Ae[4473,5,1760,0,0,0,0]Al[4473,15,4474,1819,4261,0,0]Ah[3753,5,3754,1001,0,0,0]Al[4473,15,4474,3533,1286,0,0]Al[3753,15,3754,3879,4162,0,0]Ah[3753,6,3754,4474,0,0,0]Ae[4473,6,4474,0,0,0,0]Ai[3753,19,3754,0,1514,0,0]Al[4473,19,4474,1459,1515,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Ai[4473,14,2991,0,3936,0,0]Al[4201,14,4202,4202,2305,0,0]Aa[467,3,0,0,0,0,0]Ai[4473,14,4474,0,4167,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Al[4473,14,4474,1449,2280,0,0]An[3753,13,3754,3532,2298,437,0]An[3753,13,3754,2356,2290,437,0]An[3753,13,3754,3879,3172,437,0]An[3753,13,3754,3883,3178,437,0]An[4473,13,4474,1025,1497,437,0]Am[1304,13,1305,1305,117,444,0]An[1304,13,1305,1305,3784,444,0]An[1304,13,1473,1473,3807,444,0]An[1304,13,1473,1473,1300,444,0]An[1304,13,1473,1473,4164,444,0]Ak[467,13,468,468,3785,444,0]An[3813,13,3814,3814,4744,444,0]An[3753,13,3754,3532,4274,444,0]An[3753,13,3754,3532,2298,444,0]Ak[3753,13,3754,0,4037,444,0]An[3753,13,3754,1001,2299,444,0]An[3753,13,3754,3532,3149,444,0]An[3753,13,3754,3532,3151,444,0]An[3753,13,3754,3532,1900,444,0]An[3753,13,3754,3532,1507,444,0]An[3753,13,3754,3532,2965,444,0]An[3753,13,3754,3532,2276,444,0]An[3753,13,3754,3532,3938,444,0]An[3753,13,3754,3532,3943,444,0]An[3753,13,3754,3532,4663,444,0]An[3753,13,3754,3532,4515,444,0]0An[3753,13,3754,2329,2286,444,0]An[3753,13,3754,2329,4046,444,0]An[3753,13,3754,2329,1491,444,0]An[3753,13,3754,2356,2290,444,0]An[3753,13,3754,2356,3175,444,0]An[3753,13,3754,2356,4150,444,0]Am[3753,13,3754,1001,452,444,0]An[3753,13,3754,3879,3172,444,0]An[3753,13,3754,3879,4162,444,0]0Dm[3753,13,3754,3879,2997,444,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Dk[3753,13,3754,3879,2997,444,0,\"impl-From%3CReadableError%3E-for-ReadError\"]Dh[3753,13,3754,3879,2997,444,0,\"impl-From%3CResetError%3E-for-ReadError\"]An[3753,13,3754,3879,2997,444,0]An[3753,13,3754,3879,3174,444,0]0An[3753,13,3754,3879,4161,444,0]0An[3753,13,3754,1485,3953,444,0]An[3753,13,3754,1042,3804,444,0]An[3753,13,3754,3883,3178,444,0]Dn[3753,13,3754,3883,3191,444,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]An[3753,13,3754,3883,3191,444,0]Dk[3753,13,3754,3883,3191,444,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dk[3753,13,3754,3883,3191,444,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3753,13,3754,3883,3952,444,0]0Ak[3753,13,3754,0,3169,444,0]Ak[3753,13,3754,0,4068,444,0]Ak[3753,13,3754,0,4266,444,0]Ak[3753,13,3754,0,3934,444,0]Ak[3753,13,3754,0,4156,444,0]Ck[3753,13,3754,0,1528,444,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ak[3753,13,3754,0,1528,444,0]Ck[3753,13,3754,0,1528,444,0,\"impl-From%3Cu32%3E-for-VarInt\"]Cj[3753,13,3754,0,1528,444,0,\"impl-From%3Cu8%3E-for-VarInt\"]Dj[3753,13,3754,0,1528,444,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]D`[3753,13,3754,0,1528,444,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Ak[3753,13,3754,0,4660,444,0]Ak[3753,13,3754,0,1263,444,0]Ak[3753,13,3754,0,1514,444,0]0Ak[3753,13,3754,0,2319,444,0]Ak[3753,13,3754,0,3157,444,0]Ak[3753,13,3754,0,2994,444,0]Ak[3753,13,3754,0,4256,444,0]Ak[3753,13,3754,0,1918,444,0]Ak[3753,13,3754,0,3912,444,0]Dl[3753,13,3754,0,4252,444,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Ak[3753,13,3754,0,4252,444,0]Df[3753,13,3754,0,4252,444,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Do[3753,13,3754,0,4252,444,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Ak[3753,13,3754,0,4262,444,0]Ak[3753,13,3754,0,4269,444,0]Ak[3753,13,3754,0,4535,444,0]Ak[3753,13,3754,0,4551,444,0]Ak[3753,13,3754,0,3795,444,0]0Ak[3753,13,3754,0,4145,444,0]Ak[3753,13,3754,0,3946,444,0]Ak[3753,13,3754,0,4717,444,0]Ak[3753,13,3754,0,3906,444,0]Ak[3753,13,3754,0,3787,444,0]Ea[3753,13,3754,0,3787,444,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Dl[3753,13,3754,0,3787,444,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3753,13,3754,0,2979,444,0]Ak[3753,13,3754,0,4250,444,0]0Ak[3753,13,3754,0,4425,444,0]Ak[3753,13,3754,0,3918,444,0]0Ak[3753,13,3754,0,3921,444,0]Ak[3753,13,3754,0,3925,444,0]Ak[3753,13,3754,0,4566,444,0]0Ak[3753,13,3754,0,4449,444,0]Aj[3753,13,3754,0,457,444,0]Aj[3753,13,3754,0,109,444,0]Ak[3753,13,3754,0,2307,444,0]0Ak[3753,13,3754,0,2316,444,0]Aj[3753,13,3527,0,103,444,0]Ak[3753,13,3527,0,2972,444,0]Ak[3753,13,3527,0,1268,444,0]Ak[3753,13,3527,0,3792,444,0]Ak[3753,13,3527,0,1897,444,0]Ak[3753,13,3527,0,4059,444,0]Ak[3753,13,3527,0,4507,444,0]Ak[3753,13,1759,0,1894,444,0]Aj[3753,13,1759,0,447,444,0]Ak[3753,13,1759,0,4802,444,0]Ak[3753,13,1759,0,3790,444,0]0Ak[3753,13,1759,0,4570,444,0]Ak[3753,13,1834,0,3186,444,0]Ak[3753,13,1834,0,4048,444,0]Ak[3753,13,1834,0,4445,444,0]Ak[3753,13,1834,0,4447,444,0]Ak[3753,13,1834,0,4656,444,0]Dn[3753,13,1834,0,1281,444,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Ak[3753,13,1834,0,1281,444,0]Da[3753,13,1834,0,1281,444,0,\"impl-From%3COtherError%3E-for-Error\"]Df[3753,13,1834,0,1281,444,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[3753,13,1834,0,1281,444,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[3753,13,1834,0,1281,444,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dg[3753,13,1834,0,1281,444,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[3753,13,1834,0,1281,444,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]De[3753,13,1834,0,1281,444,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]De[3753,13,1834,0,1281,444,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Dg[3753,13,1834,0,1281,444,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]E`[3753,13,1834,0,1281,444,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]An[4473,13,4474,4279,4038,444,0]An[4473,13,4474,3022,2977,444,0]An[4473,13,4474,1025,1497,444,0]An[4473,13,4474,2051,1913,444,0]An[4473,13,4474,2051,3173,444,0]An[4473,13,4474,2051,3179,444,0]An[4473,13,4474,3533,3152,444,0]An[4473,13,4474,1021,3170,444,0]An[4473,13,4474,1757,4069,444,0]An[4473,13,4474,2330,2287,444,0]An[4473,13,4474,2330,4044,444,0]An[4473,13,4474,2330,2291,444,0]An[4473,13,4474,1819,4860,444,0]An[4473,13,4474,4075,4831,444,0]An[4473,13,4474,1484,4267,444,0]An[4473,13,4474,1484,3935,444,0]An[4473,13,4474,1484,4157,444,0]Cn[4473,13,4474,1867,1529,444,0,\"impl-From%3Cu32%3E-for-VarInt\"]Dc[4473,13,4474,1867,1529,444,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Cm[4473,13,4474,1867,1529,444,0,\"impl-From%3Cu8%3E-for-VarInt\"]Co[4473,13,4474,1867,1529,444,0,\"impl-From%3CCode%3E-for-VarInt\"]Cn[4473,13,4474,1867,1529,444,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[4473,13,4474,1867,1529,444,0]An[4473,13,4474,1867,4661,444,0]An[4473,13,4474,3013,1264,444,0]An[4473,13,4474,3022,4516,444,0]An[4473,13,4474,1459,1515,444,0]0An[4473,13,4474,1459,3942,444,0]An[4473,13,4474,1459,3168,444,0]An[4473,13,4474,1459,2993,444,0]An[4473,13,4474,1459,4565,444,0]An[4473,13,4474,1459,3147,444,0]An[4473,13,4474,1475,2320,444,0]An[4473,13,4474,1475,3158,444,0]An[4473,13,4474,1475,2995,444,0]An[4473,13,4474,1475,4257,444,0]An[4473,13,4474,1025,2998,444,0]An[4473,13,4474,1025,4066,444,0]An[4473,13,4474,1030,1919,444,0]An[4473,13,4474,1030,3192,444,0]An[4473,13,4474,1030,3794,444,0]An[4473,13,4474,2051,3806,444,0]An[4473,13,4474,2051,4163,444,0]An[4473,13,4474,2051,3916,444,0]An[4473,13,4474,3533,4253,444,0]0An[4473,13,4474,3533,2990,444,0]An[4473,13,4474,3533,4136,444,0]An[4473,13,4474,3533,1286,444,0]0An[4473,13,4474,1021,4263,444,0]An[4473,13,4474,3134,4270,444,0]An[4473,13,4474,3134,4536,444,0]An[4473,13,4474,3134,4552,444,0]An[4473,13,4474,3134,3796,444,0]0An[4473,13,4474,1757,4146,444,0]An[4473,13,4474,1757,3948,444,0]An[4473,13,4474,1757,4718,444,0]An[4473,13,4474,1757,3908,444,0]An[4473,13,4474,1757,3788,444,0]Do[4473,13,4474,1757,3788,444,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ed[4473,13,4474,1757,3788,444,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[4473,13,4474,1449,2980,444,0]An[4473,13,4474,1449,4251,444,0]0An[4473,13,4474,1449,4426,444,0]An[4473,13,4474,1449,2280,444,0]An[4473,13,4474,2330,4430,444,0]An[4473,13,4474,2330,3919,444,0]0An[4473,13,4474,2330,3782,444,0]An[4473,13,4474,2330,3176,444,0]An[4473,13,4474,1819,4062,444,0]An[4473,13,4474,1819,4261,444,0]An[4473,13,4474,1819,4743,444,0]An[4473,13,4474,1819,2293,444,0]An[4473,13,4474,1819,4514,444,0]0An[4473,13,4474,1845,4255,444,0]An[4473,13,4474,1845,4045,444,0]An[4473,13,4474,1845,3922,444,0]An[4473,13,4474,1845,3926,444,0]Ak[4473,13,4474,0,4167,444,0]0Ak[4473,13,4474,0,4567,444,0]0An[4473,13,4474,4075,3161,444,0]An[4473,13,4474,4075,4836,444,0]An[4473,13,4474,4597,4450,444,0]Am[4473,13,4474,4474,458,444,0]Am[4473,13,4474,4474,110,444,0]An[4473,13,4474,4474,2308,444,0]0An[4473,13,4474,4474,2317,444,0]An[4473,13,1755,1755,4070,444,0]An[4473,13,4702,4702,4604,444,0]An[4473,13,4702,4702,1283,444,0]0An[4473,13,1760,1760,1895,444,0]Am[4473,13,1760,1760,448,444,0]An[4473,13,1760,1760,4803,444,0]An[4473,13,1760,1760,3791,444,0]0An[4473,13,1760,1760,4573,444,0]An[4473,13,1835,1835,3187,444,0]An[4473,13,1835,1835,4049,444,0]An[4473,13,1835,1835,4446,444,0]An[4473,13,1835,1835,4448,444,0]An[4473,13,1835,1835,4657,444,0]E`[4473,13,1835,0,1282,444,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Df[4473,13,1835,0,1282,444,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Dg[4473,13,1835,0,1282,444,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dg[4473,13,1835,0,1282,444,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[4473,13,1835,0,1282,444,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]Da[4473,13,1835,0,1282,444,0,\"impl-From%3COtherError%3E-for-Error\"]Df[4473,13,1835,0,1282,444,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]De[4473,13,1835,0,1282,444,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dn[4473,13,1835,0,1282,444,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]De[4473,13,1835,0,1282,444,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Ak[4473,13,1835,0,1282,444,0]E`[4473,13,1835,0,1282,444,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Al[4473,13,3528,125,104,444,0]Am[4473,13,3528,125,2973,444,0]An[4473,13,3528,1444,1269,444,0]An[4473,13,3528,1444,3793,444,0]An[4473,13,3528,2378,1898,444,0]An[4473,13,3528,2378,4060,444,0]An[4473,13,3528,3528,4508,444,0]An[4473,13,2023,2023,1284,444,0]An[4473,13,2023,2023,1287,444,0]An[4201,13,4202,4202,3190,444,0]0Am[4201,13,4202,368,4168,444,0]An[4201,13,4202,4202,2305,444,0]An[4201,13,4202,4202,2318,444,0]An[4201,13,4202,4202,3927,444,0]An[1002,13,1003,1003,2975,444,0]An[1002,13,1003,1003,4601,444,0]Am[1002,13,1749,1749,118,444,0]An[1002,13,1474,1474,1301,444,0]An[1002,13,1474,1474,3808,444,0]An[1002,13,1474,1474,4259,444,0]An[1002,13,4004,4004,4788,444,0]An[1002,13,4004,4004,4789,444,0]Am[1002,13,1840,1840,119,444,0]Am[3773,13,3774,3774,108,444,0]An[3773,13,3774,3774,2279,444,0]Ak[3753,13,3754,0,1528,445,0]Ak[3753,13,3754,0,1514,445,0]Ak[3753,13,3754,0,3795,445,0]Ak[3753,13,3754,0,3921,445,0]Aj[3753,13,3754,0,457,445,0]Aj[3753,13,3754,0,109,445,0]Ak[3753,13,3754,0,2307,445,0]An[4473,13,4474,1867,1529,445,0]An[4473,13,4474,3022,4516,445,0]An[4473,13,4474,1459,1515,445,0]An[4473,13,4474,1025,2998,445,0]An[4473,13,4474,1025,4066,445,0]An[4473,13,4474,1030,3192,445,0]An[4473,13,4474,3134,3796,445,0]An[4473,13,4474,2330,4430,445,0]An[4473,13,4474,1819,4514,445,0]An[4473,13,4474,1845,3922,445,0]Am[4473,13,4474,4474,458,445,0]Am[4473,13,4474,4474,110,445,0]An[4473,13,4474,4474,2308,445,0]Am[1304,13,1305,1305,117,446,0]An[1304,13,1305,1305,3784,446,0]An[1304,13,1473,1473,3807,446,0]An[1304,13,1473,1473,1300,446,0]An[1304,13,1473,1473,4164,446,0]Ak[467,13,468,468,3785,446,0]An[3813,13,3814,3814,4744,446,0]An[3753,13,3754,3532,4274,446,0]An[3753,13,3754,3532,2298,446,0]Ak[3753,13,3754,0,4037,446,0]An[3753,13,3754,1001,2299,446,0]An[3753,13,3754,3532,3149,446,0]An[3753,13,3754,3532,3151,446,0]An[3753,13,3754,3532,1900,446,0]An[3753,13,3754,3532,1507,446,0]An[3753,13,3754,3532,2965,446,0]An[3753,13,3754,3532,2276,446,0]An[3753,13,3754,3532,3938,446,0]An[3753,13,3754,3532,3943,446,0]An[3753,13,3754,3532,4663,446,0]An[3753,13,3754,3532,4515,446,0]An[3753,13,3754,2329,2286,446,0]An[3753,13,3754,2329,4046,446,0]An[3753,13,3754,2329,1491,446,0]An[3753,13,3754,2356,2290,446,0]An[3753,13,3754,2356,3175,446,0]An[3753,13,3754,2356,4150,446,0]Am[3753,13,3754,1001,452,446,0]An[3753,13,3754,3879,3172,446,0]An[3753,13,3754,3879,4162,446,0]An[3753,13,3754,3879,2997,446,0]An[3753,13,3754,3879,3174,446,0]An[3753,13,3754,3879,4161,446,0]An[3753,13,3754,1485,3953,446,0]An[3753,13,3754,1042,3804,446,0]An[3753,13,3754,3883,3178,446,0]An[3753,13,3754,3883,3191,446,0]An[3753,13,3754,3883,3952,446,0]Ak[3753,13,3754,0,3169,446,0]Ak[3753,13,3754,0,4068,446,0]Ak[3753,13,3754,0,4266,446,0]Ak[3753,13,3754,0,3934,446,0]Ak[3753,13,3754,0,4156,446,0]Ak[3753,13,3754,0,1528,446,0]Ak[3753,13,3754,0,4660,446,0]Ak[3753,13,3754,0,1263,446,0]Ak[3753,13,3754,0,1514,446,0]Ak[3753,13,3754,0,2319,446,0]Ak[3753,13,3754,0,3157,446,0]Ak[3753,13,3754,0,2994,446,0]Ak[3753,13,3754,0,4256,446,0]Ak[3753,13,3754,0,1918,446,0]Ak[3753,13,3754,0,3912,446,0]Ak[3753,13,3754,0,4252,446,0]Ak[3753,13,3754,0,4262,446,0]Ak[3753,13,3754,0,4269,446,0]Ak[3753,13,3754,0,4535,446,0]Ak[3753,13,3754,0,4551,446,0]Ak[3753,13,3754,0,3795,446,0]Ak[3753,13,3754,0,4145,446,0]Ak[3753,13,3754,0,3946,446,0]Ak[3753,13,3754,0,4717,446,0]Ak[3753,13,3754,0,3906,446,0]Ak[3753,13,3754,0,3787,446,0]Ak[3753,13,3754,0,2979,446,0]Ak[3753,13,3754,0,4250,446,0]Ak[3753,13,3754,0,4425,446,0]Ak[3753,13,3754,0,3918,446,0]Ak[3753,13,3754,0,3921,446,0]Ak[3753,13,3754,0,3925,446,0]Ak[3753,13,3754,0,4566,446,0]Ak[3753,13,3754,0,4449,446,0]Aj[3753,13,3754,0,457,446,0]Aj[3753,13,3754,0,109,446,0]Ak[3753,13,3754,0,2307,446,0]Ak[3753,13,3754,0,2316,446,0]Aj[3753,13,3527,0,103,446,0]Ak[3753,13,3527,0,2972,446,0]Ak[3753,13,3527,0,1268,446,0]Ak[3753,13,3527,0,3792,446,0]Ak[3753,13,3527,0,1897,446,0]Ak[3753,13,3527,0,4059,446,0]Ak[3753,13,3527,0,4507,446,0]Ak[3753,13,1759,0,1894,446,0]Aj[3753,13,1759,0,447,446,0]Ak[3753,13,1759,0,4802,446,0]Ak[3753,13,1759,0,3790,446,0]Ak[3753,13,1759,0,4570,446,0]Ak[3753,13,1834,0,3186,446,0]Ak[3753,13,1834,0,4048,446,0]Ak[3753,13,1834,0,4445,446,0]Ak[3753,13,1834,0,4447,446,0]Ak[3753,13,1834,0,4656,446,0]Ak[3753,13,1834,0,1281,446,0]An[4473,13,4474,4279,4038,446,0]An[4473,13,4474,3022,2977,446,0]An[4473,13,4474,1025,1497,446,0]An[4473,13,4474,2051,1913,446,0]An[4473,13,4474,2051,3173,446,0]An[4473,13,4474,2051,3179,446,0]An[4473,13,4474,3533,3152,446,0]An[4473,13,4474,1021,3170,446,0]An[4473,13,4474,1757,4069,446,0]An[4473,13,4474,2330,2287,446,0]An[4473,13,4474,2330,4044,446,0]An[4473,13,4474,2330,2291,446,0]An[4473,13,4474,1819,4860,446,0]An[4473,13,4474,4075,4831,446,0]An[4473,13,4474,1484,4267,446,0]An[4473,13,4474,1484,3935,446,0]An[4473,13,4474,1484,4157,446,0]An[4473,13,4474,1867,1529,446,0]An[4473,13,4474,1867,4661,446,0]An[4473,13,4474,3013,1264,446,0]An[4473,13,4474,3022,4516,446,0]An[4473,13,4474,1459,1515,446,0]An[4473,13,4474,1459,3942,446,0]An[4473,13,4474,1459,3168,446,0]An[4473,13,4474,1459,2993,446,0]An[4473,13,4474,1459,4565,446,0]An[4473,13,4474,1459,3147,446,0]An[4473,13,4474,1475,2320,446,0]An[4473,13,4474,1475,3158,446,0]An[4473,13,4474,1475,2995,446,0]An[4473,13,4474,1475,4257,446,0]An[4473,13,4474,1025,2998,446,0]An[4473,13,4474,1025,4066,446,0]An[4473,13,4474,1030,1919,446,0]An[4473,13,4474,1030,3192,446,0]An[4473,13,4474,1030,3794,446,0]An[4473,13,4474,2051,3806,446,0]An[4473,13,4474,2051,4163,446,0]An[4473,13,4474,2051,3916,446,0]An[4473,13,4474,3533,4253,446,0]An[4473,13,4474,3533,2990,446,0]An[4473,13,4474,3533,4136,446,0]An[4473,13,4474,3533,1286,446,0]An[4473,13,4474,1021,4263,446,0]An[4473,13,4474,3134,4270,446,0]An[4473,13,4474,3134,4536,446,0]An[4473,13,4474,3134,4552,446,0]An[4473,13,4474,3134,3796,446,0]An[4473,13,4474,1757,4146,446,0]An[4473,13,4474,1757,3948,446,0]An[4473,13,4474,1757,4718,446,0]An[4473,13,4474,1757,3908,446,0]An[4473,13,4474,1757,3788,446,0]An[4473,13,4474,1449,2980,446,0]An[4473,13,4474,1449,4251,446,0]An[4473,13,4474,1449,4426,446,0]An[4473,13,4474,1449,2280,446,0]An[4473,13,4474,2330,4430,446,0]An[4473,13,4474,2330,3919,446,0]An[4473,13,4474,2330,3782,446,0]An[4473,13,4474,2330,3176,446,0]An[4473,13,4474,1819,4062,446,0]An[4473,13,4474,1819,4261,446,0]An[4473,13,4474,1819,4743,446,0]An[4473,13,4474,1819,2293,446,0]An[4473,13,4474,1819,4514,446,0]An[4473,13,4474,1845,4255,446,0]An[4473,13,4474,1845,4045,446,0]An[4473,13,4474,1845,3922,446,0]An[4473,13,4474,1845,3926,446,0]Ak[4473,13,4474,0,4167,446,0]Ak[4473,13,4474,0,4567,446,0]An[4473,13,4474,4075,3161,446,0]An[4473,13,4474,4075,4836,446,0]An[4473,13,4474,4597,4450,446,0]Am[4473,13,4474,4474,458,446,0]Am[4473,13,4474,4474,110,446,0]An[4473,13,4474,4474,2308,446,0]An[4473,13,4474,4474,2317,446,0]An[4473,13,1755,1755,4070,446,0]An[4473,13,4702,4702,4604,446,0]An[4473,13,4702,4702,1283,446,0]An[4473,13,1760,1760,1895,446,0]Am[4473,13,1760,1760,448,446,0]An[4473,13,1760,1760,4803,446,0]An[4473,13,1760,1760,3791,446,0]An[4473,13,1760,1760,4573,446,0]An[4473,13,1835,1835,3187,446,0]An[4473,13,1835,1835,4049,446,0]An[4473,13,1835,1835,4446,446,0]An[4473,13,1835,1835,4448,446,0]An[4473,13,1835,1835,4657,446,0]Ak[4473,13,1835,0,1282,446,0]Al[4473,13,3528,125,104,446,0]Am[4473,13,3528,125,2973,446,0]An[4473,13,3528,1444,1269,446,0]An[4473,13,3528,1444,3793,446,0]An[4473,13,3528,2378,1898,446,0]An[4473,13,3528,2378,4060,446,0]An[4473,13,3528,3528,4508,446,0]An[4473,13,2023,2023,1284,446,0]An[4473,13,2023,2023,1287,446,0]An[4201,13,4202,4202,3190,446,0]Am[4201,13,4202,368,4168,446,0]An[4201,13,4202,4202,2305,446,0]An[4201,13,4202,4202,2318,446,0]An[4201,13,4202,4202,3927,446,0]An[1002,13,1003,1003,2975,446,0]An[1002,13,1003,1003,4601,446,0]Am[1002,13,1749,1749,118,446,0]An[1002,13,1474,1474,1301,446,0]An[1002,13,1474,1474,3808,446,0]An[1002,13,1474,1474,4259,446,0]An[1002,13,4004,4004,4788,446,0]An[1002,13,4004,4004,4789,446,0]Am[1002,13,1840,1840,119,446,0]Am[3773,13,3774,3774,108,446,0]An[3773,13,3774,3774,2279,446,0]Ac[467,7,468,0,0,0,0]Ae[3813,7,3814,0,0,0,0]Ae[4078,7,4079,0,0,0,0]Ae[3773,7,3774,0,0,0,0]Ae[4530,7,4531,0,0,0,0]Al[4473,13,4474,1025,1497,0,0]Ai[3753,12,1759,0,1878,0,0]Al[4473,13,4474,2051,1913,0,0]Al[4473,12,1760,1760,1879,0,0]Al[3753,13,3754,3532,3151,0,0]Ab[1002,3,0,0,0,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,14,4474,1475,3158,0,0]Al[3753,12,3754,2043,3140,0,0]Ao[3753,13,3754,3532,4274,1504,0]Ao[3753,13,3754,3532,2298,1504,0]Ao[3753,13,3754,1001,2299,1504,0]Ao[3753,13,3754,3532,3149,1504,0]Ao[3753,13,3754,3532,1900,1504,0]Ao[3753,13,3754,3532,1507,1504,0]Ao[3753,13,3754,3532,2965,1504,0]Ao[3753,13,3754,3532,2276,1504,0]Ao[3753,13,3754,3532,3938,1504,0]Ao[3753,13,3754,3532,3943,1504,0]Ao[3753,13,3754,2329,1491,1504,0]Ao[3753,13,3754,2356,4150,1504,0]>Al[3753,13,3754,3879,3172,0,0]Al[4473,13,4474,2051,3173,0,0]Al[4473,13,4702,4702,4604,0,0]Al[4473,13,4474,3022,2977,0,0]Ak[4201,13,4202,368,4168,0,0]Ai[3753,12,1759,0,1878,0,0]Al[4473,12,1760,1760,1879,0,0]32Ai[3753,13,3754,0,4717,0,0]Al[4473,13,4474,1757,4718,0,0]Al[3753,13,3754,3532,3151,0,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,12,1759,0,1890,0,0]Al[4473,12,1760,1760,1891,0,0]Al[1304,14,1473,1473,4164,0,0]Ai[3753,14,3754,0,2316,0,0]Al[4473,14,4474,4474,2317,0,0]Ai[4473,14,4260,0,1297,0,0]Al[3753,13,3754,3879,3172,0,0]Al[4473,13,4474,2051,3173,0,0]Ai[3753,12,3754,0,3188,0,0]Al[3753,13,3754,0,4156,3188,0]Al[3753,13,3754,0,4449,3188,0]Al[4473,12,4474,1484,3189,0,0]Ao[4473,13,4474,1484,4157,3189,0]Ao[4473,13,4474,4597,4450,3189,0]Am[1304,13,1305,1305,117,460,0]An[1304,13,1305,1305,3784,460,0]An[1304,13,1473,1473,3807,460,0]An[1304,13,1473,1473,1300,460,0]An[1304,13,1473,1473,4164,460,0]Ak[467,13,468,468,3785,460,0]An[3813,13,3814,3814,4744,460,0]An[3753,13,3754,3532,4274,460,0]An[3753,13,3754,3532,2298,460,0]Ak[3753,13,3754,0,4037,460,0]An[3753,13,3754,1001,2299,460,0]An[3753,13,3754,3532,3149,460,0]An[3753,13,3754,3532,3151,460,0]An[3753,13,3754,3532,1900,460,0]An[3753,13,3754,3532,1507,460,0]An[3753,13,3754,3532,2965,460,0]An[3753,13,3754,3532,2276,460,0]An[3753,13,3754,3532,3938,460,0]An[3753,13,3754,3532,3943,460,0]An[3753,13,3754,3532,4663,460,0]An[3753,13,3754,3532,4515,460,0]An[3753,13,3754,2329,2286,460,0]An[3753,13,3754,2329,4046,460,0]An[3753,13,3754,2329,1491,460,0]An[3753,13,3754,2356,2290,460,0]An[3753,13,3754,2356,3175,460,0]An[3753,13,3754,2356,4150,460,0]Am[3753,13,3754,1001,452,460,0]An[3753,13,3754,3879,3172,460,0]An[3753,13,3754,3879,4162,460,0]An[3753,13,3754,3879,2997,460,0]An[3753,13,3754,3879,3174,460,0]An[3753,13,3754,3879,4161,460,0]An[3753,13,3754,1485,3953,460,0]An[3753,13,3754,1042,3804,460,0]An[3753,13,3754,3883,3178,460,0]An[3753,13,3754,3883,3191,460,0]An[3753,13,3754,3883,3952,460,0]Ak[3753,13,3754,0,3169,460,0]Ak[3753,13,3754,0,4068,460,0]Ak[3753,13,3754,0,4266,460,0]Ak[3753,13,3754,0,3934,460,0]Ak[3753,13,3754,0,4156,460,0]Ak[3753,13,3754,0,1528,460,0]Ak[3753,13,3754,0,4660,460,0]Ak[3753,13,3754,0,1263,460,0]Ak[3753,13,3754,0,1514,460,0]Ak[3753,13,3754,0,2319,460,0]Ak[3753,13,3754,0,3157,460,0]Ak[3753,13,3754,0,2994,460,0]Ak[3753,13,3754,0,4256,460,0]Ak[3753,13,3754,0,1918,460,0]Ak[3753,13,3754,0,3912,460,0]Ak[3753,13,3754,0,4252,460,0]Ak[3753,13,3754,0,4262,460,0]Ak[3753,13,3754,0,4269,460,0]Ak[3753,13,3754,0,4535,460,0]Ak[3753,13,3754,0,4551,460,0]Ak[3753,13,3754,0,3795,460,0]Ak[3753,13,3754,0,4145,460,0]Ak[3753,13,3754,0,3946,460,0]Ak[3753,13,3754,0,4717,460,0]Ak[3753,13,3754,0,3906,460,0]Ak[3753,13,3754,0,3787,460,0]Ak[3753,13,3754,0,2979,460,0]Ak[3753,13,3754,0,4250,460,0]Ak[3753,13,3754,0,4425,460,0]Ak[3753,13,3754,0,3918,460,0]Ak[3753,13,3754,0,3921,460,0]Ak[3753,13,3754,0,3925,460,0]Ak[3753,13,3754,0,4566,460,0]Ak[3753,13,3754,0,4449,460,0]Aj[3753,13,3754,0,457,460,0]Aj[3753,13,3754,0,109,460,0]Ak[3753,13,3754,0,2307,460,0]Ak[3753,13,3754,0,2316,460,0]Aj[3753,13,3527,0,103,460,0]Ak[3753,13,3527,0,2972,460,0]Ak[3753,13,3527,0,1268,460,0]Ak[3753,13,3527,0,3792,460,0]Ak[3753,13,3527,0,1897,460,0]Ak[3753,13,3527,0,4059,460,0]Ak[3753,13,3527,0,4507,460,0]Ak[3753,13,1759,0,1894,460,0]Aj[3753,13,1759,0,447,460,0]Ak[3753,13,1759,0,4802,460,0]Ak[3753,13,1759,0,3790,460,0]Ak[3753,13,1759,0,4570,460,0]Ak[3753,13,1834,0,3186,460,0]Ak[3753,13,1834,0,4048,460,0]Ak[3753,13,1834,0,4445,460,0]Ak[3753,13,1834,0,4447,460,0]Ak[3753,13,1834,0,4656,460,0]Ak[3753,13,1834,0,1281,460,0]An[4473,13,4474,4279,4038,460,0]An[4473,13,4474,3022,2977,460,0]An[4473,13,4474,1025,1497,460,0]An[4473,13,4474,2051,1913,460,0]An[4473,13,4474,2051,3173,460,0]An[4473,13,4474,2051,3179,460,0]An[4473,13,4474,3533,3152,460,0]An[4473,13,4474,1021,3170,460,0]An[4473,13,4474,1757,4069,460,0]An[4473,13,4474,2330,2287,460,0]An[4473,13,4474,2330,4044,460,0]An[4473,13,4474,2330,2291,460,0]An[4473,13,4474,1819,4860,460,0]An[4473,13,4474,4075,4831,460,0]An[4473,13,4474,1484,4267,460,0]An[4473,13,4474,1484,3935,460,0]An[4473,13,4474,1484,4157,460,0]An[4473,13,4474,1867,1529,460,0]An[4473,13,4474,1867,4661,460,0]An[4473,13,4474,3013,1264,460,0]An[4473,13,4474,3022,4516,460,0]An[4473,13,4474,1459,1515,460,0]An[4473,13,4474,1459,3942,460,0]An[4473,13,4474,1459,3168,460,0]An[4473,13,4474,1459,2993,460,0]An[4473,13,4474,1459,4565,460,0]An[4473,13,4474,1459,3147,460,0]An[4473,13,4474,1475,2320,460,0]An[4473,13,4474,1475,3158,460,0]An[4473,13,4474,1475,2995,460,0]An[4473,13,4474,1475,4257,460,0]An[4473,13,4474,1025,2998,460,0]An[4473,13,4474,1025,4066,460,0]An[4473,13,4474,1030,1919,460,0]An[4473,13,4474,1030,3192,460,0]An[4473,13,4474,1030,3794,460,0]An[4473,13,4474,2051,3806,460,0]An[4473,13,4474,2051,4163,460,0]An[4473,13,4474,2051,3916,460,0]An[4473,13,4474,3533,4253,460,0]An[4473,13,4474,3533,2990,460,0]An[4473,13,4474,3533,4136,460,0]An[4473,13,4474,3533,1286,460,0]An[4473,13,4474,1021,4263,460,0]An[4473,13,4474,3134,4270,460,0]An[4473,13,4474,3134,4536,460,0]An[4473,13,4474,3134,4552,460,0]An[4473,13,4474,3134,3796,460,0]An[4473,13,4474,1757,4146,460,0]An[4473,13,4474,1757,3948,460,0]An[4473,13,4474,1757,4718,460,0]An[4473,13,4474,1757,3908,460,0]An[4473,13,4474,1757,3788,460,0]An[4473,13,4474,1449,2980,460,0]An[4473,13,4474,1449,4251,460,0]An[4473,13,4474,1449,4426,460,0]An[4473,13,4474,1449,2280,460,0]An[4473,13,4474,2330,4430,460,0]An[4473,13,4474,2330,3919,460,0]An[4473,13,4474,2330,3782,460,0]An[4473,13,4474,2330,3176,460,0]An[4473,13,4474,1819,4062,460,0]An[4473,13,4474,1819,4261,460,0]An[4473,13,4474,1819,4743,460,0]An[4473,13,4474,1819,2293,460,0]An[4473,13,4474,1819,4514,460,0]An[4473,13,4474,1845,4255,460,0]An[4473,13,4474,1845,4045,460,0]An[4473,13,4474,1845,3922,460,0]An[4473,13,4474,1845,3926,460,0]Ak[4473,13,4474,0,4167,460,0]Ak[4473,13,4474,0,4567,460,0]An[4473,13,4474,4075,3161,460,0]An[4473,13,4474,4075,4836,460,0]An[4473,13,4474,4597,4450,460,0]Am[4473,13,4474,4474,458,460,0]Am[4473,13,4474,4474,110,460,0]An[4473,13,4474,4474,2308,460,0]An[4473,13,4474,4474,2317,460,0]An[4473,13,1755,1755,4070,460,0]An[4473,13,4702,4702,4604,460,0]An[4473,13,4702,4702,1283,460,0]An[4473,13,1760,1760,1895,460,0]Am[4473,13,1760,1760,448,460,0]An[4473,13,1760,1760,4803,460,0]An[4473,13,1760,1760,3791,460,0]An[4473,13,1760,1760,4573,460,0]An[4473,13,1835,1835,3187,460,0]An[4473,13,1835,1835,4049,460,0]An[4473,13,1835,1835,4446,460,0]An[4473,13,1835,1835,4448,460,0]An[4473,13,1835,1835,4657,460,0]Ak[4473,13,1835,0,1282,460,0]Al[4473,13,3528,125,104,460,0]Am[4473,13,3528,125,2973,460,0]An[4473,13,3528,1444,1269,460,0]An[4473,13,3528,1444,3793,460,0]An[4473,13,3528,2378,1898,460,0]An[4473,13,3528,2378,4060,460,0]An[4473,13,3528,3528,4508,460,0]An[4473,13,2023,2023,1284,460,0]An[4473,13,2023,2023,1287,460,0]An[1002,13,1003,1003,2975,460,0]An[1002,13,1003,1003,4601,460,0]Am[1002,13,1749,1749,118,460,0]An[1002,13,1474,1474,1301,460,0]An[1002,13,1474,1474,3808,460,0]An[1002,13,1474,1474,4259,460,0]An[1002,13,4004,4004,4788,460,0]An[1002,13,4004,4004,4789,460,0]Am[1002,13,1840,1840,119,460,0]Am[3773,13,3774,3774,108,460,0]An[3773,13,3774,3774,2279,460,0]Ah[3753,5,3754,3013,0,0,0]Ah[4473,5,4474,3013,0,0,0]Af[4473,10,1755,0,0,0,0]Ah[3753,5,3527,1444,0,0,0]Ah[4473,5,3528,1444,0,0,0]Al[1002,15,1003,1003,4601,0,0]Ah[3753,6,1834,1447,0,0,0]Ah[4473,6,1835,1447,0,0,0]Ae[4473,6,4702,0,0,0,0]Ae[4473,6,2023,0,0,0,0]Ah[4473,6,4474,3533,0,0,0]1Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]Al[3753,15,3754,3879,2997,0,0]Ai[3753,15,3754,0,4252,0,0]Al[4473,15,4474,1025,2998,0,0]Al[4473,15,4474,3533,4253,0,0]Al[4473,15,4474,1819,4261,0,0]0Ae[1304,5,1473,0,0,0,0]Ae[1002,5,1474,0,0,0,0]Ab[1304,3,0,0,0,0,0]Ai[3753,12,3527,0,4505,0,0]Al[3753,13,3527,0,2972,4505,0]Al[3753,13,3527,0,3792,4505,0]Al[3753,13,3527,0,4059,4505,0]Al[4473,12,3528,3528,4506,0,0]An[4473,13,3528,125,2973,4506,0]Ao[4473,13,3528,1444,3793,4506,0]Ao[4473,13,3528,2378,4060,4506,0]Al[1002,13,1003,1003,4601,0,0]Ai[3753,14,3754,0,1263,0,0]Ai[3753,14,3754,0,2319,0,0]Ai[3753,14,3754,0,1918,0,0]Al[4473,14,4474,3013,1264,0,0]Al[4473,14,4474,1475,2320,0,0]Al[4473,14,4474,1030,1919,0,0]Al[4473,14,4474,2330,3782,0,0]An[1304,13,1305,1305,117,1265,0]Ao[1304,13,1305,1305,3784,1265,0]Ao[3753,13,3754,3532,3151,1265,0]Ao[3753,13,3754,3532,4663,1265,0]Ao[3753,13,3754,3532,4515,1265,0]Ao[3753,13,3754,2329,2286,1265,0]Ao[3753,13,3754,2329,4046,1265,0]Ao[3753,13,3754,3879,4162,1265,0]Ao[3753,13,3754,3879,2997,1265,0]Ao[3753,13,3754,3879,3174,1265,0]Ao[3753,13,3754,3879,4161,1265,0]Ao[3753,13,3754,3883,3191,1265,0]Ao[3753,13,3754,3883,3952,1265,0]Al[3753,13,3754,0,1528,1265,0]Al[3753,13,3754,0,4660,1265,0]Al[3753,13,3754,0,1514,1265,0]Al[3753,13,3754,0,2319,1265,0]Al[3753,13,3754,0,3157,1265,0]Al[3753,13,3754,0,2994,1265,0]Al[3753,13,3754,0,4256,1265,0]Al[3753,13,3754,0,1918,1265,0]Al[3753,13,3754,0,3912,1265,0]Al[3753,13,3754,0,4252,1265,0]Al[3753,13,3754,0,4269,1265,0]Al[3753,13,3754,0,4535,1265,0]Al[3753,13,3754,0,4551,1265,0]Al[3753,13,3754,0,3795,1265,0]Al[3753,13,3754,0,4145,1265,0]Al[3753,13,3754,0,3946,1265,0]Al[3753,13,3754,0,4717,1265,0]Al[3753,13,3754,0,3906,1265,0]Al[3753,13,3754,0,3787,1265,0]Al[3753,13,3754,0,2979,1265,0]Al[3753,13,3754,0,4250,1265,0]Al[3753,13,3754,0,4425,1265,0]Al[3753,13,3754,0,3918,1265,0]Al[3753,13,3754,0,3921,1265,0]Al[3753,13,3754,0,3925,1265,0]Al[3753,13,3754,0,4566,1265,0]Ak[3753,13,3754,0,457,1265,0]Ak[3753,13,3754,0,109,1265,0]Al[3753,13,3754,0,2307,1265,0]Ak[3753,13,3527,0,103,1265,0]Al[3753,13,3527,0,2972,1265,0]Al[3753,13,3527,0,1268,1265,0]Al[3753,13,3527,0,3792,1265,0]Al[3753,13,3527,0,1897,1265,0]Al[3753,13,3527,0,4059,1265,0]Al[3753,13,1834,0,4656,1265,0]Al[3753,13,1834,0,1281,1265,0]Ao[4473,13,4474,1867,1529,1265,0]Ao[4473,13,4474,1867,4661,1265,0]Ao[4473,13,4474,3022,4516,1265,0]Ao[4473,13,4474,1459,1515,1265,0]Ao[4473,13,4474,1459,3942,1265,0]Ao[4473,13,4474,1459,3168,1265,0]Ao[4473,13,4474,1459,2993,1265,0]Ao[4473,13,4474,1459,4565,1265,0]Ao[4473,13,4474,1459,3147,1265,0]Ao[4473,13,4474,1475,2320,1265,0]Ao[4473,13,4474,1475,3158,1265,0]Ao[4473,13,4474,1475,2995,1265,0]Ao[4473,13,4474,1475,4257,1265,0]Ao[4473,13,4474,1025,2998,1265,0]Ao[4473,13,4474,1025,4066,1265,0]Ao[4473,13,4474,1030,1919,1265,0]Ao[4473,13,4474,1030,3192,1265,0]Ao[4473,13,4474,1030,3794,1265,0]Ao[4473,13,4474,2051,4163,1265,0]Ao[4473,13,4474,2051,3916,1265,0]Ao[4473,13,4474,3533,4253,1265,0]Ao[4473,13,4474,3533,2990,1265,0]Ao[4473,13,4474,3533,4136,1265,0]Ao[4473,13,4474,3134,4270,1265,0]Ao[4473,13,4474,3134,4536,1265,0]Ao[4473,13,4474,3134,4552,1265,0]Ao[4473,13,4474,3134,3796,1265,0]Ao[4473,13,4474,1757,4146,1265,0]Ao[4473,13,4474,1757,3948,1265,0]Ao[4473,13,4474,1757,4718,1265,0]Ao[4473,13,4474,1757,3908,1265,0]Ao[4473,13,4474,1757,3788,1265,0]Ao[4473,13,4474,1449,2980,1265,0]Ao[4473,13,4474,1449,4251,1265,0]Ao[4473,13,4474,1449,4426,1265,0]Ao[4473,13,4474,1449,2280,1265,0]Ao[4473,13,4474,2330,4430,1265,0]Ao[4473,13,4474,2330,3919,1265,0]Ao[4473,13,4474,1819,4261,1265,0]Ao[4473,13,4474,1819,4743,1265,0]Ao[4473,13,4474,1819,2293,1265,0]Ao[4473,13,4474,1819,4514,1265,0]Ao[4473,13,4474,1845,3922,1265,0]Ao[4473,13,4474,1845,3926,1265,0]Al[4473,13,4474,0,4167,1265,0]Al[4473,13,4474,0,4567,1265,0]Ao[4473,13,4474,4075,3161,1265,0]Ao[4473,13,4474,4075,4836,1265,0]An[4473,13,4474,4474,458,1265,0]An[4473,13,4474,4474,110,1265,0]Ao[4473,13,4474,4474,2308,1265,0]Ao[4473,13,1755,1755,4070,1265,0]Ao[4473,13,4702,4702,4604,1265,0]Ao[4473,13,4702,4702,1283,1265,0]Ao[4473,13,1835,1835,4657,1265,0]Al[4473,13,1835,0,1282,1265,0]Am[4473,13,3528,125,104,1265,0]An[4473,13,3528,125,2973,1265,0]Ao[4473,13,3528,1444,1269,1265,0]Ao[4473,13,3528,1444,3793,1265,0]Ao[4473,13,3528,2378,1898,1265,0]Ao[4473,13,3528,2378,4060,1265,0]Ao[4473,13,2023,2023,1287,1265,0]Ao[4201,13,4202,4202,2305,1265,0]Ao[4201,13,4202,4202,2318,1265,0]Ao[4201,13,4202,4202,3927,1265,0]Ao[1002,13,1003,1003,4601,1265,0]Ao[1002,13,1474,1474,4259,1265,0]Al[3753,13,3754,3532,3151,0,0]Al[3753,13,3754,2329,2286,0,0]Ak[3753,13,3754,1001,452,0,0]Al[4473,13,4474,3533,3152,0,0]Al[3753,13,3754,0,3921,1272,0]Ao[4473,13,4474,1845,3922,1272,0]Ai[4473,14,2991,0,4054,0,0]Ai[4473,14,4474,0,4167,0,0]Ai[3753,13,3754,0,2307,0,0]Al[4473,13,4474,4474,2308,0,0]Ai[3753,13,3754,0,3925,0,0]Al[4473,13,4474,1845,3926,0,0]Ai[3753,14,1759,0,1894,0,0]Al[4473,14,1760,1760,1895,0,0]Al[4473,12,4474,1819,4541,0,0]Ao[4473,13,4474,1819,4860,4541,0]=Al[4473,13,4702,4702,4604,0,0]Ah[3753,5,3754,2329,0,0,0]Al[1304,15,1305,1305,3784,0,0]0Al[4473,15,4474,1459,3168,0,0]Ah[4473,5,4474,1025,0,0,0]Ah[3753,15,3754,0,457,0,0]Ak[4473,15,4474,4474,458,0,0]Al[3773,15,3774,3774,2279,0,0]Al[4473,15,4474,1459,2993,0,0]Ah[3753,5,3754,3532,0,0,0]1Al[4473,15,4474,2051,3806,0,0]Ah[3753,5,3754,1459,0,0,0]Ah[4473,5,4474,1459,0,0,0]Ae[4473,8,1755,0,0,0,0]876Al[4473,15,4474,3533,1286,0,0]Ah[3753,5,3754,1867,0,0,0]Ah[4473,5,4474,1867,0,0,0]Al[3753,13,3754,2329,2286,0,0]Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,2051,1913,0,0]Al[4473,13,4474,2330,2287,0,0]An[1304,13,1305,1305,117,1495,0]Ao[1304,13,1305,1305,3784,1495,0]Ao[1304,13,1473,1473,3807,1495,0]Ao[1304,13,1473,1473,1300,1495,0]Ao[1304,13,1473,1473,4164,1495,0]Al[467,13,468,468,3785,1495,0]Ao[3813,13,3814,3814,4744,1495,0]Ao[3753,13,3754,3532,4274,1495,0]Ao[3753,13,3754,3532,2298,1495,0]Al[3753,13,3754,0,4037,1495,0]Ao[3753,13,3754,1001,2299,1495,0]Ao[3753,13,3754,3532,3149,1495,0]Ao[3753,13,3754,3532,3151,1495,0]Ao[3753,13,3754,3532,1900,1495,0]Ao[3753,13,3754,3532,1507,1495,0]Ao[3753,13,3754,3532,2965,1495,0]Ao[3753,13,3754,3532,2276,1495,0]Ao[3753,13,3754,3532,3938,1495,0]Ao[3753,13,3754,3532,3943,1495,0]Ao[3753,13,3754,3532,4663,1495,0]Ao[3753,13,3754,3532,4515,1495,0]Ao[3753,13,3754,2329,2286,1495,0]Ao[3753,13,3754,2329,4046,1495,0]Ao[3753,13,3754,2329,1491,1495,0]Ao[3753,13,3754,2356,2290,1495,0]Ao[3753,13,3754,2356,3175,1495,0]Ao[3753,13,3754,2356,4150,1495,0]An[3753,13,3754,1001,452,1495,0]Ao[3753,13,3754,3879,3172,1495,0]Ao[3753,13,3754,3879,4162,1495,0]Ao[3753,13,3754,3879,2997,1495,0]Ao[3753,13,3754,3879,3174,1495,0]Ao[3753,13,3754,3879,4161,1495,0]Ao[3753,13,3754,1485,3953,1495,0]Ao[3753,13,3754,1042,3804,1495,0]Ao[3753,13,3754,3883,3178,1495,0]Ao[3753,13,3754,3883,3191,1495,0]Ao[3753,13,3754,3883,3952,1495,0]Al[3753,13,3754,0,3169,1495,0]Al[3753,13,3754,0,4068,1495,0]Al[3753,13,3754,0,4266,1495,0]Al[3753,13,3754,0,3934,1495,0]Al[3753,13,3754,0,4156,1495,0]Al[3753,13,3754,0,1528,1495,0]Al[3753,13,3754,0,4660,1495,0]Al[3753,13,3754,0,1263,1495,0]Al[3753,13,3754,0,1514,1495,0]Al[3753,13,3754,0,2319,1495,0]Al[3753,13,3754,0,3157,1495,0]Al[3753,13,3754,0,2994,1495,0]Al[3753,13,3754,0,4256,1495,0]Al[3753,13,3754,0,1918,1495,0]Al[3753,13,3754,0,3912,1495,0]Al[3753,13,3754,0,4252,1495,0]Al[3753,13,3754,0,4262,1495,0]Al[3753,13,3754,0,4269,1495,0]Al[3753,13,3754,0,4535,1495,0]Al[3753,13,3754,0,4551,1495,0]Al[3753,13,3754,0,3795,1495,0]Al[3753,13,3754,0,4145,1495,0]Al[3753,13,3754,0,3946,1495,0]Al[3753,13,3754,0,4717,1495,0]Al[3753,13,3754,0,3906,1495,0]Al[3753,13,3754,0,3787,1495,0]Al[3753,13,3754,0,2979,1495,0]Al[3753,13,3754,0,4250,1495,0]Al[3753,13,3754,0,4425,1495,0]Al[3753,13,3754,0,3918,1495,0]Al[3753,13,3754,0,3921,1495,0]Al[3753,13,3754,0,3925,1495,0]Al[3753,13,3754,0,4566,1495,0]Al[3753,13,3754,0,4449,1495,0]Ak[3753,13,3754,0,457,1495,0]Ak[3753,13,3754,0,109,1495,0]Al[3753,13,3754,0,2307,1495,0]Al[3753,13,3754,0,2316,1495,0]Ak[3753,13,3527,0,103,1495,0]Al[3753,13,3527,0,2972,1495,0]Al[3753,13,3527,0,1268,1495,0]Al[3753,13,3527,0,3792,1495,0]Al[3753,13,3527,0,1897,1495,0]Al[3753,13,3527,0,4059,1495,0]Al[3753,13,3527,0,4507,1495,0]Al[3753,13,1759,0,1894,1495,0]Ak[3753,13,1759,0,447,1495,0]Al[3753,13,1759,0,4802,1495,0]Al[3753,13,1759,0,3790,1495,0]Al[3753,13,1759,0,4570,1495,0]Al[3753,13,1834,0,3186,1495,0]Al[3753,13,1834,0,4048,1495,0]Al[3753,13,1834,0,4445,1495,0]Al[3753,13,1834,0,4447,1495,0]Al[3753,13,1834,0,4656,1495,0]Al[3753,13,1834,0,1281,1495,0]Ao[4473,13,4474,4279,4038,1495,0]Ao[4473,13,4474,3022,2977,1495,0]Ao[4473,13,4474,1025,1497,1495,0]Ao[4473,13,4474,2051,1913,1495,0]Ao[4473,13,4474,2051,3173,1495,0]Ao[4473,13,4474,2051,3179,1495,0]Ao[4473,13,4474,3533,3152,1495,0]Ao[4473,13,4474,1021,3170,1495,0]Ao[4473,13,4474,1757,4069,1495,0]Ao[4473,13,4474,2330,2287,1495,0]Ao[4473,13,4474,2330,4044,1495,0]Ao[4473,13,4474,2330,2291,1495,0]Ao[4473,13,4474,1819,4860,1495,0]Ao[4473,13,4474,4075,4831,1495,0]Ao[4473,13,4474,1484,4267,1495,0]Ao[4473,13,4474,1484,3935,1495,0]Ao[4473,13,4474,1484,4157,1495,0]Ao[4473,13,4474,1867,1529,1495,0]Ao[4473,13,4474,1867,4661,1495,0]Ao[4473,13,4474,3013,1264,1495,0]Ao[4473,13,4474,3022,4516,1495,0]Ao[4473,13,4474,1459,1515,1495,0]Ao[4473,13,4474,1459,3942,1495,0]Ao[4473,13,4474,1459,3168,1495,0]Ao[4473,13,4474,1459,2993,1495,0]Ao[4473,13,4474,1459,4565,1495,0]Ao[4473,13,4474,1459,3147,1495,0]Ao[4473,13,4474,1475,2320,1495,0]Ao[4473,13,4474,1475,3158,1495,0]Ao[4473,13,4474,1475,2995,1495,0]Ao[4473,13,4474,1475,4257,1495,0]Ao[4473,13,4474,1025,2998,1495,0]Ao[4473,13,4474,1025,4066,1495,0]Ao[4473,13,4474,1030,1919,1495,0]Ao[4473,13,4474,1030,3192,1495,0]Ao[4473,13,4474,1030,3794,1495,0]Ao[4473,13,4474,2051,3806,1495,0]Ao[4473,13,4474,2051,4163,1495,0]Ao[4473,13,4474,2051,3916,1495,0]Ao[4473,13,4474,3533,4253,1495,0]Ao[4473,13,4474,3533,2990,1495,0]Ao[4473,13,4474,3533,4136,1495,0]Ao[4473,13,4474,3533,1286,1495,0]Ao[4473,13,4474,1021,4263,1495,0]Ao[4473,13,4474,3134,4270,1495,0]Ao[4473,13,4474,3134,4536,1495,0]Ao[4473,13,4474,3134,4552,1495,0]Ao[4473,13,4474,3134,3796,1495,0]Ao[4473,13,4474,1757,4146,1495,0]Ao[4473,13,4474,1757,3948,1495,0]Ao[4473,13,4474,1757,4718,1495,0]Ao[4473,13,4474,1757,3908,1495,0]Ao[4473,13,4474,1757,3788,1495,0]Ao[4473,13,4474,1449,2980,1495,0]Ao[4473,13,4474,1449,4251,1495,0]Ao[4473,13,4474,1449,4426,1495,0]Ao[4473,13,4474,1449,2280,1495,0]Ao[4473,13,4474,2330,4430,1495,0]Ao[4473,13,4474,2330,3919,1495,0]Ao[4473,13,4474,2330,3782,1495,0]Ao[4473,13,4474,2330,3176,1495,0]Ao[4473,13,4474,1819,4062,1495,0]Ao[4473,13,4474,1819,4261,1495,0]Ao[4473,13,4474,1819,4743,1495,0]Ao[4473,13,4474,1819,2293,1495,0]Ao[4473,13,4474,1819,4514,1495,0]Ao[4473,13,4474,1845,4255,1495,0]Ao[4473,13,4474,1845,4045,1495,0]Ao[4473,13,4474,1845,3922,1495,0]Ao[4473,13,4474,1845,3926,1495,0]Al[4473,13,4474,0,4167,1495,0]Al[4473,13,4474,0,4567,1495,0]Ao[4473,13,4474,4075,3161,1495,0]Ao[4473,13,4474,4075,4836,1495,0]Ao[4473,13,4474,4597,4450,1495,0]An[4473,13,4474,4474,458,1495,0]An[4473,13,4474,4474,110,1495,0]Ao[4473,13,4474,4474,2308,1495,0]Ao[4473,13,4474,4474,2317,1495,0]Ao[4473,13,1755,1755,4070,1495,0]Ao[4473,13,4702,4702,4604,1495,0]Ao[4473,13,4702,4702,1283,1495,0]Ao[4473,13,1760,1760,1895,1495,0]An[4473,13,1760,1760,448,1495,0]Ao[4473,13,1760,1760,4803,1495,0]Ao[4473,13,1760,1760,3791,1495,0]Ao[4473,13,1760,1760,4573,1495,0]Ao[4473,13,1835,1835,3187,1495,0]Ao[4473,13,1835,1835,4049,1495,0]Ao[4473,13,1835,1835,4446,1495,0]Ao[4473,13,1835,1835,4448,1495,0]Ao[4473,13,1835,1835,4657,1495,0]Al[4473,13,1835,0,1282,1495,0]Am[4473,13,3528,125,104,1495,0]An[4473,13,3528,125,2973,1495,0]Ao[4473,13,3528,1444,1269,1495,0]Ao[4473,13,3528,1444,3793,1495,0]Ao[4473,13,3528,2378,1898,1495,0]Ao[4473,13,3528,2378,4060,1495,0]Ao[4473,13,3528,3528,4508,1495,0]Ao[4473,13,2023,2023,1284,1495,0]Ao[4473,13,2023,2023,1287,1495,0]Ao[4201,13,4202,4202,3190,1495,0]An[4201,13,4202,368,4168,1495,0]Ao[4201,13,4202,4202,2305,1495,0]Ao[4201,13,4202,4202,2318,1495,0]Ao[4201,13,4202,4202,3927,1495,0]Ao[1002,13,1003,1003,2975,1495,0]Ao[1002,13,1003,1003,4601,1495,0]An[1002,13,1749,1749,118,1495,0]Ao[1002,13,1474,1474,1301,1495,0]Ao[1002,13,1474,1474,3808,1495,0]Ao[1002,13,1474,1474,4259,1495,0]Ao[1002,13,4004,4004,4788,1495,0]Ao[1002,13,4004,4004,4789,1495,0]An[1002,13,1840,1840,119,1495,0]An[3773,13,3774,3774,108,1495,0]Ao[3773,13,3774,3774,2279,1495,0]Ai[3753,14,3754,0,1918,0,0]Al[4473,14,4474,1030,1919,0,0]Ak[1304,14,1305,1305,117,0,0]Ac[467,7,468,0,0,0,0]Al[3753,13,3754,2329,2286,0,0]Ae[1002,2,1003,0,0,0,0]Ae[4530,7,4531,0,0,0,0]Al[3753,13,3754,3532,3151,0,0]Ae[4473,2,4474,0,0,0,0]Al[4473,13,4474,2330,2287,0,0]Ah[3753,2,3754,4474,0,0,0]Ai[3753,13,3754,0,4566,0,0]Ai[3753,14,3754,0,3157,0,0]Ai[3753,14,3754,0,3946,0,0]5Ai[4473,13,4474,0,4567,0,0]Al[4473,14,4474,1475,3158,0,0]Al[4473,14,4474,1757,3948,0,0]Ai[4473,14,4474,0,4167,0,0]Al[3753,13,3754,0,1528,1267,0]Al[3753,13,3754,0,1514,1267,0]Al[3753,13,3754,0,2979,1267,0]Al[3753,13,3754,0,4566,1267,0]Al[3753,13,3754,0,2307,1267,0]Ao[4473,13,4474,1867,1529,1267,0]Ao[4473,13,4474,1459,1515,1267,0]Ao[4473,13,4474,1449,2980,1267,0]Al[4473,13,4474,1819,4261,0,0]Al[4473,13,4474,0,4567,1267,0]Ao[4473,13,4474,4474,2308,1267,0]Al[4473,12,1755,1755,1267,0,0]Al[4201,14,4202,4202,2305,0,0]<;:98765321Al[3753,13,3754,3883,3178,0,0]Al[4473,13,4474,2051,3179,0,0]Al[1002,13,1474,1474,3808,0,0]Al[4473,13,4474,2330,2287,0,0]Ah[3753,14,1759,0,447,0,0]Ak[4473,14,1760,1760,448,0,0]Al[3753,13,3754,2356,2290,0,0]3Ai[3753,12,3754,0,3188,0,0]Al[3753,13,3754,0,4156,3188,0]Al[3753,13,3754,0,4449,3188,0]Al[4473,12,4474,1484,3189,0,0]Ao[4473,13,4474,1484,4157,3189,0]Ao[4473,13,4474,4597,4450,3189,0]Al[1002,14,1003,1003,2975,0,0]Ai[3753,14,3754,0,1263,0,0]Al[4473,14,4474,3013,1264,0,0]Ai[3753,13,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,13,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Ah[3753,14,1759,0,447,0,0]Ak[4473,14,1760,1760,448,0,0]Ai[4473,14,4260,0,4453,0,0]Ai[3753,14,3754,0,4250,0,0]Ai[3753,14,3754,0,4425,0,0]Al[4473,14,4474,1449,4251,0,0]Al[4473,14,4474,1449,4426,0,0]Ai[4473,14,4474,0,4167,0,0]Ai[4473,14,1285,0,4138,0,0]Al[3753,13,3754,2329,2286,0,0]Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,2330,2287,0,0]Ai[3753,14,1759,0,1894,0,0]Al[4473,14,1760,1760,1895,0,0]Ae[3753,4,3754,0,0,0,0]Ah[3753,2,1759,1760,0,0,0]Ae[4473,4,4474,0,0,0,0]Ae[4473,2,1760,0,0,0,0]Ac[467,7,468,0,0,0,0]9Ae[1002,2,1003,0,0,0,0]Ae[4530,7,4531,0,0,0,0]Ao[3753,13,3754,3532,4515,1274,0]Ao[3753,13,3754,3879,4162,1274,0]Ao[3753,13,3754,3879,2997,1274,0]Ao[3753,13,3754,3879,3174,1274,0]Ao[3753,13,3754,3879,4161,1274,0]Ao[3753,13,3754,3883,3191,1274,0]Ao[3753,13,3754,3883,3952,1274,0]Al[3753,13,3754,0,4252,1274,0]Ao[4473,13,4474,3533,4253,1274,0]Ai[3753,14,3754,0,2316,0,0]Al[4473,14,4474,4474,2317,0,0]Al[4201,14,4202,4202,2318,0,0]Ak[3753,13,3754,1001,452,0,0]Ai[4473,14,2991,0,3939,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[4201,14,4202,4202,2305,0,0]Ai[3753,14,3754,0,4256,0,0]Al[4473,14,4474,1475,4257,0,0]10Ai[3753,12,1759,0,1890,0,0]Al[4473,12,1760,1760,1891,0,0]Ai[3753,12,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,12,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Ai[3753,10,1759,1760,0,0,0]Af[4473,10,1760,0,0,0,0]Al[4473,15,4474,3022,4516,0,0]Al[4473,15,4474,1025,2998,0,0]Al[4473,15,4474,1030,3192,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]65Al[4473,15,4474,1819,4261,0,0]Ah[3753,5,1759,1760,0,0,0]Ae[4473,5,1760,0,0,0,0]Ah[3753,5,3527,2378,0,0,0]Ah[4473,5,3528,2378,0,0,0]Al[1002,15,1003,1003,4601,0,0]Ah[3753,5,3754,3532,0,0,0]Ai[3753,10,3754,2043,0,0,0]>=Al[3753,15,3754,3883,3191,0,0];Al[4473,15,4474,1030,3794,0,0]Al[4473,15,4474,2051,3806,0,0]Ah[4473,5,4474,2051,0,0,0]Al[3753,15,3754,3879,4162,0,0]Ah[3753,5,3754,1030,0,0,0]Ah[4473,5,4474,1030,0,0,0]Al[4473,15,4474,1819,2293,0,0]Ai[3753,12,3754,0,4708,0,0]Al[4473,12,4474,4075,4709,0,0]Ao[4473,13,4474,4075,4831,4709,0]Ao[4473,13,4474,4075,4836,4709,0]Ak[1304,14,1305,1305,117,0,0]An[1304,13,1305,1305,117,4137,0]Ao[1002,13,1003,1003,2975,4137,0]An[1002,13,1749,1749,118,4137,0]An[1002,13,1840,1840,119,4137,0]An[3773,13,3774,3774,108,4137,0]Ak[3773,14,3774,3774,108,0,0]Al[3753,13,3754,0,1528,3148,0]Al[3753,13,3754,0,1514,3148,0]Al[3753,13,3754,0,3795,3148,0]Al[3753,13,3754,0,3921,3148,0]Ak[3753,13,3754,0,457,3148,0]Ak[3753,13,3754,0,109,3148,0]Al[3753,13,3754,0,2307,3148,0]Ao[4473,13,4474,1867,1529,3148,0]Ao[4473,13,4474,3022,4516,3148,0]Ao[4473,13,4474,1459,1515,3148,0]Ao[4473,13,4474,1025,2998,3148,0]Ao[4473,13,4474,1025,4066,3148,0]Ao[4473,13,4474,1030,3192,3148,0]Ao[4473,13,4474,3134,3796,3148,0]Ao[4473,13,4474,2330,4430,3148,0]Ao[4473,13,4474,1819,4514,3148,0]Ao[4473,13,4474,1845,3922,3148,0]An[4473,13,4474,4474,458,3148,0]An[4473,13,4474,4474,110,3148,0]Ao[4473,13,4474,4474,2308,3148,0]Al[3753,13,3754,2329,2286,0,0]Al[4473,13,4474,2330,2287,0,0]Ai[3753,12,1759,0,2987,0,0]Ai[3753,12,1759,0,2982,0,0]Al[4473,12,1760,1760,2988,0,0]Al[4473,12,1760,1760,2983,0,0]Ao[1304,13,1473,1473,3807,1885,0]Ao[1304,13,1473,1473,1300,1885,0]Al[467,13,468,468,3785,1885,0]Al[3753,13,3754,0,4037,1885,0]Ao[3753,13,3754,2329,4046,1885,0]Al[3753,13,3754,0,1528,1885,0]Al[3753,13,3754,0,1514,1885,0]Al[3753,13,3754,0,2319,1885,0]Al[3753,13,3754,0,3157,1885,0]Al[3753,13,3754,0,2994,1885,0]Al[3753,13,3754,0,4256,1885,0]Al[3753,13,3754,0,1918,1885,0]Al[3753,13,3754,0,3912,1885,0]Al[3753,13,3754,0,4269,1885,0]Al[3753,13,3754,0,4535,1885,0]Al[3753,13,3754,0,4551,1885,0]Al[3753,13,3754,0,3795,1885,0]Al[3753,13,3754,0,4145,1885,0]Al[3753,13,3754,0,4717,1885,0]Al[3753,13,3754,0,4449,1885,0]Al[3753,13,3527,0,2972,1885,0]Al[3753,13,3527,0,3792,1885,0]Al[3753,13,3527,0,4059,1885,0]Al[3753,13,3527,0,4507,1885,0]Ao[4473,13,4474,4279,4038,1885,0]Ao[4473,13,4474,4075,4831,1885,0]Ao[4473,13,4474,1867,1529,1885,0]Ao[4473,13,4474,1459,1515,1885,0]Ao[4473,13,4474,1459,3168,1885,0]Ao[4473,13,4474,1459,3147,1885,0]Ao[4473,13,4474,1475,2320,1885,0]Ao[4473,13,4474,1475,3158,1885,0]Ao[4473,13,4474,1475,2995,1885,0]Ao[4473,13,4474,1475,4257,1885,0]Ao[4473,13,4474,1030,1919,1885,0]Ao[4473,13,4474,2051,4163,1885,0]Ao[4473,13,4474,2051,3916,1885,0]Ao[4473,13,4474,3134,4270,1885,0]Ao[4473,13,4474,3134,4536,1885,0]Ao[4473,13,4474,3134,4552,1885,0]Ao[4473,13,4474,3134,3796,1885,0]Ao[4473,13,4474,1757,4146,1885,0]Ao[4473,13,4474,1757,4718,1885,0]Ao[4473,13,4474,4075,4836,1885,0]Ao[4473,13,4474,4597,4450,1885,0]An[4473,13,3528,125,2973,1885,0]Ao[4473,13,3528,1444,3793,1885,0]Ao[4473,13,3528,2378,4060,1885,0]Ao[4473,13,3528,3528,4508,1885,0]Ao[4201,13,4202,4202,2305,1885,0]Ao[1002,13,1474,1474,1301,1885,0]Ao[1002,13,1474,1474,4259,1885,0]Al[4473,13,4474,1845,4045,0,0]Al[4473,13,4474,1819,4062,0,0]Al[4473,13,4474,1819,4261,0,0]Al[4473,14,4474,1819,4743,0,0]Ai[4473,14,4513,0,4568,0,0]Ah[4473,14,4260,0,449,0,0]Ai[4473,14,4260,0,1295,0,0]Ai[4473,14,4260,0,1297,0,0]Ai[4473,14,4260,0,4453,0,0]Ai[3753,12,1759,0,2987,0,0]Ai[3753,12,1759,0,2982,0,0]Al[4473,12,1760,1760,2988,0,0]Al[4473,12,1760,1760,2983,0,0]Ae[4473,2,4474,0,0,0,0]Al[3753,13,3754,3879,3172,0,0]Ai[3753,13,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,13,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]9854Al[3753,13,3754,3532,3151,0,0]Al[3753,13,3754,1001,2299,0,0]Al[4473,14,4474,1819,4743,0,0]Ai[4473,14,4513,0,4568,0,0]Ah[4473,14,4260,0,449,0,0]Ai[4473,14,4260,0,1295,0,0]Ai[4473,14,4260,0,4453,0,0]Al[3753,13,3754,3883,3178,0,0]Al[4473,13,4474,2051,3179,0,0]Ak[1304,14,1305,1305,117,0,0]Al[1304,14,1473,1473,1300,0,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,12,1759,0,2987,0,0]Al[4473,12,1760,1760,2988,0,0]Am[1304,13,1305,1305,117,101,0]An[1304,13,1305,1305,3784,101,0]An[1304,13,1473,1473,3807,101,0]An[1304,13,1473,1473,1300,101,0]An[1304,13,1473,1473,4164,101,0]Ak[467,13,468,468,3785,101,0]An[3813,13,3814,3814,4744,101,0]An[3753,13,3754,3532,4274,101,0]An[3753,13,3754,3532,2298,101,0]Ak[3753,13,3754,0,4037,101,0]An[3753,13,3754,1001,2299,101,0]An[3753,13,3754,3532,3149,101,0]An[3753,13,3754,3532,3151,101,0]An[3753,13,3754,3532,1900,101,0]An[3753,13,3754,3532,1507,101,0]An[3753,13,3754,3532,2965,101,0]An[3753,13,3754,3532,2276,101,0]An[3753,13,3754,3532,3938,101,0]An[3753,13,3754,3532,3943,101,0]An[3753,13,3754,3532,4663,101,0]An[3753,13,3754,3532,4515,101,0]An[3753,13,3754,2329,2286,101,0]An[3753,13,3754,2329,4046,101,0]An[3753,13,3754,2329,1491,101,0]An[3753,13,3754,2356,2290,101,0]An[3753,13,3754,2356,3175,101,0]An[3753,13,3754,2356,4150,101,0]Am[3753,13,3754,1001,452,101,0]An[3753,13,3754,3879,3172,101,0]An[3753,13,3754,3879,4162,101,0]An[3753,13,3754,3879,2997,101,0]An[3753,13,3754,3879,3174,101,0]An[3753,13,3754,3879,4161,101,0]An[3753,13,3754,1485,3953,101,0]An[3753,13,3754,1042,3804,101,0]An[3753,13,3754,3883,3178,101,0]An[3753,13,3754,3883,3191,101,0]An[3753,13,3754,3883,3952,101,0]Ak[3753,13,3754,0,3169,101,0]Ak[3753,13,3754,0,4068,101,0]Ak[3753,13,3754,0,4266,101,0]Ak[3753,13,3754,0,3934,101,0]Ak[3753,13,3754,0,4156,101,0]Ak[3753,13,3754,0,1528,101,0]Ak[3753,13,3754,0,4660,101,0]Ak[3753,13,3754,0,1263,101,0]Ak[3753,13,3754,0,1514,101,0]Ak[3753,13,3754,0,2319,101,0]Ak[3753,13,3754,0,3157,101,0]Ak[3753,13,3754,0,2994,101,0]Ak[3753,13,3754,0,4256,101,0]Ak[3753,13,3754,0,1918,101,0]Ak[3753,13,3754,0,3912,101,0]Ak[3753,13,3754,0,4252,101,0]Ak[3753,13,3754,0,4262,101,0]Ak[3753,13,3754,0,4269,101,0]Ak[3753,13,3754,0,4535,101,0]Ak[3753,13,3754,0,4551,101,0]Ak[3753,13,3754,0,3795,101,0]Ak[3753,13,3754,0,4145,101,0]Ak[3753,13,3754,0,3946,101,0]Ak[3753,13,3754,0,4717,101,0]Ak[3753,13,3754,0,3906,101,0]Ak[3753,13,3754,0,3787,101,0]Ak[3753,13,3754,0,2979,101,0]Ak[3753,13,3754,0,4250,101,0]Ak[3753,13,3754,0,4425,101,0]Ak[3753,13,3754,0,3918,101,0]Ak[3753,13,3754,0,3921,101,0]Ak[3753,13,3754,0,3925,101,0]Ak[3753,13,3754,0,4566,101,0]Ak[3753,13,3754,0,4449,101,0]Aj[3753,13,3754,0,457,101,0]Aj[3753,13,3754,0,109,101,0]Ak[3753,13,3754,0,2307,101,0]Ak[3753,13,3754,0,2316,101,0]Aj[3753,13,3527,0,103,101,0]Ak[3753,13,3527,0,2972,101,0]Ak[3753,13,3527,0,1268,101,0]Ak[3753,13,3527,0,3792,101,0]Ak[3753,13,3527,0,1897,101,0]Ak[3753,13,3527,0,4059,101,0]Ak[3753,13,3527,0,4507,101,0]Ak[3753,13,1759,0,1894,101,0]Aj[3753,13,1759,0,447,101,0]Ak[3753,13,1759,0,4802,101,0]Ak[3753,13,1759,0,3790,101,0]Ak[3753,13,1759,0,4570,101,0]Ak[3753,13,1834,0,3186,101,0]Ak[3753,13,1834,0,4048,101,0]Ak[3753,13,1834,0,4445,101,0]Ak[3753,13,1834,0,4447,101,0]Ak[3753,13,1834,0,4656,101,0]Ak[3753,13,1834,0,1281,101,0]An[4473,13,4474,4279,4038,101,0]An[4473,13,4474,3022,2977,101,0]An[4473,13,4474,1025,1497,101,0]An[4473,13,4474,2051,1913,101,0]An[4473,13,4474,2051,3173,101,0]An[4473,13,4474,2051,3179,101,0]An[4473,13,4474,3533,3152,101,0]An[4473,13,4474,1021,3170,101,0]An[4473,13,4474,1757,4069,101,0]An[4473,13,4474,2330,2287,101,0]An[4473,13,4474,2330,4044,101,0]An[4473,13,4474,2330,2291,101,0]An[4473,13,4474,1819,4860,101,0]An[4473,13,4474,4075,4831,101,0]An[4473,13,4474,1484,4267,101,0]An[4473,13,4474,1484,3935,101,0]An[4473,13,4474,1484,4157,101,0]An[4473,13,4474,1867,1529,101,0]An[4473,13,4474,1867,4661,101,0]An[4473,13,4474,3013,1264,101,0]An[4473,13,4474,3022,4516,101,0]An[4473,13,4474,1459,1515,101,0]An[4473,13,4474,1459,3942,101,0]An[4473,13,4474,1459,3168,101,0]An[4473,13,4474,1459,2993,101,0]An[4473,13,4474,1459,4565,101,0]An[4473,13,4474,1459,3147,101,0]An[4473,13,4474,1475,2320,101,0]An[4473,13,4474,1475,3158,101,0]An[4473,13,4474,1475,2995,101,0]An[4473,13,4474,1475,4257,101,0]An[4473,13,4474,1025,2998,101,0]An[4473,13,4474,1025,4066,101,0]An[4473,13,4474,1030,1919,101,0]An[4473,13,4474,1030,3192,101,0]An[4473,13,4474,1030,3794,101,0]An[4473,13,4474,2051,3806,101,0]An[4473,13,4474,2051,4163,101,0]An[4473,13,4474,2051,3916,101,0]An[4473,13,4474,3533,4253,101,0]An[4473,13,4474,3533,2990,101,0]An[4473,13,4474,3533,4136,101,0]An[4473,13,4474,3533,1286,101,0]An[4473,13,4474,1021,4263,101,0]An[4473,13,4474,3134,4270,101,0]An[4473,13,4474,3134,4536,101,0]An[4473,13,4474,3134,4552,101,0]An[4473,13,4474,3134,3796,101,0]An[4473,13,4474,1757,4146,101,0]An[4473,13,4474,1757,3948,101,0]An[4473,13,4474,1757,4718,101,0]An[4473,13,4474,1757,3908,101,0]An[4473,13,4474,1757,3788,101,0]An[4473,13,4474,1449,2980,101,0]An[4473,13,4474,1449,4251,101,0]An[4473,13,4474,1449,4426,101,0]An[4473,13,4474,1449,2280,101,0]An[4473,13,4474,2330,4430,101,0]An[4473,13,4474,2330,3919,101,0]An[4473,13,4474,2330,3782,101,0]An[4473,13,4474,2330,3176,101,0]An[4473,13,4474,1819,4062,101,0]An[4473,13,4474,1819,4261,101,0]An[4473,13,4474,1819,4743,101,0]An[4473,13,4474,1819,2293,101,0]An[4473,13,4474,1819,4514,101,0]An[4473,13,4474,1845,4255,101,0]An[4473,13,4474,1845,4045,101,0]An[4473,13,4474,1845,3922,101,0]An[4473,13,4474,1845,3926,101,0]Ak[4473,13,4474,0,4167,101,0]Ak[4473,13,4474,0,4567,101,0]An[4473,13,4474,4075,3161,101,0]An[4473,13,4474,4075,4836,101,0]An[4473,13,4474,4597,4450,101,0]Am[4473,13,4474,4474,458,101,0]Am[4473,13,4474,4474,110,101,0]An[4473,13,4474,4474,2308,101,0]An[4473,13,4474,4474,2317,101,0]An[4473,13,1755,1755,4070,101,0]An[4473,13,4702,4702,4604,101,0]An[4473,13,4702,4702,1283,101,0]An[4473,13,1760,1760,1895,101,0]Am[4473,13,1760,1760,448,101,0]An[4473,13,1760,1760,4803,101,0]An[4473,13,1760,1760,3791,101,0]An[4473,13,1760,1760,4573,101,0]An[4473,13,1835,1835,3187,101,0]An[4473,13,1835,1835,4049,101,0]An[4473,13,1835,1835,4446,101,0]An[4473,13,1835,1835,4448,101,0]An[4473,13,1835,1835,4657,101,0]Ak[4473,13,1835,0,1282,101,0]Al[4473,13,3528,125,104,101,0]Am[4473,13,3528,125,2973,101,0]An[4473,13,3528,1444,1269,101,0]An[4473,13,3528,1444,3793,101,0]An[4473,13,3528,2378,1898,101,0]An[4473,13,3528,2378,4060,101,0]An[4473,13,3528,3528,4508,101,0]An[4473,13,2023,2023,1284,101,0]An[4473,13,2023,2023,1287,101,0]An[4201,13,4202,4202,3190,101,0]Am[4201,13,4202,368,4168,101,0]An[4201,13,4202,4202,2305,101,0]An[4201,13,4202,4202,2318,101,0]An[4201,13,4202,4202,3927,101,0]An[1002,13,1003,1003,2975,101,0]An[1002,13,1003,1003,4601,101,0]Am[1002,13,1749,1749,118,101,0]An[1002,13,1474,1474,1301,101,0]An[1002,13,1474,1474,3808,101,0]An[1002,13,1474,1474,4259,101,0]An[1002,13,4004,4004,4788,101,0]An[1002,13,4004,4004,4789,101,0]Am[1002,13,1840,1840,119,101,0]Am[3773,13,3774,3774,108,101,0]An[3773,13,3774,3774,2279,101,0]Al[3753,13,3754,3532,4663,0,0]Ai[3753,13,3754,0,3906,0,0]Al[4473,13,4474,1757,3908,0,0]Al[4473,14,4474,1819,4743,0,0]Ai[4473,14,4513,0,4568,0,0]Ah[4473,14,4260,0,449,0,0]Ai[4473,14,4260,0,1295,0,0]Ah[3753,5,3754,3532,0,0,0]Al[1304,15,1305,1305,3784,0,0]Ae[3773,6,3774,0,0,0,0]Ah[4473,5,4474,1449,0,0,0]Al[3753,15,3754,3532,4515,0,0]Al[4473,15,4474,3022,4516,0,0]Ah[3753,5,3754,2329,0,0,0]Ah[4473,5,4474,2330,0,0,0]Al[4473,15,4474,2051,3806,0,0]Ah[3753,5,3754,2356,0,0,0]2Ah[4473,6,4474,1819,0,0,0]Ai[3753,19,3754,0,1528,0,0]Al[4473,19,4474,1867,1529,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]>Ah[3753,5,3754,1001,0,0,0]7Ae[4201,5,4202,0,0,0,0]Al[4473,15,4474,2330,4044,0,0]Ah[3753,5,3754,4474,0,0,0]Ae[4473,5,4474,0,0,0,0]Ai[3753,15,3754,0,4252,0,0]Al[4473,15,4474,3533,4253,0,0]Ai[3753,10,3754,1484,0,0,0]Ai[4473,10,4474,1484,0,0,0]Al[3753,15,3754,3532,4515,0,0]Al[4473,15,4474,3022,4516,0,0]769Ah[3753,5,3754,1475,0,0,0]Ah[4473,5,4474,1475,0,0,0]Al[4473,15,4474,2051,3806,0,0]Al[1002,14,1003,1003,2975,0,0]Al[4201,14,4202,4202,2318,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[1304,14,1473,1473,4164,0,0]Al[4473,13,4474,1025,1497,0,0]Ai[3753,14,3754,0,4256,0,0]Al[4473,14,4474,1475,4257,0,0]10Ai[3753,13,3754,0,3921,0,0]Al[4473,13,4474,1845,3922,0,0]Ai[3753,13,3754,0,4262,0,0]Al[4473,13,4474,1021,4263,0,0]Al[4473,13,4474,4075,4831,0,0]Ao[1304,13,1305,1305,3784,1887,0]Ai[3753,13,3754,0,1528,0,0]Al[4473,13,4474,1867,1529,0,0]10Ai[3753,14,1277,0,4649,0,0]Ai[3753,14,1277,0,4861,0,0]Ai[4473,14,1278,0,4650,0,0]Ai[4473,14,1278,0,4862,0,0]Am[1304,13,1305,1305,117,435,0]An[1002,13,1003,1003,2975,435,0]Am[1002,13,1749,1749,118,435,0]Am[1002,13,1840,1840,119,435,0]Am[3773,13,3774,3774,108,435,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,13,3754,0,4551,0,0]Al[4473,13,4474,3134,4552,0,0]Ai[3753,12,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,12,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Al[3753,13,3754,3532,4663,0,0]Ai[3753,13,3754,0,4717,0,0]Al[4473,13,4474,1757,4718,0,0]Al[3753,13,3754,3532,3149,0,0]Al[3753,13,3754,3532,3151,0,0]Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,13,4474,2330,2291,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[4473,13,4474,3022,2977,0,0]Al[1002,13,1474,1474,3808,0,0]7Al[3753,13,3754,3883,3178,0,0]Al[4473,13,4474,2051,3179,0,0]Ai[3753,14,1834,0,4048,0,0]Al[4473,14,1835,1835,4049,0,0]Al[1002,14,1003,1003,2975,0,0]Al[4473,14,4474,2330,3782,0,0]Ai[3753,13,3754,0,4145,0,0]Al[4473,13,4474,1757,4146,0,0]Ai[3753,14,3527,0,4507,0,0]Al[4473,14,3528,3528,4508,0,0]An[1304,13,1305,1305,117,1914,0]Ao[1304,13,1305,1305,3784,1914,0]Ao[3753,13,3754,3532,3151,1914,0]Ao[3753,13,3754,3532,4663,1914,0]Ao[3753,13,3754,3532,4515,1914,0]Ao[3753,13,3754,2329,2286,1914,0]Ao[3753,13,3754,2329,4046,1914,0]Ao[3753,13,3754,3879,4162,1914,0]Ao[3753,13,3754,3879,2997,1914,0]Ao[3753,13,3754,3879,3174,1914,0]Ao[3753,13,3754,3879,4161,1914,0]Ao[3753,13,3754,3883,3191,1914,0]Ao[3753,13,3754,3883,3952,1914,0]Al[3753,13,3754,0,1528,1914,0]Al[3753,13,3754,0,4660,1914,0]Al[3753,13,3754,0,1514,1914,0]Al[3753,13,3754,0,2319,1914,0]Al[3753,13,3754,0,3157,1914,0]Al[3753,13,3754,0,2994,1914,0]Al[3753,13,3754,0,4256,1914,0]Al[3753,13,3754,0,1918,1914,0]Al[3753,13,3754,0,3912,1914,0]Al[3753,13,3754,0,4252,1914,0]Al[3753,13,3754,0,4269,1914,0]Al[3753,13,3754,0,4535,1914,0]Al[3753,13,3754,0,4551,1914,0]Al[3753,13,3754,0,3795,1914,0]Al[3753,13,3754,0,4145,1914,0]Al[3753,13,3754,0,3946,1914,0]Al[3753,13,3754,0,4717,1914,0]Al[3753,13,3754,0,3906,1914,0]Al[3753,13,3754,0,3787,1914,0]Al[3753,13,3754,0,2979,1914,0]Al[3753,13,3754,0,4250,1914,0]Al[3753,13,3754,0,4425,1914,0]Al[3753,13,3754,0,3918,1914,0]Al[3753,13,3754,0,3921,1914,0]Al[3753,13,3754,0,3925,1914,0]Al[3753,13,3754,0,4566,1914,0]Ak[3753,13,3754,0,457,1914,0]Ak[3753,13,3754,0,109,1914,0]Al[3753,13,3754,0,2307,1914,0]Ak[3753,13,3527,0,103,1914,0]Al[3753,13,3527,0,2972,1914,0]Al[3753,13,3527,0,1268,1914,0]Al[3753,13,3527,0,3792,1914,0]Al[3753,13,3527,0,1897,1914,0]Al[3753,13,3527,0,4059,1914,0]Al[3753,13,1834,0,4656,1914,0]Al[3753,13,1834,0,1281,1914,0]Ao[4473,13,4474,1867,1529,1914,0]Ao[4473,13,4474,1867,4661,1914,0]Ao[4473,13,4474,3022,4516,1914,0]Ao[4473,13,4474,1459,1515,1914,0]Ao[4473,13,4474,1459,3942,1914,0]Ao[4473,13,4474,1459,3168,1914,0]Ao[4473,13,4474,1459,2993,1914,0]Ao[4473,13,4474,1459,4565,1914,0]Ao[4473,13,4474,1459,3147,1914,0]Ao[4473,13,4474,1475,2320,1914,0]Ao[4473,13,4474,1475,3158,1914,0]Ao[4473,13,4474,1475,2995,1914,0]Ao[4473,13,4474,1475,4257,1914,0]Ao[4473,13,4474,1025,2998,1914,0]Ao[4473,13,4474,1025,4066,1914,0]Ao[4473,13,4474,1030,1919,1914,0]Ao[4473,13,4474,1030,3192,1914,0]Ao[4473,13,4474,1030,3794,1914,0]Ao[4473,13,4474,2051,4163,1914,0]Ao[4473,13,4474,2051,3916,1914,0]Ao[4473,13,4474,3533,4253,1914,0]Ao[4473,13,4474,3533,2990,1914,0]Ao[4473,13,4474,3533,4136,1914,0]Ao[4473,13,4474,3134,4270,1914,0]Ao[4473,13,4474,3134,4536,1914,0]Ao[4473,13,4474,3134,4552,1914,0]Ao[4473,13,4474,3134,3796,1914,0]Ao[4473,13,4474,1757,4146,1914,0]Ao[4473,13,4474,1757,3948,1914,0]Ao[4473,13,4474,1757,4718,1914,0]Ao[4473,13,4474,1757,3908,1914,0]Ao[4473,13,4474,1757,3788,1914,0]Ao[4473,13,4474,1449,2980,1914,0]Ao[4473,13,4474,1449,4251,1914,0]Ao[4473,13,4474,1449,4426,1914,0]Ao[4473,13,4474,1449,2280,1914,0]Ao[4473,13,4474,2330,4430,1914,0]Ao[4473,13,4474,2330,3919,1914,0]Ao[4473,13,4474,1819,4261,1914,0]Ao[4473,13,4474,1819,4743,1914,0]Ao[4473,13,4474,1819,2293,1914,0]Ao[4473,13,4474,1819,4514,1914,0]Ao[4473,13,4474,1845,3922,1914,0]Ao[4473,13,4474,1845,3926,1914,0]Al[4473,13,4474,0,4167,1914,0]Al[4473,13,4474,0,4567,1914,0]Ao[4473,13,4474,4075,3161,1914,0]Ao[4473,13,4474,4075,4836,1914,0]An[4473,13,4474,4474,458,1914,0]An[4473,13,4474,4474,110,1914,0]Ao[4473,13,4474,4474,2308,1914,0]Ao[4473,13,1755,1755,4070,1914,0]Ao[4473,13,4702,4702,4604,1914,0]Ao[4473,13,4702,4702,1283,1914,0]Ao[4473,13,1835,1835,4657,1914,0]Al[4473,13,1835,0,1282,1914,0]Am[4473,13,3528,125,104,1914,0]An[4473,13,3528,125,2973,1914,0]Ao[4473,13,3528,1444,1269,1914,0]Ao[4473,13,3528,1444,3793,1914,0]Ao[4473,13,3528,2378,1898,1914,0]Ao[4473,13,3528,2378,4060,1914,0]Ao[4473,13,2023,2023,1287,1914,0]Ao[4201,13,4202,4202,2305,1914,0]Ao[4201,13,4202,4202,2318,1914,0]Ao[4201,13,4202,4202,3927,1914,0]Ao[1002,13,1003,1003,4601,1914,0]Ao[1002,13,1474,1474,4259,1914,0]An[1304,13,1305,1305,117,1916,0]Ao[1304,13,1305,1305,3784,1916,0]Ao[1304,13,1473,1473,3807,1916,0]Ao[1304,13,1473,1473,1300,1916,0]Ao[1304,13,1473,1473,4164,1916,0]Al[467,13,468,468,3785,1916,0]Ao[3813,13,3814,3814,4744,1916,0]Ao[3753,13,3754,3532,4274,1916,0]Ao[3753,13,3754,3532,2298,1916,0]Al[3753,13,3754,0,4037,1916,0]Ao[3753,13,3754,1001,2299,1916,0]Ao[3753,13,3754,3532,3149,1916,0]Ao[3753,13,3754,3532,3151,1916,0]Ao[3753,13,3754,3532,1900,1916,0]Ao[3753,13,3754,3532,1507,1916,0]Ao[3753,13,3754,3532,2965,1916,0]Ao[3753,13,3754,3532,2276,1916,0]Ao[3753,13,3754,3532,3938,1916,0]Ao[3753,13,3754,3532,3943,1916,0]Ao[3753,13,3754,3532,4663,1916,0]Ao[3753,13,3754,3532,4515,1916,0]Ao[3753,13,3754,2329,2286,1916,0]Ao[3753,13,3754,2329,4046,1916,0]Ao[3753,13,3754,2329,1491,1916,0]Ao[3753,13,3754,2356,2290,1916,0]Ao[3753,13,3754,2356,3175,1916,0]Ao[3753,13,3754,2356,4150,1916,0]An[3753,13,3754,1001,452,1916,0]Ao[3753,13,3754,3879,3172,1916,0]Ao[3753,13,3754,3879,4162,1916,0]Ao[3753,13,3754,3879,2997,1916,0]Ao[3753,13,3754,3879,3174,1916,0]Ao[3753,13,3754,3879,4161,1916,0]Ao[3753,13,3754,1485,3953,1916,0]Ao[3753,13,3754,1042,3804,1916,0]Ao[3753,13,3754,3883,3178,1916,0]Ao[3753,13,3754,3883,3191,1916,0]Ao[3753,13,3754,3883,3952,1916,0]Al[3753,13,3754,0,3169,1916,0]Al[3753,13,3754,0,4068,1916,0]Al[3753,13,3754,0,4266,1916,0]Al[3753,13,3754,0,3934,1916,0]Al[3753,13,3754,0,4156,1916,0]Co[3753,13,3754,0,1528,1916,0,\"impl-TryFrom%3Cu64%3E-for-VarInt\"]Al[3753,13,3754,0,1528,1916,0]Da[3753,13,3754,0,1528,1916,0,\"impl-TryFrom%3Cusize%3E-for-VarInt\"]D`[3753,13,3754,0,1528,1916,0,\"impl-TryFrom%3Cu128%3E-for-VarInt\"]Al[3753,13,3754,0,4660,1916,0]Al[3753,13,3754,0,1263,1916,0]Al[3753,13,3754,0,1514,1916,0]Al[3753,13,3754,0,2319,1916,0]Al[3753,13,3754,0,3157,1916,0]Al[3753,13,3754,0,2994,1916,0]Al[3753,13,3754,0,4256,1916,0]Al[3753,13,3754,0,1918,1916,0]Al[3753,13,3754,0,3912,1916,0]Al[3753,13,3754,0,4252,1916,0]Al[3753,13,3754,0,4262,1916,0]Al[3753,13,3754,0,4269,1916,0]Al[3753,13,3754,0,4535,1916,0]Al[3753,13,3754,0,4551,1916,0]Al[3753,13,3754,0,3795,1916,0]0Al[3753,13,3754,0,4145,1916,0]Al[3753,13,3754,0,3946,1916,0]Al[3753,13,3754,0,4717,1916,0]Al[3753,13,3754,0,3906,1916,0]Al[3753,13,3754,0,3787,1916,0]Al[3753,13,3754,0,2979,1916,0]Al[3753,13,3754,0,4250,1916,0]Al[3753,13,3754,0,4425,1916,0]Al[3753,13,3754,0,3918,1916,0]Al[3753,13,3754,0,3921,1916,0]Al[3753,13,3754,0,3925,1916,0]Al[3753,13,3754,0,4566,1916,0]Al[3753,13,3754,0,4449,1916,0]Ak[3753,13,3754,0,457,1916,0]Ak[3753,13,3754,0,109,1916,0]Al[3753,13,3754,0,2307,1916,0]Al[3753,13,3754,0,2316,1916,0]Ak[3753,13,3527,0,103,1916,0]Al[3753,13,3527,0,2972,1916,0]Al[3753,13,3527,0,1268,1916,0]Al[3753,13,3527,0,3792,1916,0]Al[3753,13,3527,0,1897,1916,0]Al[3753,13,3527,0,4059,1916,0]Al[3753,13,3527,0,4507,1916,0]Al[3753,13,1759,0,1894,1916,0]Ak[3753,13,1759,0,447,1916,0]Al[3753,13,1759,0,4802,1916,0]Al[3753,13,1759,0,3790,1916,0]Al[3753,13,1759,0,4570,1916,0]Al[3753,13,1834,0,3186,1916,0]Al[3753,13,1834,0,4048,1916,0]Eb[3753,13,1834,0,4445,1916,0,\"impl-TryFrom%3CClientConfig%3E-for-QuicClientConfig\"]Ek[3753,13,1834,0,4445,1916,0,\"impl-TryFrom%3CArc%3CClientConfig%3E%3E-for-QuicClientConfig\"]Al[3753,13,1834,0,4445,1916,0]Eb[3753,13,1834,0,4447,1916,0,\"impl-TryFrom%3CServerConfig%3E-for-QuicServerConfig\"]Ek[3753,13,1834,0,4447,1916,0,\"impl-TryFrom%3CArc%3CServerConfig%3E%3E-for-QuicServerConfig\"]Al[3753,13,1834,0,4447,1916,0]Al[3753,13,1834,0,4656,1916,0]Al[3753,13,1834,0,1281,1916,0]Ao[4473,13,4474,4279,4038,1916,0]Ao[4473,13,4474,3022,2977,1916,0]Ao[4473,13,4474,1025,1497,1916,0]Ao[4473,13,4474,2051,1913,1916,0]Ao[4473,13,4474,2051,3173,1916,0]Ao[4473,13,4474,2051,3179,1916,0]Ao[4473,13,4474,3533,3152,1916,0]Ao[4473,13,4474,1021,3170,1916,0]Ao[4473,13,4474,1757,4069,1916,0]Ao[4473,13,4474,2330,2287,1916,0]Ao[4473,13,4474,2330,4044,1916,0]Ao[4473,13,4474,2330,2291,1916,0]Ao[4473,13,4474,1819,4860,1916,0]Ao[4473,13,4474,4075,4831,1916,0]Ao[4473,13,4474,1484,4267,1916,0]Ao[4473,13,4474,1484,3935,1916,0]Ao[4473,13,4474,1484,4157,1916,0]Ao[4473,13,4474,1867,1529,1916,0]Db[4473,13,4474,1867,1529,1916,0,\"impl-TryFrom%3Cu64%3E-for-VarInt\"]Dd[4473,13,4474,1867,1529,1916,0,\"impl-TryFrom%3Cusize%3E-for-VarInt\"]Dc[4473,13,4474,1867,1529,1916,0,\"impl-TryFrom%3Cu128%3E-for-VarInt\"]Ao[4473,13,4474,1867,4661,1916,0]Ao[4473,13,4474,3013,1264,1916,0]Ao[4473,13,4474,3022,4516,1916,0]Ao[4473,13,4474,1459,1515,1916,0]Ao[4473,13,4474,1459,3942,1916,0]Ao[4473,13,4474,1459,3168,1916,0]Ao[4473,13,4474,1459,2993,1916,0]Ao[4473,13,4474,1459,4565,1916,0]Ao[4473,13,4474,1459,3147,1916,0]Ao[4473,13,4474,1475,2320,1916,0]Ao[4473,13,4474,1475,3158,1916,0]Ao[4473,13,4474,1475,2995,1916,0]Ao[4473,13,4474,1475,4257,1916,0]Ao[4473,13,4474,1025,2998,1916,0]Ao[4473,13,4474,1025,4066,1916,0]Ao[4473,13,4474,1030,1919,1916,0]Ao[4473,13,4474,1030,3192,1916,0]Ao[4473,13,4474,1030,3794,1916,0]Ao[4473,13,4474,2051,3806,1916,0]Ao[4473,13,4474,2051,4163,1916,0]Ao[4473,13,4474,2051,3916,1916,0]Ao[4473,13,4474,3533,4253,1916,0]Ao[4473,13,4474,3533,2990,1916,0]Ao[4473,13,4474,3533,4136,1916,0]Ao[4473,13,4474,3533,1286,1916,0]Ao[4473,13,4474,1021,4263,1916,0]Ao[4473,13,4474,3134,4270,1916,0]Ao[4473,13,4474,3134,4536,1916,0]Ao[4473,13,4474,3134,4552,1916,0]Ao[4473,13,4474,3134,3796,1916,0]0Ao[4473,13,4474,1757,4146,1916,0]Ao[4473,13,4474,1757,3948,1916,0]Ao[4473,13,4474,1757,4718,1916,0]Ao[4473,13,4474,1757,3908,1916,0]Ao[4473,13,4474,1757,3788,1916,0]Ao[4473,13,4474,1449,2980,1916,0]Ao[4473,13,4474,1449,4251,1916,0]Ao[4473,13,4474,1449,4426,1916,0]Ao[4473,13,4474,1449,2280,1916,0]Ao[4473,13,4474,2330,4430,1916,0]Ao[4473,13,4474,2330,3919,1916,0]Ao[4473,13,4474,2330,3782,1916,0]Ao[4473,13,4474,2330,3176,1916,0]Ao[4473,13,4474,1819,4062,1916,0]Ao[4473,13,4474,1819,4261,1916,0]Ao[4473,13,4474,1819,4743,1916,0]Ao[4473,13,4474,1819,2293,1916,0]Ao[4473,13,4474,1819,4514,1916,0]Ao[4473,13,4474,1845,4255,1916,0]Ao[4473,13,4474,1845,4045,1916,0]Ao[4473,13,4474,1845,3922,1916,0]Ao[4473,13,4474,1845,3926,1916,0]Al[4473,13,4474,0,4167,1916,0]Al[4473,13,4474,0,4567,1916,0]Ao[4473,13,4474,4075,3161,1916,0]Ao[4473,13,4474,4075,4836,1916,0]Ao[4473,13,4474,4597,4450,1916,0]An[4473,13,4474,4474,458,1916,0]An[4473,13,4474,4474,110,1916,0]Ao[4473,13,4474,4474,2308,1916,0]Ao[4473,13,4474,4474,2317,1916,0]Ao[4473,13,1755,1755,4070,1916,0]Ao[4473,13,4702,4702,4604,1916,0]Ao[4473,13,4702,4702,1283,1916,0]Ao[4473,13,1760,1760,1895,1916,0]An[4473,13,1760,1760,448,1916,0]Ao[4473,13,1760,1760,4803,1916,0]Ao[4473,13,1760,1760,3791,1916,0]Ao[4473,13,1760,1760,4573,1916,0]Ao[4473,13,1835,1835,3187,1916,0]Ao[4473,13,1835,1835,4049,1916,0]Ao[4473,13,1835,1835,4446,1916,0]En[4473,13,1835,1835,4446,1916,0,\"impl-TryFrom%3CArc%3CClientConfig%3E%3E-for-QuicClientConfig\"]Ee[4473,13,1835,1835,4446,1916,0,\"impl-TryFrom%3CClientConfig%3E-for-QuicClientConfig\"]En[4473,13,1835,1835,4448,1916,0,\"impl-TryFrom%3CArc%3CServerConfig%3E%3E-for-QuicServerConfig\"]Ao[4473,13,1835,1835,4448,1916,0]Ee[4473,13,1835,1835,4448,1916,0,\"impl-TryFrom%3CServerConfig%3E-for-QuicServerConfig\"]Ao[4473,13,1835,1835,4657,1916,0]Al[4473,13,1835,0,1282,1916,0]Am[4473,13,3528,125,104,1916,0]An[4473,13,3528,125,2973,1916,0]Ao[4473,13,3528,1444,1269,1916,0]Ao[4473,13,3528,1444,3793,1916,0]Ao[4473,13,3528,2378,1898,1916,0]Ao[4473,13,3528,2378,4060,1916,0]Ao[4473,13,3528,3528,4508,1916,0]Ao[4473,13,2023,2023,1284,1916,0]Ao[4473,13,2023,2023,1287,1916,0]Ao[4201,13,4202,4202,3190,1916,0]An[4201,13,4202,368,4168,1916,0]Ao[4201,13,4202,4202,2305,1916,0]Ao[4201,13,4202,4202,2318,1916,0]Ao[4201,13,4202,4202,3927,1916,0]Ao[1002,13,1003,1003,2975,1916,0]Ao[1002,13,1003,1003,4601,1916,0]An[1002,13,1749,1749,118,1916,0]Ao[1002,13,1474,1474,1301,1916,0]Ao[1002,13,1474,1474,3808,1916,0]Ao[1002,13,1474,1474,4259,1916,0]Ao[1002,13,4004,4004,4788,1916,0]Ao[1002,13,4004,4004,4789,1916,0]An[1002,13,1840,1840,119,1916,0]An[3773,13,3774,3774,108,1916,0]Ao[3773,13,3774,3774,2279,1916,0]An[1304,13,1305,1305,117,1917,0]Ao[1304,13,1305,1305,3784,1917,0]Ao[1304,13,1473,1473,3807,1917,0]Ao[1304,13,1473,1473,1300,1917,0]Ao[1304,13,1473,1473,4164,1917,0]Al[467,13,468,468,3785,1917,0]Ao[3813,13,3814,3814,4744,1917,0]Ao[3753,13,3754,3532,4274,1917,0]Ao[3753,13,3754,3532,2298,1917,0]Al[3753,13,3754,0,4037,1917,0]Ao[3753,13,3754,1001,2299,1917,0]Ao[3753,13,3754,3532,3149,1917,0]Ao[3753,13,3754,3532,3151,1917,0]Ao[3753,13,3754,3532,1900,1917,0]Ao[3753,13,3754,3532,1507,1917,0]Ao[3753,13,3754,3532,2965,1917,0]Ao[3753,13,3754,3532,2276,1917,0]Ao[3753,13,3754,3532,3938,1917,0]Ao[3753,13,3754,3532,3943,1917,0]Ao[3753,13,3754,3532,4663,1917,0]Ao[3753,13,3754,3532,4515,1917,0]Ao[3753,13,3754,2329,2286,1917,0]Ao[3753,13,3754,2329,4046,1917,0]Ao[3753,13,3754,2329,1491,1917,0]Ao[3753,13,3754,2356,2290,1917,0]Ao[3753,13,3754,2356,3175,1917,0]Ao[3753,13,3754,2356,4150,1917,0]An[3753,13,3754,1001,452,1917,0]Ao[3753,13,3754,3879,3172,1917,0]Ao[3753,13,3754,3879,4162,1917,0]Ao[3753,13,3754,3879,2997,1917,0]Ao[3753,13,3754,3879,3174,1917,0]Ao[3753,13,3754,3879,4161,1917,0]Ao[3753,13,3754,1485,3953,1917,0]Ao[3753,13,3754,1042,3804,1917,0]Ao[3753,13,3754,3883,3178,1917,0]Ao[3753,13,3754,3883,3191,1917,0]Ao[3753,13,3754,3883,3952,1917,0]Al[3753,13,3754,0,3169,1917,0]Al[3753,13,3754,0,4068,1917,0]Al[3753,13,3754,0,4266,1917,0]Al[3753,13,3754,0,3934,1917,0]Al[3753,13,3754,0,4156,1917,0]Al[3753,13,3754,0,1528,1917,0]Al[3753,13,3754,0,4660,1917,0]Al[3753,13,3754,0,1263,1917,0]Al[3753,13,3754,0,1514,1917,0]Al[3753,13,3754,0,2319,1917,0]Al[3753,13,3754,0,3157,1917,0]Al[3753,13,3754,0,2994,1917,0]Al[3753,13,3754,0,4256,1917,0]Al[3753,13,3754,0,1918,1917,0]Al[3753,13,3754,0,3912,1917,0]Al[3753,13,3754,0,4252,1917,0]Al[3753,13,3754,0,4262,1917,0]Al[3753,13,3754,0,4269,1917,0]Al[3753,13,3754,0,4535,1917,0]Al[3753,13,3754,0,4551,1917,0]Al[3753,13,3754,0,3795,1917,0]Al[3753,13,3754,0,4145,1917,0]Al[3753,13,3754,0,3946,1917,0]Al[3753,13,3754,0,4717,1917,0]Al[3753,13,3754,0,3906,1917,0]Al[3753,13,3754,0,3787,1917,0]Al[3753,13,3754,0,2979,1917,0]Al[3753,13,3754,0,4250,1917,0]Al[3753,13,3754,0,4425,1917,0]Al[3753,13,3754,0,3918,1917,0]Al[3753,13,3754,0,3921,1917,0]Al[3753,13,3754,0,3925,1917,0]Al[3753,13,3754,0,4566,1917,0]Al[3753,13,3754,0,4449,1917,0]Ak[3753,13,3754,0,457,1917,0]Ak[3753,13,3754,0,109,1917,0]Al[3753,13,3754,0,2307,1917,0]Al[3753,13,3754,0,2316,1917,0]Ak[3753,13,3527,0,103,1917,0]Al[3753,13,3527,0,2972,1917,0]Al[3753,13,3527,0,1268,1917,0]Al[3753,13,3527,0,3792,1917,0]Al[3753,13,3527,0,1897,1917,0]Al[3753,13,3527,0,4059,1917,0]Al[3753,13,3527,0,4507,1917,0]Al[3753,13,1759,0,1894,1917,0]Ak[3753,13,1759,0,447,1917,0]Al[3753,13,1759,0,4802,1917,0]Al[3753,13,1759,0,3790,1917,0]Al[3753,13,1759,0,4570,1917,0]Al[3753,13,1834,0,3186,1917,0]Al[3753,13,1834,0,4048,1917,0]Al[3753,13,1834,0,4445,1917,0]Al[3753,13,1834,0,4447,1917,0]Al[3753,13,1834,0,4656,1917,0]Al[3753,13,1834,0,1281,1917,0]Ao[4473,13,4474,4279,4038,1917,0]Ao[4473,13,4474,3022,2977,1917,0]Ao[4473,13,4474,1025,1497,1917,0]Ao[4473,13,4474,2051,1913,1917,0]Ao[4473,13,4474,2051,3173,1917,0]Ao[4473,13,4474,2051,3179,1917,0]Ao[4473,13,4474,3533,3152,1917,0]Ao[4473,13,4474,1021,3170,1917,0]Ao[4473,13,4474,1757,4069,1917,0]Ao[4473,13,4474,2330,2287,1917,0]Ao[4473,13,4474,2330,4044,1917,0]Ao[4473,13,4474,2330,2291,1917,0]Ao[4473,13,4474,1819,4860,1917,0]Ao[4473,13,4474,4075,4831,1917,0]Ao[4473,13,4474,1484,4267,1917,0]Ao[4473,13,4474,1484,3935,1917,0]Ao[4473,13,4474,1484,4157,1917,0]Ao[4473,13,4474,1867,1529,1917,0]Ao[4473,13,4474,1867,4661,1917,0]Ao[4473,13,4474,3013,1264,1917,0]Ao[4473,13,4474,3022,4516,1917,0]Ao[4473,13,4474,1459,1515,1917,0]Ao[4473,13,4474,1459,3942,1917,0]Ao[4473,13,4474,1459,3168,1917,0]Ao[4473,13,4474,1459,2993,1917,0]Ao[4473,13,4474,1459,4565,1917,0]Ao[4473,13,4474,1459,3147,1917,0]Ao[4473,13,4474,1475,2320,1917,0]Ao[4473,13,4474,1475,3158,1917,0]Ao[4473,13,4474,1475,2995,1917,0]Ao[4473,13,4474,1475,4257,1917,0]Ao[4473,13,4474,1025,2998,1917,0]Ao[4473,13,4474,1025,4066,1917,0]Ao[4473,13,4474,1030,1919,1917,0]Ao[4473,13,4474,1030,3192,1917,0]Ao[4473,13,4474,1030,3794,1917,0]Ao[4473,13,4474,2051,3806,1917,0]Ao[4473,13,4474,2051,4163,1917,0]Ao[4473,13,4474,2051,3916,1917,0]Ao[4473,13,4474,3533,4253,1917,0]Ao[4473,13,4474,3533,2990,1917,0]Ao[4473,13,4474,3533,4136,1917,0]Ao[4473,13,4474,3533,1286,1917,0]Ao[4473,13,4474,1021,4263,1917,0]Ao[4473,13,4474,3134,4270,1917,0]Ao[4473,13,4474,3134,4536,1917,0]Ao[4473,13,4474,3134,4552,1917,0]Ao[4473,13,4474,3134,3796,1917,0]Ao[4473,13,4474,1757,4146,1917,0]Ao[4473,13,4474,1757,3948,1917,0]Ao[4473,13,4474,1757,4718,1917,0]Ao[4473,13,4474,1757,3908,1917,0]Ao[4473,13,4474,1757,3788,1917,0]Ao[4473,13,4474,1449,2980,1917,0]Ao[4473,13,4474,1449,4251,1917,0]Ao[4473,13,4474,1449,4426,1917,0]Ao[4473,13,4474,1449,2280,1917,0]Ao[4473,13,4474,2330,4430,1917,0]Ao[4473,13,4474,2330,3919,1917,0]Ao[4473,13,4474,2330,3782,1917,0]Ao[4473,13,4474,2330,3176,1917,0]Ao[4473,13,4474,1819,4062,1917,0]Ao[4473,13,4474,1819,4261,1917,0]Ao[4473,13,4474,1819,4743,1917,0]Ao[4473,13,4474,1819,2293,1917,0]Ao[4473,13,4474,1819,4514,1917,0]Ao[4473,13,4474,1845,4255,1917,0]Ao[4473,13,4474,1845,4045,1917,0]Ao[4473,13,4474,1845,3922,1917,0]Ao[4473,13,4474,1845,3926,1917,0]Al[4473,13,4474,0,4167,1917,0]Al[4473,13,4474,0,4567,1917,0]Ao[4473,13,4474,4075,3161,1917,0]Ao[4473,13,4474,4075,4836,1917,0]Ao[4473,13,4474,4597,4450,1917,0]An[4473,13,4474,4474,458,1917,0]An[4473,13,4474,4474,110,1917,0]Ao[4473,13,4474,4474,2308,1917,0]Ao[4473,13,4474,4474,2317,1917,0]Ao[4473,13,1755,1755,4070,1917,0]Ao[4473,13,4702,4702,4604,1917,0]Ao[4473,13,4702,4702,1283,1917,0]Ao[4473,13,1760,1760,1895,1917,0]An[4473,13,1760,1760,448,1917,0]Ao[4473,13,1760,1760,4803,1917,0]Ao[4473,13,1760,1760,3791,1917,0]Ao[4473,13,1760,1760,4573,1917,0]Ao[4473,13,1835,1835,3187,1917,0]Ao[4473,13,1835,1835,4049,1917,0]Ao[4473,13,1835,1835,4446,1917,0]Ao[4473,13,1835,1835,4448,1917,0]Ao[4473,13,1835,1835,4657,1917,0]Al[4473,13,1835,0,1282,1917,0]Am[4473,13,3528,125,104,1917,0]An[4473,13,3528,125,2973,1917,0]Ao[4473,13,3528,1444,1269,1917,0]Ao[4473,13,3528,1444,3793,1917,0]Ao[4473,13,3528,2378,1898,1917,0]Ao[4473,13,3528,2378,4060,1917,0]Ao[4473,13,3528,3528,4508,1917,0]Ao[4473,13,2023,2023,1284,1917,0]Ao[4473,13,2023,2023,1287,1917,0]Ao[4201,13,4202,4202,3190,1917,0]An[4201,13,4202,368,4168,1917,0]Ao[4201,13,4202,4202,2305,1917,0]Ao[4201,13,4202,4202,2318,1917,0]Ao[4201,13,4202,4202,3927,1917,0]Ao[1002,13,1003,1003,2975,1917,0]Ao[1002,13,1003,1003,4601,1917,0]An[1002,13,1749,1749,118,1917,0]Ao[1002,13,1474,1474,1301,1917,0]Ao[1002,13,1474,1474,3808,1917,0]Ao[1002,13,1474,1474,4259,1917,0]Ao[1002,13,4004,4004,4788,1917,0]Ao[1002,13,4004,4004,4789,1917,0]An[1002,13,1840,1840,119,1917,0]An[3773,13,3774,3774,108,1917,0]Ao[3773,13,3774,3774,2279,1917,0]Ao[3753,13,3754,1001,2299,2999,0]Ao[3753,13,3754,3532,3149,2999,0]Ao[3753,13,3754,3532,1900,2999,0]Ao[3753,13,3754,3532,1507,2999,0]Ao[3753,13,3754,3532,2965,2999,0]Ao[3753,13,3754,3532,2276,2999,0]Ao[3753,13,3754,3532,3938,2999,0]Ao[3753,13,3754,3532,3943,2999,0]Ao[3753,13,3754,2356,4150,2999,0]Ak[4201,13,4202,368,4168,0,0]Ai[3753,13,3754,0,4708,0,0]Al[4473,13,4474,4075,4709,0,0]0Ao[4473,13,4474,4075,4831,4709,0]Al[4473,15,4474,1459,2993,0,0]Ah[3753,5,3754,3532,0,0,0]Al[4473,15,4474,1459,3168,0,0]Al[4473,15,4474,2051,3806,0,0]Ag[3753,5,3527,125,0,0,0]Ag[4473,5,3528,125,0,0,0]Ae[1002,5,1003,0,0,0,0]Al[4473,15,4474,3533,1286,0,0]Ah[4473,5,4474,3022,0,0,0]Ah[3753,5,3754,1449,0,0,0]Ah[4473,5,4474,1449,0,0,0]Al[4473,15,4474,1819,2293,0,0]Ai[3753,10,1759,1760,0,0,0]Af[4473,10,1760,0,0,0,0]Al[4473,15,4702,4702,1283,0,0]Al[4473,15,2023,2023,1284,0,0]32Ah[4473,6,4474,3533,0,0,0]Ah[4473,6,4474,1459,0,0,0]Ah[3753,5,3754,1475,0,0,0]Ah[4473,5,4474,1475,0,0,0]Al[3753,15,3754,3879,4161,0,0]Ah[3753,6,3754,3879,0,0,0]Ah[4473,6,4474,1025,0,0,0]Ai[3753,10,3754,2043,0,0,0]Al[3753,13,3754,3532,3151,0,0]Al[3753,13,3754,0,1528,2966,0]Ak[3753,13,3754,0,457,2966,0]Ak[3753,13,3754,0,109,2966,0]Al[3753,13,3754,0,2307,2966,0]Ao[4473,13,4474,1867,1529,2966,0]An[4473,13,4474,4474,458,2966,0]An[4473,13,4474,4474,110,2966,0]Ao[4473,13,4474,4474,2308,2966,0]Al[1304,13,1305,1305,3784,0,0]Ai[3753,12,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,12,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Ai[3753,14,3754,0,2319,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,14,4474,1475,2320,0,0]Al[3753,13,3754,0,3921,2281,0]Ao[4473,13,4474,1845,3922,2281,0]Ai[3753,13,3754,0,3925,0,0]Al[4473,13,4474,1845,3926,0,0]Al[4201,13,4202,4202,3927,0,0]Ai[3753,13,3754,0,2307,0,0]Al[4473,13,4474,4474,2308,0,0]Al[3753,13,3754,3532,3149,0,0]Ah[3753,13,3754,0,457,0,0]Ak[4473,13,4474,4474,458,0,0];10Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,2330,2291,0,0]Ai[3753,13,3754,0,3946,0,0]Al[4473,13,4474,1757,3948,0,0]Al[3753,12,3754,2043,1905,0,0]Ao[3753,13,3754,1485,3953,1905,0]Ao[3753,13,3754,1042,3804,1905,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[3753,13,3754,3532,3151,0,0]0Al[4473,13,4474,3533,3152,0,0]320Co[3753,13,3754,3879,3172,2967,0,\"impl-AsyncRead-for-RecvStream\"]Co[3753,13,3754,3879,3172,2968,0,\"impl-AsyncRead-for-RecvStream\"]Bn[3753,13,3754,3879,3172,0,0,\"impl-RecvStream\"]Al[3753,12,3754,2043,4133,0,0]Al[3753,12,3754,2043,3000,0,0]87Ai[3753,13,3754,0,4145,0,0]Al[4473,13,4474,1757,4146,0,0]Ai[3753,12,1759,0,3947,0,0]Al[3753,13,1834,0,4447,3947,0]Al[4473,12,1760,1760,3949,0,0]Ao[4473,13,1835,1835,4448,3949,0]Ao[1002,13,4004,4004,4789,3949,0]Ak[3753,13,3754,0,457,2966,0]Ak[3753,13,3754,0,109,2966,0]Al[3753,13,3754,0,2307,2966,0]An[4473,13,4474,4474,458,2966,0]An[4473,13,4474,4474,110,2966,0]Ao[4473,13,4474,4474,2308,2966,0]Al[3753,13,3754,3532,3151,0,0]Ao[3753,13,3754,3532,4515,2311,0]Ao[3753,13,3754,2356,3175,2311,0]Ao[3753,13,3754,3879,4162,2311,0]Ao[3753,13,3754,3879,2997,2311,0]Ao[3753,13,3754,3879,3174,2311,0]Ao[3753,13,3754,3879,4161,2311,0]Ao[3753,13,3754,3883,3191,2311,0]Ao[3753,13,3754,3883,3952,2311,0]Al[3753,13,3754,0,1528,2311,0]Al[3753,13,3754,0,4660,2311,0]Al[3753,13,3754,0,1514,2311,0]Al[3753,13,3754,0,3912,2311,0]Al[3753,13,3754,0,4252,2311,0]Al[3753,13,3754,0,3787,2311,0]Al[3753,13,3754,0,2979,2311,0]Al[3753,13,3754,0,4250,2311,0]Al[3753,13,3754,0,4425,2311,0]Al[3753,13,3754,0,3918,2311,0]Al[3753,13,3754,0,3921,2311,0]Al[3753,13,3754,0,4566,2311,0]Ak[3753,13,3754,0,109,2311,0]Al[3753,13,3754,0,2307,2311,0]Al[3753,13,1834,0,4656,2311,0]Al[3753,13,1834,0,1281,2311,0]Ao[4473,13,4474,1867,1529,2311,0]Ao[4473,13,4474,1867,4661,2311,0]Ao[4473,13,4474,3022,4516,2311,0]Ao[4473,13,4474,1459,1515,2311,0]Ao[4473,13,4474,1459,4565,2311,0]Ao[4473,13,4474,1459,3147,2311,0]Ao[4473,13,4474,1025,2998,2311,0]Ao[4473,13,4474,1025,4066,2311,0]Ao[4473,13,4474,1030,3192,2311,0]Ao[4473,13,4474,1030,3794,2311,0]Ao[4473,13,4474,2051,3916,2311,0]Ao[4473,13,4474,3533,4253,2311,0]Ao[4473,13,4474,3533,2990,2311,0]Ao[4473,13,4474,3533,4136,2311,0]Ao[4473,13,4474,1757,3788,2311,0]Ao[4473,13,4474,1449,2980,2311,0]Ao[4473,13,4474,1449,4251,2311,0]Ao[4473,13,4474,1449,4426,2311,0]Ao[4473,13,4474,2330,3919,2311,0]Ao[4473,13,4474,2330,3176,2311,0]Ao[4473,13,4474,1819,4514,2311,0]Ao[4473,13,4474,1845,3922,2311,0]Al[4473,13,4474,0,4167,2311,0]Al[4473,13,4474,0,4567,2311,0]An[4473,13,4474,4474,110,2311,0]Ao[4473,13,4474,4474,2308,2311,0]Ao[4473,13,1755,1755,4070,2311,0]Ao[4473,13,4702,4702,1283,2311,0]Ao[4473,13,1835,1835,4657,2311,0]Al[4473,13,1835,0,1282,2311,0]Ao[4473,13,2023,2023,1284,2311,0]Ai[3753,13,3754,0,3946,0,0]Al[4473,13,4474,1757,3948,0,0]Al[4473,14,4474,1819,4743,0,0]Ai[3753,14,3754,0,3946,0,0]Al[4473,14,4474,1757,3948,0,0]Al[3753,13,3754,2329,2286,0,0]Al[3753,13,3754,3883,3178,0,0]Ai[3753,10,3754,2043,0,0,0]Af[4201,18,4202,0,0,0,0]Al[4473,15,4474,1459,4565,0,0]Al[4473,15,4474,3533,4136,0,0]Ah[4473,5,4474,1459,0,0,0]Ah[3753,5,3754,3532,0,0,0]0Ah[4473,5,4474,3533,0,0,0]Ai[3753,10,3527,3528,0,0,0]Af[4473,10,3528,0,0,0,0]Ah[3753,5,3754,1475,0,0,0]Ah[4473,5,4474,1475,0,0,0]Ah[4473,5,4474,4075,0,0,0]Ah[4473,6,4474,1459,0,0,0]Ah[3753,5,3754,1021,0,0,0]Ah[4473,5,4474,1021,0,0,0]Ah[3753,5,3754,3879,0,0,0]Ah[4473,5,4474,2051,0,0,0]Ah[3753,6,3754,3879,0,0,0]Ah[3753,5,3754,2356,0,0,0]Ah[4473,5,4474,2330,0,0,0]Ah[3753,5,3754,3883,0,0,0]4Ai[3753,10,3754,1757,0,0,0]Ai[4473,10,4474,1757,0,0,0]Ah[3753,5,1834,1835,0,0,0]Ae[4473,5,1835,0,0,0,0]Ai[3753,10,3754,1484,0,0,0]Ai[4473,10,4474,1484,0,0,0]Ae[4201,5,4202,0,0,0,0]Ah[3753,6,3754,3883,0,0,0]Ah[4473,6,4474,1030,0,0,0]Al[3753,13,3754,3532,3151,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]An[1304,13,1305,1305,117,2974,0]Ao[1304,13,1305,1305,3784,2974,0]Ao[1304,13,1473,1473,3807,2974,0]Ao[1304,13,1473,1473,1300,2974,0]Ao[1304,13,1473,1473,4164,2974,0]Al[467,13,468,468,3785,2974,0]Ao[3813,13,3814,3814,4744,2974,0]Ao[3753,13,3754,3532,4274,2974,0]Ao[3753,13,3754,3532,2298,2974,0]Al[3753,13,3754,0,4037,2974,0]Ao[3753,13,3754,1001,2299,2974,0]Ao[3753,13,3754,3532,3149,2974,0]Ao[3753,13,3754,3532,3151,2974,0]Ao[3753,13,3754,3532,1900,2974,0]Ao[3753,13,3754,3532,1507,2974,0]Ao[3753,13,3754,3532,2965,2974,0]Ao[3753,13,3754,3532,2276,2974,0]Ao[3753,13,3754,3532,3938,2974,0]Ao[3753,13,3754,3532,3943,2974,0]Ao[3753,13,3754,3532,4663,2974,0]Ao[3753,13,3754,3532,4515,2974,0]Ao[3753,13,3754,2329,2286,2974,0]Ao[3753,13,3754,2329,4046,2974,0]Ao[3753,13,3754,2329,1491,2974,0]Ao[3753,13,3754,2356,2290,2974,0]Ao[3753,13,3754,2356,3175,2974,0]Ao[3753,13,3754,2356,4150,2974,0]An[3753,13,3754,1001,452,2974,0]Ao[3753,13,3754,3879,3172,2974,0]Ao[3753,13,3754,3879,4162,2974,0]Ao[3753,13,3754,3879,2997,2974,0]Ao[3753,13,3754,3879,3174,2974,0]Ao[3753,13,3754,3879,4161,2974,0]Ao[3753,13,3754,1485,3953,2974,0]Ao[3753,13,3754,1042,3804,2974,0]Ao[3753,13,3754,3883,3178,2974,0]Ao[3753,13,3754,3883,3191,2974,0]Ao[3753,13,3754,3883,3952,2974,0]Al[3753,13,3754,0,3169,2974,0]Al[3753,13,3754,0,4068,2974,0]Al[3753,13,3754,0,4266,2974,0]Al[3753,13,3754,0,3934,2974,0]Al[3753,13,3754,0,4156,2974,0]Al[3753,13,3754,0,1528,2974,0]Al[3753,13,3754,0,4660,2974,0]Al[3753,13,3754,0,1263,2974,0]Al[3753,13,3754,0,1514,2974,0]Al[3753,13,3754,0,2319,2974,0]Al[3753,13,3754,0,3157,2974,0]Al[3753,13,3754,0,2994,2974,0]Al[3753,13,3754,0,4256,2974,0]Al[3753,13,3754,0,1918,2974,0]Al[3753,13,3754,0,3912,2974,0]Al[3753,13,3754,0,4252,2974,0]Al[3753,13,3754,0,4262,2974,0]Al[3753,13,3754,0,4269,2974,0]Al[3753,13,3754,0,4535,2974,0]Al[3753,13,3754,0,4551,2974,0]Al[3753,13,3754,0,3795,2974,0]Al[3753,13,3754,0,4145,2974,0]Al[3753,13,3754,0,3946,2974,0]Al[3753,13,3754,0,4717,2974,0]Al[3753,13,3754,0,3906,2974,0]Al[3753,13,3754,0,3787,2974,0]Al[3753,13,3754,0,2979,2974,0]Al[3753,13,3754,0,4250,2974,0]Al[3753,13,3754,0,4425,2974,0]Al[3753,13,3754,0,3918,2974,0]Al[3753,13,3754,0,3921,2974,0]Al[3753,13,3754,0,3925,2974,0]Al[3753,13,3754,0,4566,2974,0]Al[3753,13,3754,0,4449,2974,0]Ak[3753,13,3754,0,457,2974,0]Ak[3753,13,3754,0,109,2974,0]Al[3753,13,3754,0,2307,2974,0]Al[3753,13,3754,0,2316,2974,0]Ak[3753,13,3527,0,103,2974,0]Al[3753,13,3527,0,2972,2974,0]Al[3753,13,3527,0,1268,2974,0]Al[3753,13,3527,0,3792,2974,0]Al[3753,13,3527,0,1897,2974,0]Al[3753,13,3527,0,4059,2974,0]Al[3753,13,3527,0,4507,2974,0]Al[3753,13,1759,0,1894,2974,0]Ak[3753,13,1759,0,447,2974,0]Al[3753,13,1759,0,4802,2974,0]Al[3753,13,1759,0,3790,2974,0]Al[3753,13,1759,0,4570,2974,0]Al[3753,13,1834,0,3186,2974,0]Al[3753,13,1834,0,4048,2974,0]Al[3753,13,1834,0,4445,2974,0]Al[3753,13,1834,0,4447,2974,0]Al[3753,13,1834,0,4656,2974,0]Al[3753,13,1834,0,1281,2974,0]Ao[4473,13,4474,4279,4038,2974,0]Ao[4473,13,4474,3022,2977,2974,0]Ao[4473,13,4474,1025,1497,2974,0]Ao[4473,13,4474,2051,1913,2974,0]Ao[4473,13,4474,2051,3173,2974,0]Ao[4473,13,4474,2051,3179,2974,0]Ao[4473,13,4474,3533,3152,2974,0]Ao[4473,13,4474,1021,3170,2974,0]Ao[4473,13,4474,1757,4069,2974,0]Ao[4473,13,4474,2330,2287,2974,0]Ao[4473,13,4474,2330,4044,2974,0]Ao[4473,13,4474,2330,2291,2974,0]Ao[4473,13,4474,1819,4860,2974,0]Ao[4473,13,4474,4075,4831,2974,0]Ao[4473,13,4474,1484,4267,2974,0]Ao[4473,13,4474,1484,3935,2974,0]Ao[4473,13,4474,1484,4157,2974,0]Ao[4473,13,4474,1867,1529,2974,0]Ao[4473,13,4474,1867,4661,2974,0]Ao[4473,13,4474,3013,1264,2974,0]Ao[4473,13,4474,3022,4516,2974,0]Ao[4473,13,4474,1459,1515,2974,0]Ao[4473,13,4474,1459,3942,2974,0]Ao[4473,13,4474,1459,3168,2974,0]Ao[4473,13,4474,1459,2993,2974,0]Ao[4473,13,4474,1459,4565,2974,0]Ao[4473,13,4474,1459,3147,2974,0]Ao[4473,13,4474,1475,2320,2974,0]Ao[4473,13,4474,1475,3158,2974,0]Ao[4473,13,4474,1475,2995,2974,0]Ao[4473,13,4474,1475,4257,2974,0]Ao[4473,13,4474,1025,2998,2974,0]Ao[4473,13,4474,1025,4066,2974,0]Ao[4473,13,4474,1030,1919,2974,0]Ao[4473,13,4474,1030,3192,2974,0]Ao[4473,13,4474,1030,3794,2974,0]Ao[4473,13,4474,2051,3806,2974,0]Ao[4473,13,4474,2051,4163,2974,0]Ao[4473,13,4474,2051,3916,2974,0]Ao[4473,13,4474,3533,4253,2974,0]Ao[4473,13,4474,3533,2990,2974,0]Ao[4473,13,4474,3533,4136,2974,0]Ao[4473,13,4474,3533,1286,2974,0]Ao[4473,13,4474,1021,4263,2974,0]Ao[4473,13,4474,3134,4270,2974,0]Ao[4473,13,4474,3134,4536,2974,0]Ao[4473,13,4474,3134,4552,2974,0]Ao[4473,13,4474,3134,3796,2974,0]Ao[4473,13,4474,1757,4146,2974,0]Ao[4473,13,4474,1757,3948,2974,0]Ao[4473,13,4474,1757,4718,2974,0]Ao[4473,13,4474,1757,3908,2974,0]Ao[4473,13,4474,1757,3788,2974,0]Ao[4473,13,4474,1449,2980,2974,0]Ao[4473,13,4474,1449,4251,2974,0]Ao[4473,13,4474,1449,4426,2974,0]Ao[4473,13,4474,1449,2280,2974,0]Ao[4473,13,4474,2330,4430,2974,0]Ao[4473,13,4474,2330,3919,2974,0]Ao[4473,13,4474,2330,3782,2974,0]Ao[4473,13,4474,2330,3176,2974,0]Ao[4473,13,4474,1819,4062,2974,0]Ao[4473,13,4474,1819,4261,2974,0]Ao[4473,13,4474,1819,4743,2974,0]Ao[4473,13,4474,1819,2293,2974,0]Ao[4473,13,4474,1819,4514,2974,0]Ao[4473,13,4474,1845,4255,2974,0]Ao[4473,13,4474,1845,4045,2974,0]Ao[4473,13,4474,1845,3922,2974,0]Ao[4473,13,4474,1845,3926,2974,0]Al[4473,13,4474,0,4167,2974,0]Al[4473,13,4474,0,4567,2974,0]Ao[4473,13,4474,4075,3161,2974,0]Ao[4473,13,4474,4075,4836,2974,0]Ao[4473,13,4474,4597,4450,2974,0]An[4473,13,4474,4474,458,2974,0]An[4473,13,4474,4474,110,2974,0]Ao[4473,13,4474,4474,2308,2974,0]Ao[4473,13,4474,4474,2317,2974,0]Ao[4473,13,1755,1755,4070,2974,0]Ao[4473,13,4702,4702,4604,2974,0]Ao[4473,13,4702,4702,1283,2974,0]Ao[4473,13,1760,1760,1895,2974,0]An[4473,13,1760,1760,448,2974,0]Ao[4473,13,1760,1760,4803,2974,0]Ao[4473,13,1760,1760,3791,2974,0]Ao[4473,13,1760,1760,4573,2974,0]Ao[4473,13,1835,1835,3187,2974,0]Ao[4473,13,1835,1835,4049,2974,0]Ao[4473,13,1835,1835,4446,2974,0]Ao[4473,13,1835,1835,4448,2974,0]Ao[4473,13,1835,1835,4657,2974,0]Al[4473,13,1835,0,1282,2974,0]Am[4473,13,3528,125,104,2974,0]An[4473,13,3528,125,2973,2974,0]Ao[4473,13,3528,1444,1269,2974,0]Ao[4473,13,3528,1444,3793,2974,0]Ao[4473,13,3528,2378,1898,2974,0]Ao[4473,13,3528,2378,4060,2974,0]Ao[4473,13,3528,3528,4508,2974,0]Ao[4473,13,2023,2023,1284,2974,0]Ao[4473,13,2023,2023,1287,2974,0]Ao[4201,13,4202,4202,3190,2974,0]An[4201,13,4202,368,4168,2974,0]Ao[4201,13,4202,4202,2305,2974,0]Ao[4201,13,4202,4202,2318,2974,0]Ao[4201,13,4202,4202,3927,2974,0]Ao[1002,13,1003,1003,2975,2974,0]Ao[1002,13,1003,1003,4601,2974,0]An[1002,13,1749,1749,118,2974,0]Ao[1002,13,1474,1474,1301,2974,0]Ao[1002,13,1474,1474,3808,2974,0]Ao[1002,13,1474,1474,4259,2974,0]Ao[1002,13,4004,4004,4788,2974,0]Ao[1002,13,4004,4004,4789,2974,0]An[1002,13,1840,1840,119,2974,0]An[3773,13,3774,3774,108,2974,0]Ao[3773,13,3774,3774,2279,2974,0]An[1304,13,1305,1305,117,1914,0]Ao[1304,13,1305,1305,3784,1914,0]Ao[3753,13,3754,3532,3151,1914,0]Ao[3753,13,3754,3532,4663,1914,0]Ao[3753,13,3754,3532,4515,1914,0]Ao[3753,13,3754,2329,2286,1914,0]Ao[3753,13,3754,2329,4046,1914,0]Ao[3753,13,3754,3879,4162,1914,0]Ao[3753,13,3754,3879,2997,1914,0]Ao[3753,13,3754,3879,3174,1914,0]Ao[3753,13,3754,3879,4161,1914,0]Ao[3753,13,3754,3883,3191,1914,0]Ao[3753,13,3754,3883,3952,1914,0]Al[3753,13,3754,0,1528,1914,0]Al[3753,13,3754,0,4660,1914,0]Al[3753,13,3754,0,1514,1914,0]Al[3753,13,3754,0,2319,1914,0]Al[3753,13,3754,0,3157,1914,0]Al[3753,13,3754,0,2994,1914,0]Al[3753,13,3754,0,4256,1914,0]Al[3753,13,3754,0,1918,1914,0]Al[3753,13,3754,0,3912,1914,0]Al[3753,13,3754,0,4252,1914,0]Al[3753,13,3754,0,4269,1914,0]Al[3753,13,3754,0,4535,1914,0]Al[3753,13,3754,0,4551,1914,0]Al[3753,13,3754,0,3795,1914,0]Al[3753,13,3754,0,4145,1914,0]Al[3753,13,3754,0,3946,1914,0]Al[3753,13,3754,0,4717,1914,0]Al[3753,13,3754,0,3906,1914,0]Al[3753,13,3754,0,3787,1914,0]Al[3753,13,3754,0,2979,1914,0]Al[3753,13,3754,0,4250,1914,0]Al[3753,13,3754,0,4425,1914,0]Al[3753,13,3754,0,3918,1914,0]Al[3753,13,3754,0,3921,1914,0]Al[3753,13,3754,0,3925,1914,0]Al[3753,13,3754,0,4566,1914,0]Ak[3753,13,3754,0,457,1914,0]Ak[3753,13,3754,0,109,1914,0]Al[3753,13,3754,0,2307,1914,0]Ak[3753,13,3527,0,103,1914,0]Al[3753,13,3527,0,2972,1914,0]Al[3753,13,3527,0,1268,1914,0]Al[3753,13,3527,0,3792,1914,0]Al[3753,13,3527,0,1897,1914,0]Al[3753,13,3527,0,4059,1914,0]Al[3753,13,1834,0,4656,1914,0]Al[3753,13,1834,0,1281,1914,0]Ao[4473,13,4474,1867,1529,1914,0]Ao[4473,13,4474,1867,4661,1914,0]Ao[4473,13,4474,3022,4516,1914,0]Ao[4473,13,4474,1459,1515,1914,0]Ao[4473,13,4474,1459,3942,1914,0]Ao[4473,13,4474,1459,3168,1914,0]Ao[4473,13,4474,1459,2993,1914,0]Ao[4473,13,4474,1459,4565,1914,0]Ao[4473,13,4474,1459,3147,1914,0]Ao[4473,13,4474,1475,2320,1914,0]Ao[4473,13,4474,1475,3158,1914,0]Ao[4473,13,4474,1475,2995,1914,0]Ao[4473,13,4474,1475,4257,1914,0]Ao[4473,13,4474,1025,2998,1914,0]Ao[4473,13,4474,1025,4066,1914,0]Ao[4473,13,4474,1030,1919,1914,0]Ao[4473,13,4474,1030,3192,1914,0]Ao[4473,13,4474,1030,3794,1914,0]Ao[4473,13,4474,2051,4163,1914,0]Ao[4473,13,4474,2051,3916,1914,0]Ao[4473,13,4474,3533,4253,1914,0]Ao[4473,13,4474,3533,2990,1914,0]Ao[4473,13,4474,3533,4136,1914,0]Ao[4473,13,4474,3134,4270,1914,0]Ao[4473,13,4474,3134,4536,1914,0]Ao[4473,13,4474,3134,4552,1914,0]Ao[4473,13,4474,3134,3796,1914,0]Ao[4473,13,4474,1757,4146,1914,0]Ao[4473,13,4474,1757,3948,1914,0]Ao[4473,13,4474,1757,4718,1914,0]Ao[4473,13,4474,1757,3908,1914,0]Ao[4473,13,4474,1757,3788,1914,0]Ao[4473,13,4474,1449,2980,1914,0]Ao[4473,13,4474,1449,4251,1914,0]Ao[4473,13,4474,1449,4426,1914,0]Ao[4473,13,4474,1449,2280,1914,0]Ao[4473,13,4474,2330,4430,1914,0]Ao[4473,13,4474,2330,3919,1914,0]Ao[4473,13,4474,1819,4261,1914,0]Ao[4473,13,4474,1819,4743,1914,0]Ao[4473,13,4474,1819,2293,1914,0]Ao[4473,13,4474,1819,4514,1914,0]Ao[4473,13,4474,1845,3922,1914,0]Ao[4473,13,4474,1845,3926,1914,0]Al[4473,13,4474,0,4167,1914,0]Al[4473,13,4474,0,4567,1914,0]Ao[4473,13,4474,4075,3161,1914,0]Ao[4473,13,4474,4075,4836,1914,0]An[4473,13,4474,4474,458,1914,0]An[4473,13,4474,4474,110,1914,0]Ao[4473,13,4474,4474,2308,1914,0]Ao[4473,13,1755,1755,4070,1914,0]Ao[4473,13,4702,4702,4604,1914,0]Ao[4473,13,4702,4702,1283,1914,0]Ao[4473,13,1835,1835,4657,1914,0]Al[4473,13,1835,0,1282,1914,0]Am[4473,13,3528,125,104,1914,0]An[4473,13,3528,125,2973,1914,0]Ao[4473,13,3528,1444,1269,1914,0]Ao[4473,13,3528,1444,3793,1914,0]Ao[4473,13,3528,2378,1898,1914,0]Ao[4473,13,3528,2378,4060,1914,0]Ao[4473,13,2023,2023,1287,1914,0]Ao[4201,13,4202,4202,2305,1914,0]Ao[4201,13,4202,4202,2318,1914,0]Ao[4201,13,4202,4202,3927,1914,0]Ao[1002,13,1003,1003,4601,1914,0]Ao[1002,13,1474,1474,4259,1914,0]Al[4473,13,4474,3533,3152,0,0]Ah[3753,2,3754,4474,0,0,0]Ae[4473,2,4474,0,0,0,0]Al[1002,14,1003,1003,2975,0,0]Ao[1304,13,1305,1305,3784,3155,0]Ao[1304,13,1305,1305,3784,3156,0]1Ao[3753,13,3754,3532,4515,3155,0]0Ao[3753,13,3754,3532,4515,3156,0]Ao[3753,13,3754,3879,4162,3156,0]Ao[3753,13,3754,3879,4162,3155,0]0Ao[3753,13,3754,3879,2997,3155,0]Ao[3753,13,3754,3879,2997,3156,0]1Ao[3753,13,3754,3879,3174,3155,0]0Ao[3753,13,3754,3879,3174,3156,0]Ao[3753,13,3754,3879,4161,3156,0]Ao[3753,13,3754,3879,4161,3155,0]0Ao[3753,13,3754,3883,3191,3155,0]0Ao[3753,13,3754,3883,3191,3156,0]Ao[3753,13,3754,3883,3952,3156,0]Ao[3753,13,3754,3883,3952,3155,0]0Al[3753,13,3754,0,1528,3155,0]0Al[3753,13,3754,0,1528,3156,0]Al[3753,13,3754,0,4660,3156,0]Al[3753,13,3754,0,4660,3155,0]0Al[3753,13,3754,0,1263,3156,0]Al[3753,13,3754,0,1263,3155,0]0Al[3753,13,3754,0,1514,3155,0]Al[3753,13,3754,0,1514,3156,0]1Al[3753,13,3754,0,2994,3156,0]Al[3753,13,3754,0,2994,3155,0]0Al[3753,13,3754,0,1918,3155,0]0Al[3753,13,3754,0,1918,3156,0]Al[3753,13,3754,0,3912,3155,0]0Al[3753,13,3754,0,3912,3156,0]Al[3753,13,3754,0,4252,3155,0]0Al[3753,13,3754,0,4252,3156,0]Al[3753,13,3754,0,3795,3155,0]Al[3753,13,3754,0,3795,3156,0]1Al[3753,13,3754,0,3787,3156,0]Al[3753,13,3754,0,3787,3155,0]0Al[3753,13,3754,0,2979,3156,0]Al[3753,13,3754,0,2979,3155,0]0Al[3753,13,3754,0,4250,3155,0]Al[3753,13,3754,0,4250,3156,0]1Al[3753,13,3754,0,4425,3156,0]Al[3753,13,3754,0,4425,3155,0]0Al[3753,13,3754,0,3918,3156,0]Al[3753,13,3754,0,3918,3155,0]0Al[3753,13,3754,0,3921,3155,0]Al[3753,13,3754,0,3921,3156,0]1Al[3753,13,3754,0,3925,3155,0]Al[3753,13,3754,0,3925,3156,0]1Al[3753,13,3754,0,4566,3156,0]Al[3753,13,3754,0,4566,3155,0]0Ak[3753,13,3754,0,457,3155,0]0Ak[3753,13,3754,0,457,3156,0]Ak[3753,13,3754,0,109,3155,0]0Ak[3753,13,3754,0,109,3156,0]Al[3753,13,3754,0,2307,3155,0]Al[3753,13,3754,0,2307,3156,0]1Al[3753,13,1759,0,4802,3156,0]Al[3753,13,1759,0,4802,3155,0]0Ao[4473,13,4474,1867,1529,3155,0]Ao[4473,13,4474,1867,1529,3156,0]1Ao[4473,13,4474,1867,4661,3156,0]Ao[4473,13,4474,1867,4661,3155,0]0Ao[4473,13,4474,3013,1264,3155,0]0Ao[4473,13,4474,3013,1264,3156,0]Ao[4473,13,4474,3022,4516,3155,0]0Ao[4473,13,4474,3022,4516,3156,0]Ao[4473,13,4474,1459,1515,3155,0]0Ao[4473,13,4474,1459,1515,3156,0]Ao[4473,13,4474,1459,3168,3155,0]0Ao[4473,13,4474,1459,3168,3156,0]Ao[4473,13,4474,1459,2993,3156,0]Ao[4473,13,4474,1459,2993,3155,0]0Ao[4473,13,4474,1459,4565,3156,0]Ao[4473,13,4474,1459,4565,3155,0]0Ao[4473,13,4474,1459,3147,3155,0]Ao[4473,13,4474,1459,3147,3156,0]1Ao[4473,13,4474,1475,2995,3155,0]Ao[4473,13,4474,1475,2995,3156,0]1Ao[4473,13,4474,1025,2998,3155,0]0Ao[4473,13,4474,1025,2998,3156,0]Ao[4473,13,4474,1025,4066,3156,0]Ao[4473,13,4474,1025,4066,3155,0]0Ao[4473,13,4474,1030,1919,3155,0]0Ao[4473,13,4474,1030,1919,3156,0]Ao[4473,13,4474,1030,3192,3155,0]0Ao[4473,13,4474,1030,3192,3156,0]Ao[4473,13,4474,1030,3794,3156,0]Ao[4473,13,4474,1030,3794,3155,0]0Ao[4473,13,4474,2051,3806,3155,0]0Ao[4473,13,4474,2051,3806,3156,0]Ao[4473,13,4474,2051,4163,3156,0]Ao[4473,13,4474,2051,4163,3155,0]0Ao[4473,13,4474,2051,3916,3155,0]Ao[4473,13,4474,2051,3916,3156,0]1Ao[4473,13,4474,3533,4253,3156,0]Ao[4473,13,4474,3533,4253,3155,0]0Ao[4473,13,4474,3533,2990,3155,0]0Ao[4473,13,4474,3533,2990,3156,0]Ao[4473,13,4474,3533,4136,3155,0]Ao[4473,13,4474,3533,4136,3156,0]1Ao[4473,13,4474,3134,3796,3156,0]Ao[4473,13,4474,3134,3796,3155,0]0Ao[4473,13,4474,1757,3788,3156,0]Ao[4473,13,4474,1757,3788,3155,0]0Ao[4473,13,4474,1449,2980,3155,0]0Ao[4473,13,4474,1449,2980,3156,0]Ao[4473,13,4474,1449,4251,3155,0]Ao[4473,13,4474,1449,4251,3156,0]1Ao[4473,13,4474,1449,4426,3155,0]Ao[4473,13,4474,1449,4426,3156,0]1Ao[4473,13,4474,2330,4430,3156,0]Ao[4473,13,4474,2330,4430,3155,0]0Ao[4473,13,4474,2330,3919,3156,0]Ao[4473,13,4474,2330,3919,3155,0]0Ao[4473,13,4474,1819,2293,3156,0]Ao[4473,13,4474,1819,2293,3155,0]0Ao[4473,13,4474,1819,4514,3156,0]Ao[4473,13,4474,1819,4514,3155,0]0Ao[4473,13,4474,1845,3922,3155,0]Ao[4473,13,4474,1845,3922,3156,0]1Ao[4473,13,4474,1845,3926,3155,0]0Ao[4473,13,4474,1845,3926,3156,0]Al[4473,13,4474,0,4167,3155,0]0Al[4473,13,4474,0,4167,3156,0]Al[4473,13,4474,0,4567,3155,0]Al[4473,13,4474,0,4567,3156,0]1An[4473,13,4474,4474,458,3155,0]0An[4473,13,4474,4474,458,3156,0]An[4473,13,4474,4474,110,3155,0]0An[4473,13,4474,4474,110,3156,0]Ao[4473,13,4474,4474,2308,3155,0]0Ao[4473,13,4474,4474,2308,3156,0]Ao[4473,13,1755,1755,4070,3155,0]Ao[4473,13,1755,1755,4070,3156,0]1Ao[4473,13,4702,4702,4604,3156,0]Ao[4473,13,4702,4702,4604,3155,0]0Ao[4473,13,4702,4702,1283,3156,0]Ao[4473,13,4702,4702,1283,3155,0]0Ao[4473,13,1760,1760,4803,3155,0]0Ao[4473,13,1760,1760,4803,3156,0]Ai[3753,14,3754,0,4250,0,0]Ai[3753,14,3754,0,4425,0,0]Al[4473,14,4474,1449,4251,0,0]Al[4473,14,4474,1449,4426,0,0]Ai[4473,14,2991,0,1501,0,0]Ai[4473,14,3805,0,1908,0,0]53Ai[3753,13,3754,0,1528,0,0]Al[4473,13,4474,1867,1529,0,0]Ab[3753,3,0,0,0,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,13,4474,1845,4045,0,0]Al[3753,12,3754,2043,4133,0,0]Al[3753,13,3754,2329,2286,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Al[1002,13,1474,1474,4259,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Al[3753,13,3754,3532,3151,0,0]9Ai[4473,14,2991,0,2963,0,0]Ao[3753,13,3754,3883,3178,3142,0]D`[3753,13,3754,3883,3178,3141,0,\"impl-AsyncWrite-for-SendStream\"]D`[3753,13,3754,3883,3178,3142,0,\"impl-AsyncWrite-for-SendStream\"]1Bn[3753,13,3754,3883,3178,0,0,\"impl-SendStream\"]1Al[1002,13,1474,1474,1301,0,0]Ab[3773,3,0,0,0,0,0]Al[3753,13,3754,3879,3172,0,0]0Ak[3753,13,3754,1001,452,0,0]Ai[3753,13,3754,0,3169,0,0]Al[4473,13,4474,1021,3170,0,0]Al[1304,14,1473,1473,4164,0,0]Al[1304,14,1473,1473,1300,0,0]Ah[4473,5,4474,2330,0,0,0]Af[4530,18,4531,0,0,0,0]Ae[1304,6,1305,0,0,0,0]Ac[467,5,468,0,0,0,0]Ah[3753,6,3754,1757,0,0,0]Ah[4473,6,4474,1757,0,0,0]Ah[3753,5,1759,1760,0,0,0]Ae[4473,5,1760,0,0,0,0]Ah[3753,5,3527,1444,0,0,0]Ah[4473,5,3528,1444,0,0,0]Ah[4473,6,4474,1030,0,0,0]Ah[3753,5,3754,3134,0,0,0]Ah[4473,5,4474,3134,0,0,0]Ai[3753,15,3754,0,3787,0,0]Al[4473,15,4474,1757,3788,0,0]Ai[3753,10,3754,1021,0,0,0]Ai[4473,10,4474,1021,0,0,0]??Ah[3753,5,3754,1042,0,0,0]Ah[4473,6,4474,2051,0,0,0]Ae[1304,5,1473,0,0,0,0]Ae[1002,5,1474,0,0,0,0]Al[3753,13,3754,2356,2290,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[1002,13,1003,1003,2975,0,0]Ab[3813,3,0,0,0,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,14,4474,1475,2995,0,0]Ai[3753,13,3754,0,3169,0,0]Al[4473,13,4474,1021,3170,0,0]Ai[3753,14,3754,0,2316,0,0]Al[4473,14,4474,4474,2317,0,0]Al[4201,14,4202,4202,2318,0,0]Ak[1304,14,1305,1305,117,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Al[1002,14,1003,1003,2975,0,0]210Ao[3753,13,3754,3532,4274,3159,0]Ao[3753,13,3754,3532,2298,3159,0]Ao[3753,13,3754,1001,2299,3159,0]Ao[3753,13,3754,3532,3149,3159,0]Ao[3753,13,3754,3532,1900,3159,0]Ao[3753,13,3754,3532,1507,3159,0]Ao[3753,13,3754,3532,2965,3159,0]Ao[3753,13,3754,3532,2276,3159,0]Ao[3753,13,3754,3532,3938,3159,0]Ao[3753,13,3754,3532,3943,3159,0]Ao[3753,13,3754,2329,1491,3159,0]Ao[3753,13,3754,2356,2290,3159,0]Ao[3753,13,3754,2356,4150,3159,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Ak[1304,14,1305,1305,117,0,0]An[3773,13,3774,3774,108,2292,0]Ao[3773,13,3774,3774,2279,2292,0]Ai[3753,13,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[4473,13,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Al[1002,13,1474,1474,1301,0,0]Ai[3753,14,3527,0,4507,0,0]Al[4473,14,3528,3528,4508,0,0]Al[3753,13,3754,0,1528,3166,0]Al[3753,13,3754,0,1514,3166,0]Al[3753,13,3754,0,3795,3166,0]Al[3753,13,3754,0,3921,3166,0]Ak[3753,13,3754,0,457,3166,0]Ak[3753,13,3754,0,109,3166,0]Al[3753,13,3754,0,2307,3166,0]Ao[4473,13,4474,1867,1529,3166,0]Ao[4473,13,4474,3022,4516,3166,0]Ao[4473,13,4474,1459,1515,3166,0]Ao[4473,13,4474,1025,2998,3166,0]Ao[4473,13,4474,1025,4066,3166,0]Ao[4473,13,4474,1030,3192,3166,0]Ao[4473,13,4474,3134,3796,3166,0]Ao[4473,13,4474,2330,4430,3166,0]Ao[4473,13,4474,1819,4514,3166,0]Ao[4473,13,4474,1845,3922,3166,0]An[4473,13,4474,4474,458,3166,0]An[4473,13,4474,4474,110,3166,0]Ao[4473,13,4474,4474,2308,3166,0]Al[3753,13,3754,3532,3151,0,0]Al[4473,13,4474,3533,3152,0,0]Al[3753,13,3754,3879,3172,0,0]01Ai[3753,12,1759,0,2982,0,0]Al[4473,12,1760,1760,2983,0,0]3Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Al[1002,14,1003,1003,2975,0,0]Ai[3753,14,1834,0,4048,0,0]Al[4473,14,1835,1835,4049,0,0]Ai[3753,13,3754,0,3946,0,0]Al[4473,13,4474,1757,3948,0,0]Ai[3753,13,3754,0,3906,0,0]Al[4473,13,4474,1757,3908,0,0]Ak[1304,14,1305,1305,117,0,0]Ai[3753,13,3754,0,4551,0,0]Al[4473,13,4474,3134,4552,0,0]Al[3753,13,3754,3532,3151,0,0]76Ai[3753,13,3754,0,4262,0,0]Al[4473,13,4474,1021,4263,0,0]Al[3753,13,3754,3883,3178,0,0]Al[4473,15,2023,2023,1287,0,0]Ah[3753,5,3754,1757,0,0,0]Ai[3753,10,1759,1760,0,0,0]Ah[4473,5,4474,1757,0,0,0]Af[4473,10,1760,0,0,0,0]Al[3753,15,3754,3879,2997,0,0]Al[3753,15,3754,3883,3191,0,0]Ah[3753,5,3754,2051,0,0,0]Al[4473,15,4474,1025,4066,0,0]Al[4473,15,4474,1030,3192,0,0]Al[4473,15,4474,1030,3794,0,0]Ah[4473,5,4474,2051,0,0,0]Ah[3753,6,3754,2330,0,0,0]Ah[4473,6,4474,2330,0,0,0]Ah[3753,5,3754,1845,0,0,0]Ah[4473,5,4474,1845,0,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]Ah[3753,6,3754,1845,0,0,0]Ah[4473,6,4474,1845,0,0,0]Ae[4201,6,4202,0,0,0,0]43Al[4473,15,4702,4702,1283,0,0]Af[4530,18,4531,0,0,0,0]Al[4473,15,4474,3533,4136,0,0]Al[4473,15,4474,3533,1286,0,0]Ah[3753,5,3754,1484,0,0,0]Ah[4473,5,4474,1484,0,0,0]Al[4473,15,4474,1459,2993,0,0]Ah[3753,5,3754,3532,0,0,0]1Ah[4473,5,4474,1459,0,0,0]1Ah[3753,5,3754,1757,0,0,0]Ai[3753,10,1759,1760,0,0,0]Ah[4473,5,4474,1757,0,0,0]Af[4473,10,1760,0,0,0,0]Ah[3753,6,3754,3883,0,0,0]Ah[3753,5,3754,1485,0,0,0]Am[1304,13,1305,1305,117,435,0]An[1002,13,1003,1003,2975,435,0]Am[1002,13,1749,1749,118,435,0]Am[1002,13,1840,1840,119,435,0]Am[3773,13,3774,3774,108,435,0]Ai[3753,12,3754,0,4708,0,0]Al[4473,12,4474,4075,4709,0,0]Ao[4473,13,4474,4075,4831,4709,0]Ao[4473,13,4474,4075,4836,4709,0]Al[3753,13,3754,3532,3151,0,0]Al[3753,13,3754,2329,2286,0,0]Al[4473,13,4474,1459,3942,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Ae[1304,7,1305,0,0,0,0]Ai[3753,12,1759,0,1906,0,0]Al[3753,13,1834,0,3186,1906,0]Al[4473,12,1760,1760,1907,0,0]Ao[4473,13,1835,1835,3187,1907,0]Ai[3753,14,1277,0,4649,0,0]Ai[3753,14,1277,0,4861,0,0]Ai[4473,14,1278,0,4650,0,0]Ai[4473,14,1278,0,4862,0,0]Ai[3753,12,3754,0,4708,0,0]Al[4473,12,4474,4075,4709,0,0]Ao[4473,13,4474,4075,4831,4709,0]Ao[4473,13,4474,4075,4836,4709,0]Ak[4201,13,4202,368,4168,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,13,4474,2330,2287,0,0]>Ai[3753,12,1759,0,3947,0,0]>Al[3753,13,1834,0,4447,3947,0]>Al[4473,12,1760,1760,3949,0,0]>Ao[4473,13,1835,1835,4448,3949,0]Ao[1002,13,4004,4004,4789,3949,0]An[3773,13,3774,3774,108,3797,0]Ao[3773,13,3774,3774,2279,3797,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Ai[3753,13,3754,0,3946,0,0]Al[4473,13,4474,1757,3948,0,0]Al[3753,13,3754,2043,4133,0,0]0>Al[1002,13,1474,1474,4259,0,0]Ae[1002,2,1003,0,0,0,0]Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,2330,2291,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]43Ai[3753,14,3754,0,2316,0,0]Al[4473,14,4474,4474,2317,0,0]Al[4201,14,4202,4202,2318,0,0]Al[4473,13,4474,2051,1913,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Al[4473,13,4474,4075,4831,0,0]Al[4473,13,4474,4075,4836,0,0]Al[3753,13,3754,3883,3178,0,0]Al[4473,13,4474,2051,3179,0,0]>=Al[1304,14,1473,1473,1300,0,0]Ai[467,14,468,468,3785,0,0]Ai[3753,13,1834,0,4445,0,0]Ai[3753,13,1834,0,4447,0,0]Al[4473,13,1835,1835,4446,0,0]Al[4473,13,1835,1835,4448,0,0]76Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]Ah[3753,5,3754,4279,0,0,0]Ah[4473,5,4474,4279,0,0,0]Ai[3753,15,3754,0,4252,0,0]Ai[3753,15,3754,0,3918,0,0]Al[4473,15,4474,3533,4253,0,0]Al[4473,15,4474,2330,3919,0,0]Ah[4473,6,4474,2330,0,0,0]Ah[4473,5,4474,1845,0,0,0]Ah[3753,5,3754,2329,0,0,0]Al[3753,15,3754,3879,4161,0,0]Ah[3753,5,1834,1835,0,0,0]Ae[4473,5,1835,0,0,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Al[4473,15,4474,1819,4514,0,0]Al[1304,14,1473,1473,1300,0,0]Ao[1002,13,1003,1003,4601,3002,0]Ah[3753,5,3754,1449,0,0,0]Ah[4473,5,4474,1449,0,0,0]Ah[3753,6,3754,3533,0,0,0]Ah[4473,6,4474,3533,0,0,0]Al[4473,15,4474,2330,4044,0,0]Ah[4473,5,4474,1845,0,0,0]Ah[3753,5,3754,1475,0,0,0]Ah[4473,5,4474,1475,0,0,0]Ae[1002,5,1474,0,0,0,0]Ah[4473,6,4474,1819,0,0,0]Ah[3753,5,3754,1021,0,0,0]Ah[4473,5,4474,1021,0,0,0]Ah[3753,5,3754,1484,0,0,0]Ah[4473,5,4474,1484,0,0,0]Ah[3753,5,3754,3134,0,0,0]Ah[4473,5,4474,3134,0,0,0]Ai[3753,15,3754,0,4252,0,0]Al[4473,15,4474,3533,4253,0,0]Ah[3753,5,3754,3532,0,0,0]Al[3753,15,3754,3879,2997,0,0]Al[3753,15,3754,3879,3174,0,0]Al[3753,15,3754,3883,3191,0,0]Al[3753,15,3754,3883,3952,0,0]An[1304,13,1305,1305,117,4043,0]Ao[1304,13,1305,1305,3784,4043,0]Ao[3753,13,3754,3532,3151,4043,0]Ao[3753,13,3754,3532,4663,4043,0]Ao[3753,13,3754,3532,4515,4043,0]Ao[3753,13,3754,2329,2286,4043,0]Ao[3753,13,3754,2329,4046,4043,0]Ao[3753,13,3754,3879,4162,4043,0]Ao[3753,13,3754,3879,2997,4043,0]Ao[3753,13,3754,3879,3174,4043,0]Ao[3753,13,3754,3879,4161,4043,0]Ao[3753,13,3754,3883,3191,4043,0]Ao[3753,13,3754,3883,3952,4043,0]Al[3753,13,3754,0,1528,4043,0]Al[3753,13,3754,0,4660,4043,0]Al[3753,13,3754,0,1514,4043,0]Al[3753,13,3754,0,2319,4043,0]Al[3753,13,3754,0,3157,4043,0]Al[3753,13,3754,0,2994,4043,0]Al[3753,13,3754,0,4256,4043,0]Al[3753,13,3754,0,1918,4043,0]Al[3753,13,3754,0,3912,4043,0]Al[3753,13,3754,0,4252,4043,0]Al[3753,13,3754,0,4269,4043,0]Al[3753,13,3754,0,4535,4043,0]Al[3753,13,3754,0,4551,4043,0]Al[3753,13,3754,0,3795,4043,0]Al[3753,13,3754,0,4145,4043,0]Al[3753,13,3754,0,3946,4043,0]Al[3753,13,3754,0,4717,4043,0]Al[3753,13,3754,0,3906,4043,0]Al[3753,13,3754,0,3787,4043,0]Al[3753,13,3754,0,2979,4043,0]Al[3753,13,3754,0,4250,4043,0]Al[3753,13,3754,0,4425,4043,0]Al[3753,13,3754,0,3918,4043,0]Al[3753,13,3754,0,3921,4043,0]Al[3753,13,3754,0,3925,4043,0]Al[3753,13,3754,0,4566,4043,0]Ak[3753,13,3754,0,457,4043,0]Ak[3753,13,3754,0,109,4043,0]Al[3753,13,3754,0,2307,4043,0]Ak[3753,13,3527,0,103,4043,0]Al[3753,13,3527,0,2972,4043,0]Al[3753,13,3527,0,1268,4043,0]Al[3753,13,3527,0,3792,4043,0]Al[3753,13,3527,0,1897,4043,0]Al[3753,13,3527,0,4059,4043,0]Al[3753,13,1834,0,4656,4043,0]Al[3753,13,1834,0,1281,4043,0]Ao[4473,13,4474,1867,1529,4043,0]Ao[4473,13,4474,1867,4661,4043,0]Ao[4473,13,4474,3022,4516,4043,0]Ao[4473,13,4474,1459,1515,4043,0]Ao[4473,13,4474,1459,3942,4043,0]Ao[4473,13,4474,1459,3168,4043,0]Ao[4473,13,4474,1459,2993,4043,0]Ao[4473,13,4474,1459,4565,4043,0]Ao[4473,13,4474,1459,3147,4043,0]Ao[4473,13,4474,1475,2320,4043,0]Ao[4473,13,4474,1475,3158,4043,0]Ao[4473,13,4474,1475,2995,4043,0]Ao[4473,13,4474,1475,4257,4043,0]Ao[4473,13,4474,1025,2998,4043,0]Ao[4473,13,4474,1025,4066,4043,0]Ao[4473,13,4474,1030,1919,4043,0]Ao[4473,13,4474,1030,3192,4043,0]Ao[4473,13,4474,1030,3794,4043,0]Ao[4473,13,4474,2051,4163,4043,0]Ao[4473,13,4474,2051,3916,4043,0]Ao[4473,13,4474,3533,4253,4043,0]Ao[4473,13,4474,3533,2990,4043,0]Ao[4473,13,4474,3533,4136,4043,0]Ao[4473,13,4474,3134,4270,4043,0]Ao[4473,13,4474,3134,4536,4043,0]Ao[4473,13,4474,3134,4552,4043,0]Ao[4473,13,4474,3134,3796,4043,0]Ao[4473,13,4474,1757,4146,4043,0]Ao[4473,13,4474,1757,3948,4043,0]Ao[4473,13,4474,1757,4718,4043,0]Ao[4473,13,4474,1757,3908,4043,0]Ao[4473,13,4474,1757,3788,4043,0]Ao[4473,13,4474,1449,2980,4043,0]Ao[4473,13,4474,1449,4251,4043,0]Ao[4473,13,4474,1449,4426,4043,0]Ao[4473,13,4474,1449,2280,4043,0]Ao[4473,13,4474,2330,4430,4043,0]Ao[4473,13,4474,2330,3919,4043,0]Ao[4473,13,4474,1819,4261,4043,0]Ao[4473,13,4474,1819,4743,4043,0]Ao[4473,13,4474,1819,2293,4043,0]Ao[4473,13,4474,1819,4514,4043,0]Ao[4473,13,4474,1845,3922,4043,0]Ao[4473,13,4474,1845,3926,4043,0]Al[4473,13,4474,0,4167,4043,0]Al[4473,13,4474,0,4567,4043,0]Ao[4473,13,4474,4075,3161,4043,0]Ao[4473,13,4474,4075,4836,4043,0]An[4473,13,4474,4474,458,4043,0]An[4473,13,4474,4474,110,4043,0]Ao[4473,13,4474,4474,2308,4043,0]Ao[4473,13,1755,1755,4070,4043,0]Ao[4473,13,4702,4702,4604,4043,0]Ao[4473,13,4702,4702,1283,4043,0]Ao[4473,13,1835,1835,4657,4043,0]Al[4473,13,1835,0,1282,4043,0]Am[4473,13,3528,125,104,4043,0]An[4473,13,3528,125,2973,4043,0]Ao[4473,13,3528,1444,1269,4043,0]Ao[4473,13,3528,1444,3793,4043,0]Ao[4473,13,3528,2378,1898,4043,0]Ao[4473,13,3528,2378,4060,4043,0]Ao[4473,13,2023,2023,1287,4043,0]Ao[4201,13,4202,4202,2305,4043,0]Ao[4201,13,4202,4202,2318,4043,0]Ao[4201,13,4202,4202,3927,4043,0]Ao[1002,13,1003,1003,4601,4043,0]Ao[1002,13,1474,1474,4259,4043,0]Ah[3753,7,3754,2043,0,0,0]Ai[3753,13,3754,0,4145,0,0]Al[4473,13,4474,1757,4146,0,0]Ai[3753,12,1759,0,2987,0,0]Al[4473,12,1760,1760,2988,0,0]Al[4201,14,4202,4202,2305,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]10Ae[1002,7,1003,0,0,0,0]Al[3753,13,3754,2329,2286,0,0]Ae[4078,7,4079,0,0,0,0]Ae[1304,7,1305,0,0,0,0]Al[4473,13,4474,3533,3152,0,0]Al[3753,13,3754,3532,3151,0,0]1Al[4473,13,4474,2051,4163,0,0]Al[1304,13,1473,1473,1300,0,0]Al[1304,14,1473,1473,3807,0,0]Al[3753,12,3754,2043,1905,0,0]Ao[3753,13,3754,1485,3953,1905,0]Ao[3753,13,3754,1042,3804,1905,0]Ai[3753,12,1759,0,1906,0,0]Al[3753,13,1834,0,3186,1906,0]Al[4473,12,1760,1760,1907,0,0]Ao[4473,13,1835,1835,3187,1907,0]Ah[3753,5,3754,1449,0,0,0]Ah[4473,5,4474,1449,0,0,0]Ai[3753,15,3754,0,4252,0,0]Al[4473,15,4474,3533,4253,0,0]Ah[4473,5,4474,2330,0,0,0]Al[4473,15,4474,3533,1286,0,0]Ai[3753,15,3754,0,3918,0,0]Al[4473,15,4474,2330,3919,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]32Al[4473,15,4474,3533,2990,0,0]21Ah[3753,5,1834,1835,0,0,0]Ae[4473,5,1835,0,0,0,0]10Ah[3753,5,3754,4597,0,0,0]Ah[4473,5,4474,4597,0,0,0]Al[4473,15,2023,2023,1284,0,0]5Al[4473,15,4474,1819,4261,0,0]Ai[3753,12,3754,0,2312,0,0]Al[3753,13,3754,0,4037,2312,0]Al[3753,13,3754,0,3934,2312,0]Al[4473,12,4474,1484,2313,0,0]Ao[4473,13,4474,4279,4038,2313,0]Ao[4473,13,4474,1484,3935,2313,0]Ae[3813,7,3814,0,0,0,0]Al[3753,13,3754,3532,3151,0,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]32An[1304,13,1305,1305,117,4147,0]Ao[1002,13,1003,1003,2975,4147,0]An[1002,13,1749,1749,118,4147,0]An[1002,13,1840,1840,119,4147,0]An[3773,13,3774,3774,108,4147,0]Ao[3773,13,3774,3774,2279,4147,0]Ab[4473,3,0,0,0,0,0]Ak[4201,13,4202,368,4168,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0];:Al[3753,13,3754,2329,2286,0,0]Al[4473,13,4474,2330,2287,0,0]?>32Ae[4078,7,4079,0,0,0,0]5Al[1002,14,1003,1003,2975,0,0]60Ae[1304,7,1305,0,0,0,0]Ai[3753,13,3754,0,3946,0,0]Ai[3753,13,3754,0,3906,0,0]Al[4473,13,4474,1757,3948,0,0]Al[4473,13,4474,1757,3908,0,0]Ai[3753,14,3754,0,3946,0,0]Al[4473,14,4474,1757,3948,0,0]53Al[3753,13,3754,3883,3178,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Ai[3753,15,3754,0,4252,0,0]Al[4473,15,4474,3533,4253,0,0]Ai[3753,10,3527,3528,0,0,0]Af[4473,10,3528,0,0,0,0]Ah[3753,5,3527,3528,0,0,0]Ae[4473,5,3528,0,0,0,0]Ai[3753,10,1759,1760,0,0,0]Af[4473,10,1760,0,0,0,0]Ai[3753,15,3754,0,3918,0,0]Al[4473,15,4474,2330,3919,0,0]Ah[4473,6,4474,1819,0,0,0]Ah[3753,6,3754,3532,0,0,0]Ah[4473,6,4474,3022,0,0,0]Al[3753,15,3754,3532,4515,0,0]Al[4473,15,4474,3022,4516,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Ai[3753,14,3527,0,4507,0,0]Al[4473,14,3528,3528,4508,0,0]Al[3753,13,3754,3532,3151,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]10Al[4473,13,4474,3022,2977,0,0]Al[3753,13,3754,2329,2286,0,0]Al[4473,13,4474,2330,2287,0,0]Ab[4530,3,0,0,0,0,0]Ao[1002,13,1003,1003,4601,3002,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Ah[3753,5,3754,3134,0,0,0]Ah[4473,5,4474,3134,0,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]54Ai[4473,10,4474,1819,0,0,0]Al[4473,15,4474,3533,1286,0,0]7600Al[3753,15,3754,3879,2997,0,0]Al[4473,15,4474,1025,4066,0,0]5476Al[4473,15,2023,2023,1284,0,0]Ae[1002,9,1003,0,0,0,0];:;:;:Ah[4473,6,4474,1459,0,0,0]Ae[3753,5,3754,0,0,0,0]Ae[4473,5,4474,0,0,0,0]Ai[3753,15,3754,0,3918,0,0]Ah[3753,5,1759,1760,0,0,0]Al[4473,15,4474,2330,3919,0,0]Al[4473,15,4474,1819,4514,0,0]Ae[4473,5,1760,0,0,0,0]An[1304,13,1305,1305,117,4137,0]Ao[1002,13,1003,1003,2975,4137,0]An[1002,13,1749,1749,118,4137,0]An[1002,13,1840,1840,119,4137,0]An[3773,13,3774,3774,108,4137,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Ai[3753,13,3754,0,1528,0,0]Al[4473,13,4474,1867,1529,0,0]Al[3753,14,3754,2329,4046,0,0]Ai[3753,13,3754,0,4145,0,0]Al[4473,13,4474,1757,4146,0,0]Ai[3753,13,3754,0,4037,0,0]Al[4473,13,4474,4279,4038,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]6Al[4473,13,4474,3533,3152,0,0]Al[3753,13,3754,3532,3151,0,0]0176Ao[3813,13,3814,3814,4744,4563,0]Ai[3753,13,3754,0,4262,0,0]Al[4473,13,4474,1021,4263,0,0]Ae[1002,6,1003,0,0,0,0]Al[4473,15,4474,3533,2990,0,0]Ae[4473,5,4702,0,0,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]Al[4473,15,2023,2023,1284,0,0]Al[3753,14,3754,2329,4046,0,0]Ak[3753,13,3754,0,457,2966,0]Ak[3753,13,3754,0,109,2966,0]Al[3753,13,3754,0,2307,2966,0]An[4473,13,4474,4474,458,2966,0]An[4473,13,4474,4474,110,2966,0]Ao[4473,13,4474,4474,2308,2966,0]Ao[3773,13,3774,3774,2279,3182,0]Ai[3753,13,3754,0,4551,0,0]Al[4473,13,4474,3134,4552,0,0]Ai[3753,12,1759,0,1906,0,0]Al[3753,13,1834,0,3186,1906,0]Al[4473,12,1760,1760,1907,0,0]Ao[4473,13,1835,1835,3187,1907,0]Al[3753,13,3754,3532,3151,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Ai[3753,14,3754,0,2994,0,0]Al[4473,14,4474,1475,2995,0,0]Ai[3753,12,3527,0,3153,0,0]Ak[3753,13,3527,0,103,3153,0]Al[3753,13,3527,0,1268,3153,0]Al[3753,13,3527,0,1897,3153,0]Al[4473,12,3528,3528,3154,0,0]Am[4473,13,3528,125,104,3154,0]Ao[4473,13,3528,1444,1269,3154,0]Ao[4473,13,3528,2378,1898,3154,0]Al[3753,14,3754,2329,4046,0,0]Al[4473,13,4474,3533,3152,0,0]Al[4473,13,4474,2051,1913,0,0]Ae[1304,7,1305,0,0,0,0]==Ak[3753,13,3754,1001,452,0,0]Ak[4201,13,4202,368,4168,0,0]032Ai[3753,12,1759,0,1906,0,0]Al[3753,13,1834,0,3186,1906,0]Ae[4473,2,4474,0,0,0,0]Al[4473,12,1760,1760,1907,0,0]Ao[4473,13,1835,1835,3187,1907,0]Ai[3753,10,3754,4075,0,0,0]Ai[4473,10,4474,4075,0,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]Ai[3753,15,3754,0,3918,0,0]Al[4473,15,4474,2330,3919,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Ah[3753,5,3754,1757,0,0,0]Ah[4473,5,4474,1757,0,0,0]Ai[3753,12,1759,0,2987,0,0]Al[4473,12,1760,1760,2988,0,0]Al[4473,13,4474,2330,2287,0,0]Al[4473,13,4474,3533,3152,0,0]Ai[3753,13,3527,0,4059,0,0]Al[4473,13,3528,2378,4060,0,0]Al[3753,13,3754,2043,3000,0,0]0Al[3753,13,3754,3532,3151,0,0]4Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Al[1002,14,1003,1003,2975,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Al[4473,15,2023,2023,1284,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]10Al[4473,15,4474,1459,4565,0,0]Al[4473,15,4474,3533,2990,0,0]Ah[4473,5,4474,1819,0,0,0]Ae[3813,5,3814,0,0,0,0]Al[1002,13,1003,1003,2975,0,0]?Ai[3753,12,1759,0,1906,0,0]Al[3753,13,1834,0,3186,1906,0]Al[4473,12,1760,1760,1907,0,0]Ao[4473,13,1835,1835,3187,1907,0]Al[4473,13,4474,3533,3152,0,0]Al[3753,13,3754,3532,3151,0,0]Ak[3753,13,3754,1001,452,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]Ao[3813,13,3814,3814,4744,4563,0]0Ai[3753,13,3754,0,3906,0,1]Al[4473,13,4474,1757,3908,0,1]Ai[3753,13,3754,0,3906,0,0]Al[4473,13,4474,1757,3908,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]10Ai[3753,13,3754,0,4535,0,0]Al[4473,13,4474,3134,4536,0,0]Am[1304,13,1305,1305,117,435,0]An[1002,13,1003,1003,2975,435,0]Am[1002,13,1749,1749,118,435,0]Am[1002,13,1840,1840,119,435,0]Am[3773,13,3774,3774,108,435,0]Al[4473,13,4474,3533,3152,0,0]0Ak[3753,13,3754,1001,452,0,0]An[1304,13,1305,1305,117,4147,0]Ao[1002,13,1003,1003,2975,4147,0]An[1002,13,1749,1749,118,4147,0]An[1002,13,1840,1840,119,4147,0]An[3773,13,3774,3774,108,4147,0]Ao[3773,13,3774,3774,2279,4147,0]Ai[3753,13,3754,0,3946,0,0]Al[4473,13,4474,1757,3948,0,0]Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]Ae[1002,5,4004,0,0,0,0]0Ai[3753,13,3754,0,4145,0,0]Al[4473,13,4474,1757,4146,0,0]Ai[3753,13,3754,0,3906,0,0]Al[4473,13,4474,1757,3908,0,0]Al[3753,13,3754,2329,2286,0,0]Al[3753,13,3754,2356,2290,0,0]Al[4473,13,4474,2330,2291,0,0]Al[4473,13,4474,3533,3152,0,0]Ao[3813,13,3814,3814,4744,4563,0];:Ah[3753,5,1759,1760,0,0,0]Ae[4473,5,1760,0,0,0,0]Ai[3753,15,1834,0,1281,0,0]Ai[4473,15,1835,0,1282,0,0]?>?>Al[3753,13,3754,3532,3151,0,0]6Ai[3753,13,3754,0,4269,0,0]Al[4473,13,4474,3134,4270,0,0]Ae[3813,7,3814,0,0,0,0]Ae[4078,7,4079,0,0,0,0]Ai[3753,14,3754,0,3157,0,0]Al[4473,14,4474,1475,3158,0,0]?=Ai[3753,19,3754,0,4566,0,0]Ai[4473,19,4474,0,4567,0,0]2=<==<>=>=>=<;<;Al[4473,14,4702,4702,4604,0,0]?>Ai[4473,18,4474,3134,0,0,0]") \ No newline at end of file diff --git a/pr/234/docs/search.index/function/6f4f4cff046f.js b/pr/234/docs/search.index/function/6f4f4cff046f.js new file mode 100644 index 000000000..efd0b6ae5 --- /dev/null +++ b/pr/234/docs/search.index/function/6f4f4cff046f.js @@ -0,0 +1 @@ +rd_("Bf[\"{{{AGNj{AMI`}}{AGNj{AMI`}}}CJf}\",[]]Bf[\"{{{AGNj{BCDf}}{AGNj{BCDf}}}CJf}\",[]]Bf[\"{{{AGNj{B@Hd}}{AGNj{B@Hd}}}CJf}\",[]]Bf[\"{{{AGNj{AGFj}}{AGNj{AGFj}}}CJf}\",[]]Bf[\"{{{AGNj{AHLl}}{AGNj{AHLl}}}CJf}\",[]]Bf[\"{{{AGNj{B@Hb}}{AGNj{B@Hb}}}CJf}\",[]]Bf[\"{{{AGNj{AHNn}}{AGNj{AHNn}}}CJf}\",[]]Bf[\"{{{AGNj{ANN`}}{AGNj{ANN`}}}CJf}\",[]]Bd[\"{{{AGNj{KO`}}{AGNj{KO`}}}CJf}\",[]]Bf[\"{{{AGNj{BDFh}}{AGNj{BDFh}}}CJf}\",[]]Bd[\"{{{AGNj{IMn}}{AGNj{IMn}}}CJf}\",[]]Bd[\"{{{AGNj{KMd}}{AGNj{KMd}}}CJf}\",[]]Bf[\"{{{AGNj{AGFd}}{AGNj{AGFd}}}CJf}\",[]]Bd[\"{{{AGNj{NOl}}{AGNj{NOl}}}CJf}\",[]]Bf[\"{{{AGNj{ANI`}}{AGNj{ANI`}}}CJf}\",[]]Bf[\"{{{AGNj{BACh}}{AGNj{BACh}}}CJf}\",[]]Bf[\"{{{AGNj{AMJf}}{AGNj{AMJf}}}CJf}\",[]]Bf[\"{{{AGNj{AMIf}}{AGNj{AMIf}}}CJf}\",[]]Bf[\"{{{AGNj{AGDf}}{AGNj{AGDf}}}CJf}\",[]]Bf[\"{{{AGNj{BACd}}{AGNj{BACd}}}CJf}\",[]]Bf[\"{{{AGNj{BBIb}}{AGNj{BBIb}}}CJf}\",[]]Bf[\"{{{AGNj{ANIl}}{AGNj{ANIl}}}CJf}\",[]]Bf[\"{{{AGNj{ANJb}}{AGNj{ANJb}}}CJf}\",[]]Bf[\"{{{AGNj{ANJj}}{AGNj{ANJj}}}CJf}\",[]]Bf[\"{{{AGNj{BCJl}}{AGNj{BCJl}}}CJf}\",[]]Bd[\"{{{AGNj{CIb}}{AGNj{CIb}}}CJf}\",[]]Bb[\"{{{AGNj{Mj}}{AGNj{Mj}}}CJf}\",[]]Bf[\"{{{AGNj{AB@f}}{AGNj{AB@f}}}CJf}\",[]]Bf[\"{{{AGNj{BEHd}}{AGNj{BEHd}}}CJf}\",[]]Bd[\"{{{AGNj{J@b}}{AGNj{J@b}}}CJf}\",[]]Bd[\"{{{AGNj{KOb}}{AGNj{KOb}}}CJf}\",[]]Bf[\"{{{AGNj{BDFj}}{AGNj{BDFj}}}CJf}\",[]]Bd[\"{{{AGNj{IN`}}{AGNj{IN`}}}CJf}\",[]]Bf[\"{{{AGNj{BCDh}}{AGNj{BCDh}}}CJf}\",[]]Bd[\"{{{AGNj{KMf}}{AGNj{KMf}}}CJf}\",[]]Bf[\"{{{AGNj{AHL`}}{AGNj{AHL`}}}CJf}\",[]]Bf[\"{{{AGNj{AGFb}}{AGNj{AGFb}}}CJf}\",[]]Bf[\"{{{AGNj{BCJj}}{AGNj{BCJj}}}CJf}\",[]]Bf[\"{{{AGNj{AHIf}}{AGNj{AHIf}}}CJf}\",[]]Bf[\"{{{AGNj{AGFf}}{AGNj{AGFf}}}CJf}\",[]]Bf[\"{{{AGNj{AGFl}}{AGNj{AGFl}}}CJf}\",[]]Bf[\"{{{AGNj{AOLd}}{AGNj{AOLd}}}CJf}\",[]]Bd[\"{{{AGNj{NOn}}{AGNj{NOn}}}CJf}\",[]]Bf[\"{{{AGNj{AHO`}}{AGNj{AHO`}}}CJf}\",[]]Bf[\"{{{AGNj{AMJd}}{AGNj{AMJd}}}CJf}\",[]]Bf[\"{{{AGNj{AMKl}}{AGNj{AMKl}}}CJf}\",[]]Bf[\"{{{AGNj{B@Hf}}{AGNj{B@Hf}}}CJf}\",[]]Bf[\"{{{AGNj{ANIh}}{AGNj{ANIh}}}CJf}\",[]]Bf[\"{{{AGNj{BACj}}{AGNj{BACj}}}CJf}\",[]]Bf[\"{{{AGNj{AGEl}}{AGNj{AGEl}}}CJf}\",[]]Bf[\"{{{AGNj{B@E`}}{AGNj{B@E`}}}CJf}\",[]]Bf[\"{{{AGNj{AMJh}}{AGNj{AMJh}}}CJf}\",[]]Bf[\"{{{AGNj{AMIh}}{AGNj{AMIh}}}CJf}\",[]]Bf[\"{{{AGNj{AGDh}}{AGNj{AGDh}}}CJf}\",[]]Bf[\"{{{AGNj{BACf}}{AGNj{BACf}}}CJf}\",[]]Bf[\"{{{AGNj{BBId}}{AGNj{BBId}}}CJf}\",[]]Bf[\"{{{AGNj{BBIl}}{AGNj{BBIl}}}CJf}\",[]]Bf[\"{{{AGNj{ANIn}}{AGNj{ANIn}}}CJf}\",[]]Bf[\"{{{AGNj{AANj}}{AGNj{AANj}}}CJf}\",[]]Bf[\"{{{AGNj{BCDd}}{AGNj{BCDd}}}CJf}\",[]]Bf[\"{{{AGNj{ANJd}}{AGNj{ANJd}}}CJf}\",[]]Bf[\"{{{AGNj{ANJl}}{AGNj{ANJl}}}CJf}\",[]]Bf[\"{{{AGNj{B@Hn}}{AGNj{B@Hn}}}CJf}\",[]]Bf[\"{{{AGNj{BCJn}}{AGNj{BCJn}}}CJf}\",[]]Bd[\"{{{AGNj{CId}}{AGNj{CId}}}CJf}\",[]]Bb[\"{{{AGNj{Ml}}{AGNj{Ml}}}CJf}\",[]]Bf[\"{{{AGNj{AB@h}}{AGNj{AB@h}}}CJf}\",[]]Bf[\"{{{AGNj{AOLl}}{AGNj{AOLl}}}CJf}\",[]]Bf[\"{{{AGNj{BCOh}}{AGNj{BCOh}}}CJf}\",[]]Bd[\"{{{AGNj{J@f}}{AGNj{J@f}}}CJf}\",[]]Bf[\"{{{AGNj{BEHf}}{AGNj{BEHf}}}CJf}\",[]]Bd[\"{{{AGNj{J@d}}{AGNj{J@d}}}CJf}\",[]]Bf[\"{{{AGNj{ANJn}}{AGNj{ANJn}}}CJf}\",[]]Ai[\"{{{AGNj{CHh}}}KMd}\",[]]Ak[\"{{{AGNj{AHLh}}}AB@f}\",[]]Ak[\"{{{AGNj{AHMd}}}AB@f}\",[]]o[\"{KLhKMf}\",[]]o[\"{KKjKMf}\",[]]A`[\"{AGBfKMf}\",[]]A`[\"{AOJlKMf}\",[]]A`[\"{ANLfKMf}\",[]]A`[\"{ANL`KMf}\",[]]Aa[\"{AAOjAB@h}\",[]]Aa[\"{ABBdAB@h}\",[]]Aa[\"{AAN`AB@h}\",[]]A`[\"{NNhAB@h}\",[]]n[\"{{}NN`}\",[]]A`[\"{CHbAANj}\",[]]Be[\"{{{AGNj{KO`}}{AGNj{KO`}}}AAOh}\",[]]Be[\"{{{AGNj{KMd}}{AGNj{KMd}}}AAOh}\",[]]Bg[\"{{{AGNj{AMJf}}{AGNj{AMJf}}}AAOh}\",[]]Bg[\"{{{AGNj{ANJb}}{AGNj{ANJb}}}AAOh}\",[]]Be[\"{{{AGNj{CIb}}{AGNj{CIb}}}AAOh}\",[]]Bc[\"{{{AGNj{Mj}}{AGNj{Mj}}}AAOh}\",[]]Bg[\"{{{AGNj{AB@f}}{AGNj{AB@f}}}AAOh}\",[]]Be[\"{{{AGNj{KOb}}{AGNj{KOb}}}AAOh}\",[]]Bg[\"{{{AGNj{BCDh}}{AGNj{BCDh}}}AAOh}\",[]]Be[\"{{{AGNj{KMf}}{AGNj{KMf}}}AAOh}\",[]]Bg[\"{{{AGNj{AGFl}}{AGNj{AGFl}}}AAOh}\",[]]Bg[\"{{{AGNj{AOLd}}{AGNj{AOLd}}}AAOh}\",[]]Bg[\"{{{AGNj{AHO`}}{AGNj{AHO`}}}AAOh}\",[]]Bg[\"{{{AGNj{AMJh}}{AGNj{AMJh}}}AAOh}\",[]]Bg[\"{{{AGNj{BBIl}}{AGNj{BBIl}}}AAOh}\",[]]Bg[\"{{{AGNj{BCDd}}{AGNj{BCDd}}}AAOh}\",[]]Bg[\"{{{AGNj{ANJd}}{AGNj{ANJd}}}AAOh}\",[]]Be[\"{{{AGNj{CId}}{AGNj{CId}}}AAOh}\",[]]Bc[\"{{{AGNj{Ml}}{AGNj{Ml}}}AAOh}\",[]]Bg[\"{{{AGNj{AB@h}}{AGNj{AB@h}}}AAOh}\",[]]o[\"{AB@fMj}\",[]]o[\"{AB@hMl}\",[]]n[\"{KLjMl}\",[]]o[\"{AGCbMl}\",[]]A`[\"{ABAhKM`}\",[]]A`[\"{ABAjKM`}\",[]]A`[\"{AB@bKM`}\",[]]A`[\"{ABAlKM`}\",[]]Bg[\"{{{AGNj{Nj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMI`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@Hh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BEA`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHIj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHIn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BDFn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCDf}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AAMl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AOIl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AANd}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHLn}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{B@Fl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{CHh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHLh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@Hd}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AGFj}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AHLl}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{B@Hb}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ANNb}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMKh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHMd}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHNn}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ANN`}}{AGNj{BOdAGDd}}}KMh}\",[]]0Cd[\"{{{AGNj{KO`}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BDFh}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Cd[\"{{{AGNj{IMn}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cd[\"{{{AGNj{KMd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{ABAn}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AHJj}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AGFd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BAD`}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cd[\"{{{AGNj{NOl}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{ANI`}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BACh}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BADl}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BAEj}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BCFn}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BCHn}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AMJf}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{B@Fb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{ANMd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BDMj}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{ANHd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AMIf}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{AGDf}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BACd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BBIb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{ANIl}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{ANJb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{ANJj}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BCJl}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{BBLb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cd[\"{{{AGNj{CIb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cc[\"{{{AGNj{Mj}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{AB@f}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Ce[\"{{{AGNj{ABAh}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cc[\"{{{AGNj{Ln}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AGCh}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cd[\"{{{AGNj{INh}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AMJ`}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Cd[\"{{{AGNj{NMb}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AOKf}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BCCf}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BEHd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{AMIl}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BCKd}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]Ce[\"{{{AGNj{BDF`}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Cd[\"{{{AGNj{J@b}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Bi[\"{{{AGNj{AHJ`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AAMn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AANf}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{KOb}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BDFj}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bh[\"{{{AGNj{IN`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCDh}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bh[\"{{{AGNj{KMf}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ANLl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHL`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AGFb}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCJj}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AHIf}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ABB`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHJl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AGFf}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BADb}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AGFl}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AOLd}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bh[\"{{{AGNj{NOn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHO`}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AMJd}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AMKl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@Hf}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANIh}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BACj}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AGEl}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{B@E`}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bh[\"{{{AGNj{J@l}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BADn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BAEl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCG`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCI`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMJh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@Fd}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANMh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BDMl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANHh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMIh}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AGDh}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BACf}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BBId}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AAM`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BBIl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANIn}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AMHl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AHM`}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AOKl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BADj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BE@n}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AANj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCDd}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BACn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AOIj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANJd}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ANJl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@Hn}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BCJn}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AHKb}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BELh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BBLd}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{CId}}{AGNj{BOdAGDd}}}KMh}\",[]]Bg[\"{{{AGNj{Ml}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{AB@h}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{ABAj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AOLl}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BCOh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{J@f}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bi[\"{{{AGNj{BEHf}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMIn}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCKj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BDFb}}{AGNj{BOdAGDd}}}KMh}\",[]]0Cd[\"{{{AGNj{J@d}}{AGNj{BOdAGDd}}}{{KMj{HCjIOb}}}}\",[]]0Bg[\"{{{AGNj{M`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AGCj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{INj}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AMJb}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{NMd}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AOKh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{BCCh}}{AGNj{BOdAGDd}}}KMh}\",[]]Bh[\"{{{AGNj{J@h}}{AGNj{BOdAGDd}}}KMh}\",[]]0Bh[\"{{{AGNj{J@n}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{B@I`}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{AB@b}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ABAl}}{AGNj{BOdAGDd}}}KMh}\",[]]Bi[\"{{{AGNj{ANJn}}{AGNj{BOdAGDd}}}KMh}\",[]]Ak[\"{{{AGNj{ANLl}}}AAMh}\",[]]A`[\"{ABAnCFb}\",[]]A`[\"{ABB`CFb}\",[]]Aj[\"{{{AGNj{AOKl}}}KIn}\",[]]A`[\"{BE@nCFb}\",[]]o[\"{CHbCFb}\",[]]A`[\"{AB@bKIn}\",[]]Ce[\"{{{AGNj{BOdBDMj}}{Ll{ABA`}}}{{AGNj{BOdBDMj}}}}\",[]]Ce[\"{{{AGNj{BOdBDMl}}{Ll{ABAb}}}{{AGNj{BOdBDMl}}}}\",[]]8Af[\"{{AAMhCFb}B@Hh}\",[]]Ag[\"{{}{{Ll{BEA`}}}}\",[]]Ae[\"{{KInCF`}AOHj}\",[]]Cd[\"{{B@Fb{KM`{ANMd}}AGGb{Ll{NNb}}}{{KMl{AAMl}}}}\",[]]Ah[\"{{{Md{JBn}}}AHLb}\",[]]A`[\"{NMjBADl}\",[]]Ah[\"{{{Ll{NLd}}}B@Fb}\",[]]Bc[\"{{{Ll{ANMf}}{Ll{BCCj}}}ANMd}\",[]]Ai[\"{{{Ll{ANHf}}}ANHd}\",[]]B`[\"{{{AGNj{{KGj{Lh}}}}}ANJb}\",[]]Ae[\"{{CF`KIn}BBLb}\",[]]Ag[\"{{CIbMjCFb}AB@f}\",[]]Aj[\"{{{Ll{AGCh}}CEn}Ln}\",[]]An[\"{{{Ll{AMJ`}}NLjCEn}INh}\",[]]An[\"{{{Ll{AOKf}}NLjCEn}NMb}\",[]]Ae[\"{{KInCF`}AOHl}\",[]]Ah[\"{{{Md{JBn}}}AHLd}\",[]]Cm[\"{{{Ll{B@Fd}}{KM`{{Ll{ANMh}}}}CJf{KM`{{JC`{Lh}}}}}AAMn}\",[]]A`[\"{KInBEOh}\",[]]o[\"{{}BEKn}\",[]]A`[\"{NMjBADn}\",[]]Ah[\"{{{Ll{NLf}}}B@Fd}\",[]]Bc[\"{{{Ll{ANMj}}{Ll{BCCl}}}ANMh}\",[]]Ai[\"{{{Ll{ANHj}}}ANHh}\",[]]Ek[\"{{AALj{AGNj{c}}{AGNj{{KGj{CF`}}}}CJf}{{KMj{{KIl{AOKl{KM`{AALj}}}}BCDd}}}{BCGjJBf}}\",[\"\"]]B`[\"{{{AGNj{{KGj{Lh}}}}}ANJd}\",[]]Af[\"{{BCJnKNl}B@Hn}\",[]]A`[\"{KInBELh}\",[]]Ae[\"{{CF`KIn}BBLd}\",[]]Ag[\"{{CIdMlCFb}AB@h}\",[]]Aj[\"{{{Ll{AGCj}}CEn}M`}\",[]]An[\"{{{Ll{AMJb}}NLjCEn}INj}\",[]]An[\"{{{Ll{AOKh}}NLjCEn}NMd}\",[]]Aj[\"{AHNl{{KMl{B@I`}}}}\",[]]Ai[\"{{{Ll{BBKl}}}BEFh}\",[]]Ai[\"{{{Ll{BBL`}}}BEFj}\",[]]o[\"{CIbCIb}\",[]]o[\"{CIdCId}\",[]]Ak[\"{{{AGNj{AHN`}}}AHMn}\",[]]Ai[\"{{{AGNj{NNb}}}NLj}\",[]]0Aj[\"{{{AGNj{ANNb}}}NLj}\",[]]Ak[\"{{{AGNj{AOLh}}}AHMn}\",[]]Ak[\"{{{AGNj{AHNb}}}AHMn}\",[]]Ak[\"{{{AGNj{AOLj}}}AHMn}\",[]]Bg[\"{{{AGNj{AHIn}}KMd}{{KM`{AAMh}}}}\",[]]Aa[\"{AGFdAAMh}\",[]]Bg[\"{{{AGNj{AHJ`}}KMf}{{KM`{AAMh}}}}\",[]]Aa[\"{AGFfAAMh}\",[]]Ag[\"{Nl{{KMn{HCj}}}}\",[]]Ag[\"{Nn{{KMn{HCj}}}}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]Aa[\"{ANL`AHMj}\",[]]Aa[\"{AB@bAHMj}\",[]]Aa[\"{B@HnBCJn}\",[]]A`[\"{AGFdCFb}\",[]]A`[\"{AGFfCFb}\",[]]A`[\"{AAM`IMl}\",[]]Am[\"{{{AGNj{BOdAAOd}}}HCj}\",[]]Am[\"{{{AGNj{BOdAANd}}}HCj}\",[]]Am[\"{{{AGNj{BOdAHLh}}}HCj}\",[]]Am[\"{{{AGNj{BOdAHMd}}}HCj}\",[]]Al[\"{{{AGNj{BOdKKb}}}HCj}\",[]]A`[\"{cc{}}\",[\"T\"]]00000000000000000000Aa[\"{BAChBCDf}\",[]]11111111Aa[\"{AGFjB@Hd}\",[]]2Aa[\"{BAChAGFj}\",[]]Aa[\"{AOLbAGFj}\",[]]Aa[\"{AHLlAGFj}\",[]]55Aa[\"{BAChAHLl}\",[]]Aa[\"{AGFjB@Hb}\",[]]7777Aa[\"{BAChAHNn}\",[]]8Aa[\"{ANN`AHNn}\",[]]Aa[\"{ANI`AHNn}\",[]]Aa[\"{BAChANN`}\",[]];;;;;;o[\"{CEnKO`}\",[]]>>>A`[\"{J@fB@Hn}\",[]]?A`[\"{KObBCJn}\",[]]A`[\"{cc{}}\",[\"T\"]]000000A`[\"{KObAB@h}\",[]]111A`[\"{AOLlJ@f}\",[]]2222Aa[\"{AMLbAMIn}\",[]]3333333A`[\"{BEIdJ@d}\",[]]A`[\"{BADdJ@d}\",[]]A`[\"{BBKdJ@d}\",[]]A`[\"{BBIfJ@d}\",[]]A`[\"{BBJhJ@d}\",[]]A`[\"{AHKjJ@d}\",[]]A`[\"{BAEbJ@d}\",[]]A`[\"{B@GlJ@d}\",[]]A`[\"{BECdJ@d}\",[]]A`[\"{B@FnJ@d}\",[]]=A`[\"{BEHbJ@d}\",[]]>>>>>>>>>An[\"{{{AGNj{c}}}AHNlCFh}\",[\"S\"]]????????????????Bm[\"{{{AGNj{KO`}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bk[\"{{{AGNj{KMd}}{AGNj{BOdc}}}HCjKLb}\",[\"H\"]]Bn[\"{{{AGNj{AMJf}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{ANJb}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bm[\"{{{AGNj{CIb}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bl[\"{{{AGNj{Mj}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{AB@f}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bm[\"{{{AGNj{KOb}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{BCDh}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bk[\"{{{AGNj{KMf}}{AGNj{BOdc}}}HCjKLb}\",[\"H\"]]Bn[\"{{{AGNj{AGFl}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{AOLd}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{AHO`}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{AMJh}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{BBIl}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{BCDd}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{ANJd}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bm[\"{{{AGNj{CId}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bl[\"{{{AGNj{Ml}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Bn[\"{{{AGNj{AB@h}}{AGNj{BOdc}}}HCjKLb}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HCj}\",[]]0000Cc[\"{{{AGNj{BOdKKb}}KIn}{{KMj{{KM`{IN`}}AGFl}}}}\",[]]El[\"{{{AGNj{NJl}}{AGNj{BOd{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{{AGNj{BOd{KGj{Lh}}}}AMIl}}}}\",[]]Bh[\"{{{AGNj{BOdNOb}}Ml}{{KM`{AB@h}}}}\",[]]El[\"{{{AGNj{NJn}}{AGNj{BOd{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{{AGNj{BOd{KGj{Lh}}}}AMIn}}}}\",[]]Bf[\"{{{AGNj{AHIn}}KMd}{{KM`{CHh}}}}\",[]]A`[\"{AHJjCFb}\",[]]Am[\"{{{AGNj{BOdAHJ`}}}HCj}\",[]]A`[\"{AHJlCFb}\",[]]Cj[\"{{{Ob{{AGNj{BOdAHHh}}}}{AGNj{BOdNKh}}}{{CHl{HCj}}}}\",[]]Dj[\"{{{Ob{{AGNj{BOdBAFd}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdAAOd}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdAAOf}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdAHIj}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Ob{{AGNj{BOdNMh}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Ob{{AGNj{BOdKLf}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdAGBj}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdAALh}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdANLd}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdANLn}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Di[\"{{{Ob{{AGNj{BOdKJf}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{Ob{{AGNj{BOdB@Fl}}}}{AGNj{BOdNKh}}}{{CHl{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AGNj{BOdAHJ`}}}{{KM`{J@l}}}}\",[]]De[\"{{{AGNj{BOdAHLh}}{AGNj{BOd{KGj{Lh}}}}}{{KMj{{KM`{KIn}}AGFj}}}}\",[]]Bm[\"{{{AGNj{BOdAHLj}}CJf}{{KMj{KKbAOLd}}}}\",[]]Bo[\"{{CId{AGNj{BOdc}}}{{KMj{BCOhJ@f}}}Mf}\",[\"R\"]]Bf[\"{{{AGNj{BOdAGDb}}}{{KM`{IMl}}}}\",[]]Ec[\"{{{AGNj{B@I`}}AHNl{AGNj{BOd{KGj{AHKd}}}}{AGNj{BOd{KGj{AB@b}}}}}{{KMl{KIn}}}}\",[]]Dj[\"{{{AGNj{NJl}}{AGNj{BOd{Oh{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{HCjAMIl}}}}\",[]]Dj[\"{{{AGNj{NJn}}{AGNj{BOd{Oh{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{HCjAMIn}}}}\",[]]C`[\"{{{AGNj{BOdAGDb}}IMlCJf}{{KMj{HCjBCDh}}}}\",[]]Cc[\"{{{AGNj{B@I`}}AHNl{AGNj{ABAl}}}{{KMl{HCj}}}}\",[]]Bn[\"{{{AGNj{BOdBDMj}}CF`}{{AGNj{BOdBDMj}}}}\",[]]Bn[\"{{{AGNj{BOdBDMl}}CF`}{{AGNj{BOdBDMl}}}}\",[]]Aj[\"{{{AGNj{AHIn}}}CIb}\",[]]Aj[\"{{{AGNj{AHJ`}}}CId}\",[]]Cn[\"{{{AGNj{NLd}}{AGNj{{KGj{Lh}}}}{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]Cn[\"{{{AGNj{NLf}}{AGNj{{KGj{Lh}}}}{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]A`[\"{B@HhCFb}\",[]]A`[\"{ABAhKIn}\",[]]A`[\"{ABAjKIn}\",[]]o[\"{JBbCJf}\",[]]Bm[\"{{{AGNj{BOdAHLh}}KO`}{{KMj{HCjANI`}}}}\",[]]Bm[\"{{{AGNj{BOdAHLj}}KOb}{{KMj{HCjANIh}}}}\",[]]Bn[\"{{{AGNj{AHNh}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Bn[\"{{{AGNj{B@Gh}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Bn[\"{{{AGNj{BBLb}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Bn[\"{{{AGNj{AHNj}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Bn[\"{{{AGNj{B@Gj}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Bn[\"{{{AGNj{BBLd}}{AGNj{CEl}}}{{KM`{IMl}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{Ll{BCCb}}NLjCEn}{{Md{AHJb}}}}\",[]]Bg[\"{{{Ll{AGCh}}NLjCEn}{{Md{AHJb}}}}\",[]]Bg[\"{{{Ll{AMJ`}}NLjCEn}{{Md{AHJb}}}}\",[]]Bg[\"{{{Ll{AOKf}}NLjCEn}{{Md{AHJb}}}}\",[]]Bg[\"{{{Ll{BCCd}}NLjCEn}{{Md{AHJd}}}}\",[]]Bg[\"{{{Ll{AGCj}}NLjCEn}{{Md{AHJd}}}}\",[]]Bg[\"{{{Ll{AMJb}}NLjCEn}{{Md{AHJd}}}}\",[]]Bg[\"{{{Ll{AOKh}}NLjCEn}{{Md{AHJd}}}}\",[]]Ai[\"{BCOb{{Ll{BCCd}}}}\",[]]o[\"{IMnIMl}\",[]]A`[\"{ABAnCFb}\",[]]o[\"{NOlKIn}\",[]]o[\"{IN`IMl}\",[]]A`[\"{ABB`CFb}\",[]]o[\"{NOnKIn}\",[]]Aa[\"{AMHlBACj}\",[]]Ag[\"{{{AGNj{Nj}}}Nj}\",[]]Ak[\"{{{AGNj{AMI`}}}AMI`}\",[]]Ak[\"{{{AGNj{AHIn}}}AHIn}\",[]]Ak[\"{{{AGNj{BDFn}}}BDFn}\",[]]Ak[\"{{{AGNj{BCDf}}}BCDf}\",[]]Ak[\"{{{AGNj{AAMl}}}AAMl}\",[]]Ak[\"{{{AGNj{AOIl}}}AOIl}\",[]]Ak[\"{{{AGNj{B@Hd}}}B@Hd}\",[]]Ak[\"{{{AGNj{AGFj}}}AGFj}\",[]]Ak[\"{{{AGNj{AHLl}}}AHLl}\",[]]Ak[\"{{{AGNj{B@Hb}}}B@Hb}\",[]]Ak[\"{{{AGNj{AHNn}}}AHNn}\",[]]Ak[\"{{{AGNj{ANN`}}}ANN`}\",[]]Ai[\"{{{AGNj{KO`}}}KO`}\",[]]Ak[\"{{{AGNj{BDFh}}}BDFh}\",[]]Ai[\"{{{AGNj{KMd}}}KMd}\",[]]Ak[\"{{{AGNj{ABAn}}}ABAn}\",[]]Ak[\"{{{AGNj{AHJj}}}AHJj}\",[]]Ak[\"{{{AGNj{AGFd}}}AGFd}\",[]]Ak[\"{{{AGNj{BAD`}}}BAD`}\",[]]Ai[\"{{{AGNj{NOl}}}NOl}\",[]]Ak[\"{{{AGNj{ANI`}}}ANI`}\",[]]Ak[\"{{{AGNj{BACh}}}BACh}\",[]]Ak[\"{{{AGNj{BAEj}}}BAEj}\",[]]Ak[\"{{{AGNj{BCFn}}}BCFn}\",[]]Ak[\"{{{AGNj{BCHn}}}BCHn}\",[]]Ak[\"{{{AGNj{AMJf}}}AMJf}\",[]]Ak[\"{{{AGNj{B@Fb}}}B@Fb}\",[]]Ak[\"{{{AGNj{ANMd}}}ANMd}\",[]]Ak[\"{{{AGNj{BDMj}}}BDMj}\",[]]Ak[\"{{{AGNj{ANHd}}}ANHd}\",[]]Ak[\"{{{AGNj{AMIf}}}AMIf}\",[]]Ak[\"{{{AGNj{AGDf}}}AGDf}\",[]]Ak[\"{{{AGNj{BACd}}}BACd}\",[]]Ak[\"{{{AGNj{BBIb}}}BBIb}\",[]]Ak[\"{{{AGNj{ANIl}}}ANIl}\",[]]Ak[\"{{{AGNj{ANJb}}}ANJb}\",[]]Ak[\"{{{AGNj{ANJj}}}ANJj}\",[]]Ak[\"{{{AGNj{BCJl}}}BCJl}\",[]]Ai[\"{{{AGNj{CIb}}}CIb}\",[]]Ag[\"{{{AGNj{Mj}}}Mj}\",[]]Ak[\"{{{AGNj{AB@f}}}AB@f}\",[]]Ag[\"{{{AGNj{Ln}}}Ln}\",[]]Ak[\"{{{AGNj{AGCh}}}AGCh}\",[]]Ai[\"{{{AGNj{INh}}}INh}\",[]]Ak[\"{{{AGNj{AMJ`}}}AMJ`}\",[]]Ai[\"{{{AGNj{NMb}}}NMb}\",[]]Ak[\"{{{AGNj{AOKf}}}AOKf}\",[]]Ak[\"{{{AGNj{BDF`}}}BDF`}\",[]]Ai[\"{{{AGNj{J@b}}}J@b}\",[]]Ai[\"{{{AGNj{KOb}}}KOb}\",[]]Ak[\"{{{AGNj{BDFj}}}BDFj}\",[]]Ak[\"{{{AGNj{BCDh}}}BCDh}\",[]]Ai[\"{{{AGNj{KMf}}}KMf}\",[]]Ak[\"{{{AGNj{ANLl}}}ANLl}\",[]]Ak[\"{{{AGNj{AHL`}}}AHL`}\",[]]Ak[\"{{{AGNj{AGFb}}}AGFb}\",[]]Ak[\"{{{AGNj{BCJj}}}BCJj}\",[]]Ak[\"{{{AGNj{AHIf}}}AHIf}\",[]]Ak[\"{{{AGNj{ABB`}}}ABB`}\",[]]Ak[\"{{{AGNj{AHJl}}}AHJl}\",[]]Ak[\"{{{AGNj{AGFf}}}AGFf}\",[]]Ak[\"{{{AGNj{BADb}}}BADb}\",[]]Ak[\"{{{AGNj{AGFl}}}AGFl}\",[]]Ak[\"{{{AGNj{AOLd}}}AOLd}\",[]]Ai[\"{{{AGNj{NOn}}}NOn}\",[]]Ak[\"{{{AGNj{AHO`}}}AHO`}\",[]]Ak[\"{{{AGNj{AMJd}}}AMJd}\",[]]Ak[\"{{{AGNj{B@Hf}}}B@Hf}\",[]]Ak[\"{{{AGNj{ANIh}}}ANIh}\",[]]Ak[\"{{{AGNj{BACj}}}BACj}\",[]]Ak[\"{{{AGNj{AGEl}}}AGEl}\",[]]Ak[\"{{{AGNj{B@E`}}}B@E`}\",[]]Ak[\"{{{AGNj{BAEl}}}BAEl}\",[]]Ak[\"{{{AGNj{BCG`}}}BCG`}\",[]]Ak[\"{{{AGNj{BCI`}}}BCI`}\",[]]Ak[\"{{{AGNj{AMJh}}}AMJh}\",[]]Ak[\"{{{AGNj{B@Fd}}}B@Fd}\",[]]Ak[\"{{{AGNj{ANMh}}}ANMh}\",[]]Ak[\"{{{AGNj{BDMl}}}BDMl}\",[]]Ak[\"{{{AGNj{ANHh}}}ANHh}\",[]]Ak[\"{{{AGNj{AMIh}}}AMIh}\",[]]Ak[\"{{{AGNj{AGDh}}}AGDh}\",[]]Ak[\"{{{AGNj{BACf}}}BACf}\",[]]Ak[\"{{{AGNj{BBId}}}BBId}\",[]]Ak[\"{{{AGNj{AAM`}}}AAM`}\",[]]Ak[\"{{{AGNj{BBIl}}}BBIl}\",[]]Ak[\"{{{AGNj{ANIn}}}ANIn}\",[]]Ak[\"{{{AGNj{BADj}}}BADj}\",[]]Ak[\"{{{AGNj{BE@n}}}BE@n}\",[]]Ak[\"{{{AGNj{AANj}}}AANj}\",[]]Ak[\"{{{AGNj{BCDd}}}BCDd}\",[]]Ak[\"{{{AGNj{ANJd}}}ANJd}\",[]]Ak[\"{{{AGNj{ANJl}}}ANJl}\",[]]Ak[\"{{{AGNj{B@Hn}}}B@Hn}\",[]]Ak[\"{{{AGNj{BCJn}}}BCJn}\",[]]Ak[\"{{{AGNj{AHKb}}}AHKb}\",[]]Ak[\"{{{AGNj{BELh}}}BELh}\",[]]Ai[\"{{{AGNj{CId}}}CId}\",[]]Ag[\"{{{AGNj{Ml}}}Ml}\",[]]Ak[\"{{{AGNj{AB@h}}}AB@h}\",[]]Ak[\"{{{AGNj{AOLl}}}AOLl}\",[]]Ak[\"{{{AGNj{BCOh}}}BCOh}\",[]]Ai[\"{{{AGNj{J@f}}}J@f}\",[]]Ak[\"{{{AGNj{BDFb}}}BDFb}\",[]]Ai[\"{{{AGNj{J@d}}}J@d}\",[]]Ag[\"{{{AGNj{M`}}}M`}\",[]]Ak[\"{{{AGNj{AGCj}}}AGCj}\",[]]Ai[\"{{{AGNj{INj}}}INj}\",[]]Ak[\"{{{AGNj{AMJb}}}AMJb}\",[]]Ai[\"{{{AGNj{NMd}}}NMd}\",[]]Ak[\"{{{AGNj{AOKh}}}AOKh}\",[]]Ai[\"{{{AGNj{J@n}}}J@n}\",[]]Ak[\"{{{AGNj{AB@b}}}AB@b}\",[]]Ak[\"{{{AGNj{ABAl}}}ABAl}\",[]]Ak[\"{{{AGNj{ANJn}}}ANJn}\",[]]Ak[\"{{{AGNj{BCOb}}}BCOb}\",[]]Ak[\"{{{AGNj{BADf}}}BADf}\",[]]Bn[\"{{{AGNj{AHIn}}KO`{AGNj{{KGj{Lh}}}}}HCj}\",[]]Bn[\"{{{AGNj{AAMl}}KO`{AGNj{{KGj{Lh}}}}}HCj}\",[]]Bd[\"{{{AGNj{CHh}}}{{KMj{`B@Dn}}}}\",[]]Bf[\"{{{AGNj{BOdAHJ`}}NLjKObIMl}HCj}\",[]]Bj[\"{{{AGNj{ANJb}}}{{AGNj{{KGj{Lh}}}}}}\",[]]Bj[\"{{{AGNj{ANJd}}}{{AGNj{{KGj{Lh}}}}}}\",[]]Aa[\"{AOJlAGEl}\",[]]A`[\"{B@HnKM`}\",[]]A`[\"{AB@fCFb}\",[]]A`[\"{AB@hCFb}\",[]]A`[\"{ANJjCJf}\",[]]A`[\"{ANJlCJf}\",[]]l[\"{NLl}\",[]]l[\"{NLn}\",[]]Ce[\"{{{AGNj{BCGj}}{AGNj{BOdMf}}}{{KMj{ANJdBCDd}}}}\",[]]Ce[\"{{{AGNj{BEOh}}{AGNj{BOdMf}}}{{KMj{ANJdBCDd}}}}\",[]]Bb[\"{{{AGNj{AHJ`}}}{{Oh{KMf}}}}\",[]]Bd[\"{{{AGNj{JBh}}{AGNj{CEl}}}HCj}\",[]]Am[\"{{{AGNj{AMIb}}KIn}HCj}\",[]]Ai[\"{{{AGNj{JBj}}}HCj}\",[]]Bf[\"{{{Ob{{AGNj{BOdAHHh}}}}NLj}HCj}\",[]]Bm[\"{{{AGNj{BOdAHMd}}KO`}{{KMj{HCjANI`}}}}\",[]]Bm[\"{{{AGNj{BOdAHMf}}KOb}{{KMj{HCjANIh}}}}\",[]]Am[\"{AANd{{KMj{HCjAHLn}}}}\",[]]Db[\"{{{AGNj{BOdAAMn}}AANf{AGNj{BOd{Oh{Lh}}}}}{{KMj{ABAjAHM`}}}}\",[]]Bh[\"{{{AGNj{NNb}}{Ob{{Md{KL`}}}}}HCj}\",[]]Bi[\"{{{AGNj{ANNb}}{Ob{{Md{KL`}}}}}HCj}\",[]]Bi[\"{{{AGNj{AMKh}}{Ob{{Md{KL`}}}}}HCj}\",[]]n[\"{NjCJf}\",[]]Ak[\"{{{AGNj{AHIn}}}BAD`}\",[]]Ak[\"{{{AGNj{AAMl}}}AOIl}\",[]]An[\"{{{AGNj{BOdAHJ`}}}BADb}\",[]]Ce[\"{{{AGNj{BOdAHLb}}{KM`{KNl}}}{{AGNj{BOdAHLb}}}}\",[]]Ce[\"{{{AGNj{BOdAHLd}}{KM`{KNl}}}{{AGNj{BOdAHLd}}}}\",[]]Ck[\"{{{AGNj{BOdAHMd}}{AGNj{{KGj{Lh}}}}}{{KMj{KInAHNn}}}}\",[]]Ck[\"{{{AGNj{BOdAHMf}}{AGNj{{KGj{Lh}}}}}{{KMj{KInAHO`}}}}\",[]]Bl[\"{{{AGNj{BCOh}}{AGNj{BOdc}}}HCjKK`}\",[\"W\"]]Aj[\"{{{AGNj{AAMl}}}KJf}\",[]]An[\"{AANd{{KMj{AHIjBACh}}}}\",[]]Bh[\"{{{AGNj{BOdNOb}}Ml}{{KM`{AB@h}}}}\",[]]Fg[\"{{{AGNj{BOdAAMn}}AANfNLj{AGNj{BOd{Oh{Lh}}}}{KM`{{Ll{ANMh}}}}}{{KMj{{KIl{BBIlAHJ`}}{Md{AMHl}}}}}}\",[]]Am[\"{AGNj{{AGNj{c}}}{}}\",[\"T\"]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000o[\"{NOlKIn}\",[]]o[\"{NOnKIn}\",[]]o[\"{NjAMI`}\",[]]Bb[\"{{AHMjB@DlNj}{{KMn{AMIb}}}}\",[]]Aj[\"{AHMj{{KMl{AAMl}}}}\",[]]B`[\"{{}{{KMj{HCj{Md{IOd}}}}}}\",[]]Ak[\"{{{AGNj{AHIn}}}BACh}\",[]]Be[\"{{{AGNj{AAMn}}}{{AGNj{B@Fd}}}}\",[]]o[\"{LhBCJl}\",[]]A`[\"{AHJjCFb}\",[]]o[\"{ANMdLl}\",[]]o[\"{LhBCJn}\",[]]A`[\"{AHJlCFb}\",[]]o[\"{ANMhLl}\",[]]A`[\"{B@HnKM`}\",[]]Bl[\"{{{AGNj{BOdc}}}{{KMj{KO`AOLl}}}Mf}\",[\"B\"]]Bl[\"{{{AGNj{BOdc}}}{{KMj{KMdAOLl}}}Mf}\",[\"B\"]]Bm[\"{{{AGNj{BOdc}}}{{KMj{AGDfAOLl}}}Mf}\",[\"B\"]]Bm[\"{{{AGNj{BOdc}}}{{KMj{BCJlAOLl}}}Mf}\",[\"B\"]]Bm[\"{{{AGNj{BOdc}}}{{KMj{AB@fAOLl}}}Mf}\",[\"B\"]]Bh[\"{{{AGNj{BOdc}}}{{KNb{KOb}}}Mf}\",[\"B\"]]Bh[\"{{{AGNj{BOdc}}}{{KNb{KMf}}}Mf}\",[\"B\"]]Bi[\"{{{AGNj{BOdc}}}{{KNb{AGDh}}}Mf}\",[\"B\"]]Ek[\"{{{AGNj{BOd{KKn{AALj}}}}{AGNj{c}}{AGNj{{KGj{CF`}}}}CJf}{{KMj{BADjBCDd}}}{BCGjJBf}}\",[\"\"]]Bi[\"{{{AGNj{BOdc}}}{{KNb{BCJn}}}Mf}\",[\"B\"]]Bi[\"{{{AGNj{BOdc}}}{{KNb{AB@h}}}Mf}\",[\"B\"]]Bh[\"{{{AGNj{BOdc}}}{{KNb{INf}}}Mf}\",[\"B\"]]A`[\"{AB@bKM`}\",[]]Bk[\"{{{AGNj{KO`}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bk[\"{{{AGNj{KMd}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{AGDf}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{BCJl}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{AB@f}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bk[\"{{{AGNj{KOb}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bk[\"{{{AGNj{KMf}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{AGDh}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{BCJn}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bl[\"{{{AGNj{AB@h}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bk[\"{{{AGNj{INf}}{AGNj{BOdc}}}HCjKK`}\",[\"B\"]]Bj[\"{{{AGNj{BOdAHMd}}}{{KMj{HCjANI`}}}}\",[]]Bj[\"{{{AGNj{BOdAHMf}}}{{KMj{HCjAMJd}}}}\",[]]An[\"{{{AGNj{AML`}}AAMh}HCj}\",[]]Ej[\"{{{AGNj{BOdAAMn}}NLjAHMj{KM`{KLd}}{KM`{ANJl}}AALj{AGNj{BOd{Oh{Lh}}}}}{{KM`{AOIh}}}}\",[]]o[\"{CGnNLl}\",[]]o[\"{CH`NLn}\",[]]A`[\"{AANdHCj}\",[]]Ba[\"{{{AGNj{BOdAAMn}}AANf}HCj}\",[]]Bh[\"{{{AGNj{AHNh}}{AGNj{CEl}}IMl}HCj}\",[]]Bh[\"{{{AGNj{B@Gh}}{AGNj{CEl}}IMl}HCj}\",[]]Bh[\"{{{AGNj{BBLb}}{AGNj{CEl}}IMl}HCj}\",[]]Bh[\"{{{AGNj{AHNj}}{AGNj{CEl}}IMl}HCj}\",[]]Bh[\"{{{AGNj{B@Gj}}{AGNj{CEl}}IMl}HCj}\",[]]Bh[\"{{{AGNj{BBLd}}{AGNj{CEl}}IMl}HCj}\",[]]A`[\"{AGCnCJf}\",[]]o[\"{IMnCFb}\",[]]o[\"{IN`CFb}\",[]]Ce[\"{{{AGNj{BOdAHJb}}NLjNLjCFbCJf{AGNj{ANLj}}}HCj}\",[]]Cc[\"{{{AGNj{BOdLn}}NLjNLjCFbCJf{AGNj{ANLj}}}HCj}\",[]]Cd[\"{{{AGNj{BOdINh}}NLjNLjCFbCJf{AGNj{ANLj}}}HCj}\",[]]Cd[\"{{{AGNj{BOdNMb}}NLjNLjCFbCJf{AGNj{ANLj}}}HCj}\",[]]Ce[\"{{{AGNj{BOdAHJd}}NLjNLjCFbCJf{AGNj{ANLl}}}HCj}\",[]]Cc[\"{{{AGNj{BOdM`}}NLjNLjCFbCJf{AGNj{ANLl}}}HCj}\",[]]Cd[\"{{{AGNj{BOdINj}}NLjNLjCFbCJf{AGNj{ANLl}}}HCj}\",[]]Cd[\"{{{AGNj{BOdNMd}}NLjNLjCFbCJf{AGNj{ANLl}}}HCj}\",[]]o[\"{CGnNLl}\",[]]o[\"{CH`NLn}\",[]]o[\"{BBLjLh}\",[]]A`[\"{BACdIMl}\",[]]A`[\"{BBIbIMl}\",[]]A`[\"{BACfIMl}\",[]]A`[\"{BBIdIMl}\",[]]A`[\"{B@HnKNl}\",[]]Aa[\"{B@EdBACj}\",[]]Bg[\"{{{AGNj{AAMl}}AGGb}{{KMl{HCj}}}}\",[]]A`[\"{AANdHCj}\",[]]Ce[\"{{{AGNj{BOdAAMn}}AANf{AGNj{BOd{Oh{Lh}}}}}ABAj}\",[]]l[\"{NLl}\",[]]l[\"{NLn}\",[]]Am[\"{{AAMjNj}{{KMn{HCj}}}}\",[]]B`[\"{{ANMdAHMj}{{KMl{AAMl}}}}\",[]]Bb[\"{ANMb{{KMj{HCj{Md{IOd}}}}}}\",[]]Bk[\"{{{AGNj{BCDf}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{B@Hd}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{AGFj}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{AHLl}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{B@Hb}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{AHNn}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{ANN`}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{BACh}}}{{KM`{{AGNj{IOd}}}}}}\",[]]Bk[\"{{{AGNj{BACj}}}{{KM`{{AGNj{IOd}}}}}}\",[]]A`[\"{ABAhKM`}\",[]]A`[\"{ABAjKM`}\",[]]A`[\"{ABAlKM`}\",[]]Bg[\"{{{AGNj{CHh}}}{{KMj{AHKnAHI`}}}}\",[]]Aa[\"{ANLfAHL`}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]A`[\"{AB@bKIn}\",[]]Aa[\"{BAD`ABAn}\",[]]Aa[\"{BADbABB`}\",[]]10Dh[\"{{{AGNj{NLd}}{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{HCjAMIl}}}}\",[]]Dh[\"{{{AGNj{NLf}}{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}{{KMj{HCjAMIn}}}}\",[]]Aj[\"{{{AGNj{AHJb}}}CFb}\",[]]Ah[\"{{{AGNj{Ln}}}CFb}\",[]]Ai[\"{{{AGNj{INh}}}CFb}\",[]]Ai[\"{{{AGNj{NMb}}}CFb}\",[]]Aj[\"{{{AGNj{AHJd}}}CFb}\",[]]Ah[\"{{{AGNj{M`}}}CFb}\",[]]Ai[\"{{{AGNj{INj}}}CFb}\",[]]Ai[\"{{{AGNj{NMd}}}CFb}\",[]]Aj[\"{{{AGNj{BDLh}}}KIn}\",[]]Aj[\"{{{AGNj{BDLj}}}KIn}\",[]]Aj[\"{{{AGNj{BEKn}}}KIn}\",[]]Aj[\"{{{AGNj{BELh}}}KIn}\",[]]n[\"{NjKIn}\",[]]n[\"{{}NKf}\",[]]0000o[\"{MhAALn}\",[]]Ba[\"{{AGNj{AGNj{c}}}AAOh{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AGNj{AAMl}}AHMj{AGNj{CEl}}}{{KMj{AHIjANIl}}}}\",[]]Dl[\"{{{AGNj{BOdAAMn}}NLjANHhAHMj{AGNj{CEl}}}{{KMj{{KIl{BBIlAHJ`}}ANIn}}}}\",[]]Dm[\"{{{AGNj{AGEf}}KMdCFb{AGNj{{KGj{Lh}}}}{AGNj{BOdAALj}}}{{KMj{HCjAMIl}}}}\",[]]Ca[\"{{{AGNj{AGDl}}KIn{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]Dm[\"{{{AGNj{AGEh}}KMfCFb{AGNj{{KGj{Lh}}}}{AGNj{BOdAALj}}}{{KMj{HCjAMIn}}}}\",[]]Ca[\"{{{AGNj{AGDn}}KIn{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]o[\"{{}AMKn}\",[]]n[\"{{}JBh}\",[]]o[\"{{}AMIb}\",[]]o[\"{{}AOHj}\",[]]o[\"{{}AOIl}\",[]]n[\"{{}KO`}\",[]]n[\"{{}KMd}\",[]]o[\"{{}ABAn}\",[]]o[\"{{}AHJj}\",[]]o[\"{{}AGFd}\",[]]o[\"{{}BAD`}\",[]]n[\"{{}NOl}\",[]]o[\"{{}ANI`}\",[]]o[\"{{}BAEj}\",[]]o[\"{{}BCFn}\",[]]o[\"{{}BCHn}\",[]]o[\"{{}AMJf}\",[]]o[\"{{}B@Fb}\",[]]o[\"{{}BDMj}\",[]]o[\"{{}BBLb}\",[]]o[\"{{}AGCh}\",[]]o[\"{{}AMJ`}\",[]]o[\"{{}AOKf}\",[]]o[\"{{}BCCf}\",[]]o[\"{{}AOHl}\",[]]o[\"{{}BEKn}\",[]]n[\"{{}KOb}\",[]]n[\"{{}KMf}\",[]]o[\"{{}AHL`}\",[]]o[\"{{}AHIf}\",[]]o[\"{{}ABB`}\",[]]o[\"{{}AHJl}\",[]]o[\"{{}AGFf}\",[]]o[\"{{}BADb}\",[]]n[\"{{}NOn}\",[]]o[\"{{}B@Hf}\",[]]o[\"{{}ANIh}\",[]]o[\"{{}BAEl}\",[]]o[\"{{}BCG`}\",[]]o[\"{{}BCI`}\",[]]o[\"{{}AMJh}\",[]]o[\"{{}B@Fd}\",[]]o[\"{{}BDMl}\",[]]o[\"{{}BELh}\",[]]o[\"{{}BBLd}\",[]]o[\"{{}AGCj}\",[]]o[\"{{}AMJb}\",[]]o[\"{{}AOKh}\",[]]o[\"{{}BCCh}\",[]]o[\"{{}AB@b}\",[]]n[\"{{}JBj}\",[]]o[\"{{}BADf}\",[]]o[\"{{}AOIj}\",[]]Ak[\"{{{AGNj{AOKl}}}ANJd}\",[]]Ak[\"{{{AGNj{BADj}}}ANJd}\",[]]Aa[\"{BE@nANJd}\",[]]Aa[\"{BCK`ANJd}\",[]]A`[\"{CHbANJd}\",[]]A`[\"{JAnANJd}\",[]]A`[\"{JBbANJd}\",[]]Aa[\"{BBLjANJd}\",[]]Cg[\"{{{AGNj{AGEf}}KMdCFb{AGNj{BOd{KGj{Lh}}}}KIn}HCj}\",[]]Ca[\"{{{AGNj{AGDl}}KIn{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]Cg[\"{{{AGNj{AGEh}}KMfCFb{AGNj{BOd{KGj{Lh}}}}KIn}HCj}\",[]]Ca[\"{{{AGNj{AGDn}}KIn{AGNj{BOd{KGj{Lh}}}}}HCj}\",[]]Aj[\"{{{AGNj{AHLh}}}CJf}\",[]]Ak[\"{{{AGNj{AHJb}}}BCCf}\",[]]Ai[\"{{{AGNj{Ln}}}BCCf}\",[]]Aj[\"{{{AGNj{INh}}}BCCf}\",[]]Aj[\"{{{AGNj{NMb}}}BCCf}\",[]]Ak[\"{{{AGNj{AHJd}}}BCCh}\",[]]Ai[\"{{{AGNj{M`}}}BCCh}\",[]]Aj[\"{{{AGNj{INj}}}BCCh}\",[]]Aj[\"{{{AGNj{NMd}}}BCCh}\",[]]Bn[\"{{{AGNj{BOdBAEj}}CEn}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}CEn}{{AGNj{BOdBAEl}}}}\",[]]Bf[\"{{{AGNj{BOdAHJb}}NLjCFbCFb}HCj}\",[]]Bd[\"{{{AGNj{BOdLn}}NLjCFbCFb}HCj}\",[]]Bf[\"{{{AGNj{BOdAHJd}}NLjCFbCFb}HCj}\",[]]Bd[\"{{{AGNj{BOdM`}}NLjCFbCFb}HCj}\",[]]Aj[\"{{{AGNj{AHIn}}}KLf}\",[]]Bc[\"{{{AGNj{AAOf}}}{{KM`{KMd}}}}\",[]]Aa[\"{BE@nANJd}\",[]]Aa[\"{BCK`ANJd}\",[]]A`[\"{CHbANJd}\",[]]A`[\"{JAnANJd}\",[]]Aa[\"{BBLjANJd}\",[]]Di[\"{{{AGNj{AHMd}}}{{`{{KL`{}{{KMb{{KMj{{KM`{KO`}}ANN`}}}}}}CI`CIf}}}}\",[]]Bn[\"{{{AGNj{AHMf}}}{{KMj{{KM`{KOb}}ANIh}}}}\",[]]n[\"{NjKIn}\",[]]o[\"{JBhKIn}\",[]]Am[\"{{{AGNj{BOdAHJ`}}}NOb}\",[]]Aj[\"{{{AGNj{AGEf}}}KIn}\",[]]Aj[\"{{{AGNj{AGEh}}}KIn}\",[]]A`[\"{AGNjKNn}\",[]]0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AGNj{BDFn}}}{{KM`{AHIn}}}}\",[]]Bn[\"{{{AGNj{BOdANHd}}CF`}{{AGNj{BOdANHd}}}}\",[]]Bn[\"{{{AGNj{BOdANHh}}CF`}{{AGNj{BOdANHh}}}}\",[]]A`[\"{BE@nCF`}\",[]]A`[\"{BCK`CF`}\",[]]o[\"{CHbCF`}\",[]]o[\"{JAnCF`}\",[]]A`[\"{AGCnKM`}\",[]]Aa[\"{ABAlAGNj}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]Aa[\"{B@HhAAMh}\",[]]A`[\"{KKbB@Hf}\",[]]Aa[\"{BAD`AHJj}\",[]]Aa[\"{BADbAHJl}\",[]]10Bg[\"{{{AGNj{BOdc}}KIn}ANJb{MfJBf}}\",[\"\"]]Bg[\"{{{AGNj{BOdc}}KIn}ANJd{MfJBf}}\",[\"\"]]o[\"{{}BADl}\",[]]o[\"{{}BADn}\",[]]A`[\"{CFbBEKn}\",[]]Cd[\"{{{AGNj{CEl}}}{{KMn{AMI`c}}}{}}\",[\"FromStr::Err\"]]o[\"{CF`KO`}\",[]]o[\"{CF`KOb}\",[]]Al[\"{CFb{{KMj{KO`BDFh}}}}\",[]]Al[\"{CFb{{KMj{KObBDFj}}}}\",[]]Aa[\"{BDEbAMIj}\",[]]Aa[\"{BEOjAOJd}\",[]]Aa[\"{BDEdAMIj}\",[]]Aa[\"{BEOlAOJd}\",[]]Af[\"{{}{{KM`{Ab}}}}\",[]]0000Aj[\"{{{AGNj{AHJ`}}}CJf}\",[]]Bo[\"{{{AGNj{BOdBCHn}}AAMh}{{AGNj{BOdBCHn}}}}\",[]]Bo[\"{{{AGNj{BOdBCI`}}AAMh}{{AGNj{BOdBCI`}}}}\",[]]Ao[\"{{{Md{AHJb}}}{{Md{Lj}}}}\",[]]Am[\"{{{Md{Ln}}}{{Md{Lj}}}}\",[]]An[\"{{{Md{INh}}}{{Md{Lj}}}}\",[]]An[\"{{{Md{NMb}}}{{Md{Lj}}}}\",[]]Ao[\"{{{Md{AHJd}}}{{Md{Lj}}}}\",[]]Am[\"{{{Md{M`}}}{{Md{Lj}}}}\",[]]An[\"{{{Md{INj}}}{{Md{Lj}}}}\",[]]An[\"{{{Md{NMd}}}{{Md{Lj}}}}\",[]]Aj[\"{{{AGNj{BDFn}}}CJf}\",[]]Bo[\"{{{AGNj{BOdBDMj}}AAMh}{{AGNj{BOdBDMj}}}}\",[]]Bo[\"{{{AGNj{BOdBDMl}}AAMh}{{AGNj{BOdBDMl}}}}\",[]]Bc[\"{{{AGNj{AHIj}}}{{KM`{KLd}}}}\",[]]Bc[\"{{{AGNj{AHIn}}}{{KM`{KLd}}}}\",[]]Bc[\"{{{AGNj{AANd}}}{{KM`{KLd}}}}\",[]]Bc[\"{{{AGNj{AHJ`}}}{{KM`{KLd}}}}\",[]]Bc[\"{{{AGNj{AANf}}}{{KM`{KLd}}}}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]Bc[\"{{{AGNj{AGDb}}}{{KM`{KIn}}}}\",[]]Am[\"{{{AGNj{AML`}}KIn}HCj}\",[]]Aj[\"{{{AGNj{AHIn}}}NMh}\",[]]Bg[\"{{{AGNj{AHMd}}}{{KMj{BMnANI`}}}}\",[]]Bg[\"{{{AGNj{AHMf}}}{{KMj{BMnANIh}}}}\",[]]A`[\"{AOJ`KM`}\",[]]A`[\"{AOJbKM`}\",[]]A`[\"{AGCnKM`}\",[]]A`[\"{AMHlKM`}\",[]]Ck[\"{{{AGNj{BOdB@Fb}}{KM`{{JC`{Lh}}}}}{{AGNj{BOdB@Fb}}}}\",[]]Ck[\"{{{AGNj{BOdB@Fd}}{KM`{{JC`{Lh}}}}}{{AGNj{BOdB@Fd}}}}\",[]]A`[\"{BCCfKM`}\",[]]A`[\"{BCChKM`}\",[]]Ac[\"{AGNjc{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{KMj{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000Al[\"{CFb{{KMj{KO`BDFh}}}}\",[]]1Al[\"{KIn{{KMj{KO`BDFh}}}}\",[]]Al[\"{HCh{{KMj{KO`BDFh}}}}\",[]]333333333333333Aj[\"{AAMh{{KMj{AMJf}}}}\",[]]4444444444444444444444444444444Aj[\"{ANH`{{KMj{BBKj}}}}\",[]]Bb[\"{{{Ll{ANH`}}}{{KMj{BBKj}}}}\",[]]6Aj[\"{ANM`{{KMj{BBKn}}}}\",[]]Bb[\"{{{Ll{ANM`}}}{{KMj{BBKn}}}}\",[]]888888888888888888888Al[\"{CFb{{KMj{KObBDFj}}}}\",[]]Al[\"{KIn{{KMj{KObBDFj}}}}\",[]]Al[\"{HCh{{KMj{KObBDFj}}}}\",[]];;;;;;;;;;;;;;;;;;;;;;;;;;;;;;Bm[\"{AAMh{{KMj{AMJhc}}}{}}\",[\"TryFrom::Error\"]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<=A`[\"{JBhAMKn}\",[]]A`[\"{AMIbJBh}\",[]]Bi[\"{{{Ll{ANH`}}JBl}{{KMj{BBKjBDF`}}}}\",[]]Bi[\"{{{Ll{ANM`}}JBl}{{KMj{BBKnBDF`}}}}\",[]]Bi[\"{{{Ll{ANH`}}JBl}{{KMj{BBKlBDFb}}}}\",[]]Bi[\"{{{Ll{ANM`}}JBl}{{KMj{BBL`BDFb}}}}\",[]]Co[\"{{{AGNj{BOdAHMd}}{AGNj{BOd{KGj{IMl}}}}}{{KMj{NOlAHNn}}}}\",[]]Co[\"{{{AGNj{BOdAHMf}}{AGNj{BOd{KGj{IMl}}}}}{{KMj{NOnAHO`}}}}\",[]]Aj[\"{{{AGNj{AHJ`}}}CJf}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]A`[\"{AGCnCJf}\",[]]E`[\"{{{AGNj{BOdB@Fb}}c}{{AGNj{BOdB@Fb}}}{{A`{}{{KMb{{Md{BDLh}}}}}}CI`CIf}}\",[\"F\"]]E`[\"{{{AGNj{BOdB@Fd}}c}{{AGNj{BOdB@Fd}}}{{A`{}{{KMb{{Md{BDLj}}}}}}CI`CIf}}\",[\"F\"]]Bi[\"{{{AGNj{B@Dj}}}{{Ob{{Md{AGG`}}}}}}\",[]]n[\"{NjCFb}\",[]]Aa[\"{AMKnAGE`}\",[]]76Aa[\"{AHLnAANd}\",[]]Aa[\"{AHM`AANf}\",[]]Cf[\"{{{AGNj{BOdBCFn}}{KM`{AAMh}}}{{AGNj{BOdBCFn}}}}\",[]]Cf[\"{{{AGNj{BOdBCG`}}{KM`{AAMh}}}{{AGNj{BOdBCG`}}}}\",[]]9;:An[\"{{{AGNj{AML`}}AAMh}HCj}\",[]]B`[\"{{{AGNj{BOdAHJb}}CEn}HCj}\",[]]An[\"{{{AGNj{BOdLn}}CEn}HCj}\",[]]Ao[\"{{{AGNj{BOdINh}}CEn}HCj}\",[]]Ao[\"{{{AGNj{BOdNMb}}CEn}HCj}\",[]]B`[\"{{{AGNj{BOdAHJd}}CEn}HCj}\",[]]An[\"{{{AGNj{BOdM`}}CEn}HCj}\",[]]Ao[\"{{{AGNj{BOdINj}}CEn}HCj}\",[]]Ao[\"{{{AGNj{BOdNMd}}CEn}HCj}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]10Bh[\"{{{AGNj{AHIn}}}{{KM`{{Md{Lj}}}}}}\",[]]Bg[\"{{{AGNj{NNd}}}{{KM`{{Md{Lj}}}}}}\",[]]Bh[\"{{{AGNj{AHNd}}}{{KM`{{Md{Lj}}}}}}\",[]]Bg[\"{{{AGNj{NNf}}}{{KM`{{Md{Lj}}}}}}\",[]]Bh[\"{{{AGNj{AHNf}}}{{KM`{{Md{Lj}}}}}}\",[]]Dm[\"{{{AGNj{BOdAHLh}}{AGNj{BOdNKh}}{AGNj{BOdNMl}}}{{CHl{{KMj{HCjAGFj}}}}}}\",[]]Da[\"{{{Ob{{AGNj{BOdAHMd}}}}{AGNj{BOdNKh}}}{{CHl{{KMl{HCj}}}}}}\",[]]D`[\"{{{AGNj{BOdAHJ`}}NLjKIn{AGNj{BOd{Oh{Lh}}}}}{{KM`{ABAj}}}}\",[]]Cf[\"{{{AGNj{BOdBAEj}}{AGNj{CEl}}}{{AGNj{BOdBAEj}}}}\",[]]Cf[\"{{{AGNj{BOdBAEl}}{AGNj{CEl}}}{{AGNj{BOdBAEl}}}}\",[]]Ak[\"{{{AGNj{AHIn}}}ANLd}\",[]]Bj[\"{{{AGNj{AHIn}}IMl}{{KMj{HCjBCDf}}}}\",[]]Bn[\"{{{AGNj{BOdBAEj}}CJf}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}CJf}{{AGNj{BOdBAEl}}}}\",[]]Ai[\"{{{AGNj{NLd}}}KIn}\",[]]Ai[\"{{{AGNj{NLf}}}KIn}\",[]]De[\"{{{Ll{ANHf}}CF`{AGNj{CEl}}{AGNj{BCOh}}}{{KMj{{Md{NNd}}ANIl}}}}\",[]]Bo[\"{{{Ll{ANMf}}CF`{AGNj{BCOh}}}{{Md{NNd}}}}\",[]]De[\"{{{Ll{BBKj}}CF`{AGNj{CEl}}{AGNj{BCOh}}}{{KMj{{Md{NNd}}ANIl}}}}\",[]]Bo[\"{{{Ll{BBKn}}CF`{AGNj{BCOh}}}{{Md{NNd}}}}\",[]]De[\"{{{Ll{ANHj}}CF`{AGNj{CEl}}{AGNj{BCOh}}}{{KMj{{Md{NNf}}ANIn}}}}\",[]]Bo[\"{{{Ll{ANMj}}CF`{AGNj{BCOh}}}{{Md{NNf}}}}\",[]]De[\"{{{Ll{BBKl}}CF`{AGNj{CEl}}{AGNj{BCOh}}}{{KMj{{Md{NNf}}ANIn}}}}\",[]]Bo[\"{{{Ll{BBL`}}CF`{AGNj{BCOh}}}{{Md{NNf}}}}\",[]]De[\"{{{Ll{BEFh}}CF`{AGNj{CEl}}{AGNj{BCOh}}}{{KMj{{Md{NNf}}ANIj}}}}\",[]]Bo[\"{{{Ll{BEFj}}CF`{AGNj{BCOh}}}{{Md{NNf}}}}\",[]]Bm[\"{KIn{{KMj{{KIl{KIn{KM`{KIn}}}}BCOd}}}}\",[]]0Cc[\"{{{AGNj{BCCj}}{AGNj{{KGj{Lh}}}}}{{Md{NJl}}}}\",[]]Cc[\"{{{AGNj{BCCl}}{AGNj{{KGj{Lh}}}}}{{Md{NJn}}}}\",[]]Bm[\"{{AHMjB@DlNj}{{KMn{{KIl{AAMjAHIl}}}}}}\",[]]Bd[\"{{{AGNj{AHJ`}}}{{AGNj{NNf}}}}\",[]]A`[\"{AMIbJBh}\",[]]Cb[\"{{{AGNj{AMK`}}CIbAHMjANJbNLj}{{KM`{AHLb}}}}\",[]]Cb[\"{{{AGNj{BADl}}CIbAHMjANJbNLj}{{KM`{AHLb}}}}\",[]]Cb[\"{{{AGNj{AMKb}}CIdAHMjANJdNLj}{{KM`{AHLd}}}}\",[]]Cb[\"{{{AGNj{BADn}}CIdAHMjANJdNLj}{{KM`{AHLd}}}}\",[]]B`[\"{{{AGNj{BOdAHJ`}}NLj}HCj}\",[]]Bo[\"{{{AGNj{BOdAHIj}}}{{KMj{{Md{Lj}}BACh}}}}\",[]]Bh[\"{{{AGNj{AHIn}}}{{KM`{{Md{Lj}}}}}}\",[]]Bg[\"{{{AGNj{NNd}}}{{KM`{{Md{Lj}}}}}}\",[]]Bh[\"{{{AGNj{AHNd}}}{{KM`{{Md{Lj}}}}}}\",[]]Bg[\"{{{AGNj{NNf}}}{{KM`{{Md{Lj}}}}}}\",[]]Bh[\"{{{AGNj{AHNf}}}{{KM`{{Md{Lj}}}}}}\",[]]o[\"{AHJjLh}\",[]]o[\"{AHJlLh}\",[]]Ai[\"{{{AGNj{CEl}}}CJf}\",[]]Aj[\"{{{AGNj{AHJb}}}CFb}\",[]]Ah[\"{{{AGNj{Ln}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAGCh}}CFb}{{AGNj{BOdAGCh}}}}\",[]]Ai[\"{{{AGNj{INh}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAMJ`}}CFb}{{AGNj{BOdAMJ`}}}}\",[]]Ai[\"{{{AGNj{NMb}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAOKf}}CFb}{{AGNj{BOdAOKf}}}}\",[]]Aj[\"{{{AGNj{AHJd}}}CFb}\",[]]Ah[\"{{{AGNj{M`}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAGCj}}CFb}{{AGNj{BOdAGCj}}}}\",[]]Ai[\"{{{AGNj{INj}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAMJb}}CFb}{{AGNj{BOdAMJb}}}}\",[]]Ai[\"{{{AGNj{NMd}}}CFb}\",[]]Bn[\"{{{AGNj{BOdAOKh}}CFb}{{AGNj{BOdAOKh}}}}\",[]]Ai[\"{{{AGNj{NNd}}}CJf}\",[]]Aj[\"{{{AGNj{AHNd}}}CJf}\",[]]Aj[\"{{{AGNj{AHJ`}}}CJf}\",[]]Ai[\"{{{AGNj{NNf}}}CJf}\",[]]Aj[\"{{{AGNj{AHNf}}}CJf}\",[]]Co[\"{{{AGNj{NNd}}ANJb{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}CJf}\",[]]D`[\"{{{AGNj{AHNd}}ANJb{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}CJf}\",[]]Co[\"{{{AGNj{NNf}}ANJd{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}CJf}\",[]]D`[\"{{{AGNj{AHNf}}ANJd{AGNj{{KGj{Lh}}}}{AGNj{{KGj{Lh}}}}}CJf}\",[]]Bn[\"{{{AGNj{BOdBCHn}}CEn}{{AGNj{BOdBCHn}}}}\",[]]Bn[\"{{{AGNj{BOdBCI`}}CEn}{{AGNj{BOdBCI`}}}}\",[]]Cc[\"{{{AGNj{BOdNNd}}}{{KM`{{NLl{{Md{AGEf}}}}}}}}\",[]]Cd[\"{{{AGNj{BOdAHNd}}}{{KM`{{NLl{{Md{AGEf}}}}}}}}\",[]]Cc[\"{{{AGNj{BOdNNf}}}{{KM`{{NLn{{Md{AGEh}}}}}}}}\",[]]Cd[\"{{{AGNj{BOdAHNf}}}{{KM`{{NLn{{Md{AGEh}}}}}}}}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]De[\"{{{AGNj{BOdBAEj}}c{AGNj{CEl}}}{{AGNj{BOdBAEj}}}{{IMj{CHf}}}}\",[\"\"]]De[\"{{{AGNj{BOdBAEl}}c{AGNj{CEl}}}{{AGNj{BOdBAEl}}}{{IMj{CHf}}}}\",[\"\"]]Ci[\"{{{AGNj{BOdNNd}}{AGNj{{KGj{Lh}}}}}{{KMj{CJfIOn}}}}\",[]]Cj[\"{{{AGNj{BOdAHNd}}{AGNj{{KGj{Lh}}}}}{{KMj{CJfIOn}}}}\",[]]Cj[\"{{{AGNj{BOdNNf}}{AGNj{{KGj{Lh}}}}}{{KMj{CJfB@Hn}}}}\",[]]Ck[\"{{{AGNj{BOdAHNf}}{AGNj{{KGj{Lh}}}}}{{KMj{CJfB@Hn}}}}\",[]]n[\"{NjCJf}\",[]]Bn[\"{{{AGNj{BOdBAEj}}KO`}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}KOb}{{AGNj{BOdBAEl}}}}\",[]]A`[\"{AGCnKM`}\",[]]Ca[\"{{{AGNj{BOdAHLh}}}{{KMj{{KM`{KO`}}AHLl}}}}\",[]]Ca[\"{{{AGNj{BOdAHLj}}}{{KMj{{KM`{KOb}}ANIh}}}}\",[]]Ak[\"{{{AGNj{AHIj}}}AHMj}\",[]]Ak[\"{{{AGNj{AHIn}}}AHMj}\",[]]Ak[\"{{{AGNj{AANd}}}AHMj}\",[]]Bg[\"{{{AGNj{CHh}}}{{KMj{AHMjAHI`}}}}\",[]]Ak[\"{{{AGNj{AANf}}}AHMj}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]B`[\"{{KO`c}KO`{{CGl{KO`}}}}\",[\"\"]]B`[\"{{KMdc}KMd{{CGl{KMd}}}}\",[\"\"]]B`[\"{{KObc}KOb{{CGl{KOb}}}}\",[\"\"]]B`[\"{{KMfc}KMf{{CGl{KMf}}}}\",[\"\"]]20Ae[\"{{AAMhCFb}ACf}\",[]]Bn[\"{{{AGNj{BOdBAEj}}ACd}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}ACd}{{AGNj{BOdBAEl}}}}\",[]]A`[\"{JBhAAMh}\",[]]B`[\"{{}{{AGNj{{KGj{BCOb}}}}}}\",[]]An[\"{{AGNj{OOd{BOdLh}}}HCj}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{KM`{{Ll{NNb}}}}}}\",[]]Bn[\"{{{AGNj{BOdB@Fb}}CJf}{{AGNj{BOdB@Fb}}}}\",[]]Bn[\"{{{AGNj{BOdB@Fd}}CJf}{{AGNj{BOdB@Fd}}}}\",[]]Aj[\"{{{AGNj{AGEf}}}CFb}\",[]]Aj[\"{{{AGNj{AGEh}}}CFb}\",[]]A`[\"{AB@bKM`}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]10Bf[\"{{{AGNj{CEl}}}{{KMn{CFbAOKj}}}}\",[]]Bm[\"{{{AGNj{AAMl}}{Md{B@Dj}}}{{KMl{HCj}}}}\",[]]Ai[\"{AHIl{{KMn{HCj}}}}\",[]]Ci[\"{{{AGNj{NN`}}B@DlAOL`{AGNj{Nj}}}{{KIl{AHMjAAMj}}}}\",[]]Cb[\"{{{AGNj{BOdAHJ`}}KMfAHL`}{{KMj{AHL`BCJj}}}}\",[]]Am[\"{{{AGNj{AHIn}}CFb}HCj}\",[]]B`[\"{{{AGNj{BOdAHJ`}}CFb}HCj}\",[]]A`[\"{B@HfCJf}\",[]]B`[\"{{{AGNj{BOdJBh}}B@Hh}HCj}\",[]]Aa[\"{AMKnAGE`}\",[]]Bm[\"{{{AGNj{NNb}}AGGb}{{KMl{{Md{B@Dj}}}}}}\",[]]Bn[\"{{{AGNj{ANNb}}AGGb}{{KMl{{Md{B@Dj}}}}}}\",[]]Bn[\"{{{AGNj{AMKh}}AGGb}{{KMl{{Md{B@Dj}}}}}}\",[]]Ch[\"{{{AGNj{BOdNNd}}{AGNj{BOd{Oh{Lh}}}}}{{KM`{CGn}}}}\",[]]Ci[\"{{{AGNj{BOdAHNd}}{AGNj{BOd{Oh{Lh}}}}}{{KM`{CGn}}}}\",[]]Ch[\"{{{AGNj{BOdNNf}}{AGNj{BOd{Oh{Lh}}}}}{{KM`{CH`}}}}\",[]]Ci[\"{{{AGNj{BOdAHNf}}{AGNj{BOd{Oh{Lh}}}}}{{KM`{CH`}}}}\",[]]Cb[\"{{{AGNj{ABA`}}HChAHMnAAMh}{{KMj{HCjBAEd}}}}\",[]]Cb[\"{{{AGNj{AOHj}}HChAHMnAAMh}{{KMj{HCjBAEd}}}}\",[]]Cb[\"{{{AGNj{ANKl}}HChAHMnAAMh}{{KMj{HCjBAEd}}}}\",[]]Cb[\"{{{AGNj{ABAb}}HChAHMnAAMh}{{KMj{HCjBAEf}}}}\",[]]Cb[\"{{{AGNj{AOHl}}HChAHMnAAMh}{{KMj{HCjBAEf}}}}\",[]]Cb[\"{{{AGNj{ANKn}}HChAHMnAAMh}{{KMj{HCjBAEf}}}}\",[]]Al[\"{{}{{KMj{ANHbBDFb}}}}\",[]]Bm[\"{{{AGNj{AHIn}}KMd}{{KM`{{Md{AHJb}}}}}}\",[]]Bo[\"{{{AGNj{AHJ`}}KMf}{{KM`{{AGNj{AHJd}}}}}}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]Aj[\"{{{AGNj{AHIn}}}HCj}\",[]]Am[\"{{{AGNj{BOdAHJ`}}}HCj}\",[]]Be[\"{{{AGNj{AHHf}}}{{KMj{NjIOh}}}}\",[]]Bg[\"{{{AGNj{AHHf}}}{{KMj{AGCnIOh}}}}\",[]]Be[\"{{{AGNj{AHHf}}}{{KMj{NlIOh}}}}\",[]]Be[\"{{{AGNj{AHHf}}}{{KMj{NnIOh}}}}\",[]]Be[\"{{{AGNj{AHHf}}}{{KMj{MhIOh}}}}\",[]]Bg[\"{{{AGNj{AHHf}}}{{KMj{AALnIOh}}}}\",[]]Aj[\"{{{AGNj{B@I`}}}KIn}\",[]]Cf[\"{{{AGNj{BOdBAEj}}{KM`{AMJf}}}{{AGNj{BOdBAEj}}}}\",[]]Cf[\"{{{AGNj{BOdBAEl}}{KM`{AMJh}}}{{AGNj{BOdBAEl}}}}\",[]]<;Aj[\"{{{AGNj{AAMl}}}KIn}\",[]]Aj[\"{{{AGNj{AAMn}}}KIn}\",[]]Bc[\"{{{AGNj{AHIn}}AHMjAHKn}AAOf}\",[]]Co[\"{{{AGNj{BOdAHJ`}}AHMjAHL`NLj}{{KMj{{KIl{KMfCJf}}AGEl}}}}\",[]]Bn[\"{{{AGNj{BOdBAEj}}CF`}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}CF`}{{AGNj{BOdBAEl}}}}\",[]]Ai[\"{AHIl{{KMn{HCj}}}}\",[]]Bg[\"{{{AGNj{B@I`}}AHNl}{{KMl{KIn}}}}\",[]]A`[\"{AGCnCFb}\",[]]10Ai[\"{{{AGNj{Nj}}}BAEh}\",[]]Ce[\"{{{AGNj{BOdANMd}}{Ll{BAEj}}}{{AGNj{BOdANMd}}}}\",[]]Ce[\"{{{AGNj{BOdANHd}}{Ll{BAEj}}}{{AGNj{BOdANHd}}}}\",[]]Ce[\"{{{AGNj{BOdANMh}}{Ll{BAEl}}}{{AGNj{BOdANMh}}}}\",[]]Ce[\"{{{AGNj{BOdANHh}}{Ll{BAEl}}}{{AGNj{BOdANHh}}}}\",[]]Aa[\"{ANMdBDMj}\",[]]Aa[\"{ANMhBDMl}\",[]]Bi[\"{{{Oh{B@Dl}}AOL`}{{KMj{ANMdJ@b}}}}\",[]]Bi[\"{{{Oh{B@Dl}}AOL`}{{KMj{ANMhJ@d}}}}\",[]]Co[\"{{{AGNj{BOdAHMd}}{AGNj{BOd{KGj{IMl}}}}}{{KMj{HCjAHNn}}}}\",[]]A`[\"{AGFdCFb}\",[]]A`[\"{AGFfCFb}\",[]]A`[\"{BCCfCFb}\",[]]A`[\"{BCChCFb}\",[]]Bc[\"{{{AGNj{AHIn}}}{{KM`{KIn}}}}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]10Aj[\"{{{AGNj{AGDb}}}KIn}\",[]]Be[\"{{{AGNj{AAMl}}{KM`{ANMd}}}HCj}\",[]]Bn[\"{{{AGNj{BOdAAMn}}{KM`{{Ll{ANMh}}}}}HCj}\",[]]Bd[\"{{{AGNj{BCOb}}}{{KM`{AOKn}}}}\",[]]o[\"{{}AGNj}\",[]]n[\"{{}NKf}\",[]]0000Bn[\"{{{AGNj{BOdBAEj}}KIn}{{AGNj{BOdBAEj}}}}\",[]]Bn[\"{{{AGNj{BOdBAEl}}KIn}{{AGNj{BOdBAEl}}}}\",[]]o[\"{CFbKO`}\",[]]o[\"{CFbKOb}\",[]]A`[\"{AOIlCFb}\",[]]Bo[\"{{{AGNj{BOdB@Fb}}AAMh}{{AGNj{BOdB@Fb}}}}\",[]]Bo[\"{{{AGNj{BOdB@Fd}}AAMh}{{AGNj{BOdB@Fd}}}}\",[]]Ae[\"{{KInCFb}AOHj}\",[]]Ae[\"{{KInCFb}AOHl}\",[]]A`[\"{AHJjCFb}\",[]]A`[\"{AHJlCFb}\",[]]6Bg[\"{{{AGNj{AHJ`}}KMf}{{KM`{AHL`}}}}\",[]]An[\"{{{AGNj{AHIn}}IMl}ANLn}\",[]]Am[\"{{{AGNj{AHIn}}KO`}HCj}\",[]]B`[\"{{{AGNj{BOdAHJ`}}KOb}HCj}\",[]]Cd[\"{{{AGNj{BOdB@Fb}}{Oh{CF`}}}{{AGNj{BOdB@Fb}}}}\",[]]Cd[\"{{{AGNj{BOdB@Fd}}{Oh{CF`}}}{{AGNj{BOdB@Fd}}}}\",[]]Fg[\"{{{AGNj{BEA`}}{AGNj{B@Dl}}{AGNj{{KGj{B@Dl}}}}{AGNj{AHMh}}{AGNj{{KGj{Lh}}}}ABBb}{{KMj{BCJdIOf}}}}\",[]]Af[\"{{BADlNLj}BADl}\",[]]Af[\"{{BADnNLj}BADn}\",[]]?Al[\"{ANNd{{KMj{CIbJ@`}}}}\",[]]Ak[\"{ANNd{{KMj{MjJ@`}}}}\",[]]Am[\"{ANNd{{KMj{AB@fJ@`}}}}\",[]]Ai[\"{ANNd{{KN`{CId}}}}\",[]]Ah[\"{ANNd{{KN`{Ml}}}}\",[]]Aj[\"{ANNd{{KN`{AB@h}}}}\",[]]o[\"{NKfNKf}\",[]]Bo[\"{{{AGNj{BOdBCHn}}AAMh}{{AGNj{BOdBCHn}}}}\",[]]Bo[\"{{{AGNj{BOdBCI`}}AAMh}{{AGNj{BOdBCI`}}}}\",[]]Bb[\"{{{AGNj{NNd}}}{{KM`{CJf}}}}\",[]]Bc[\"{{{AGNj{AHNd}}}{{KM`{CJf}}}}\",[]]Bb[\"{{{AGNj{NNf}}}{{KM`{CJf}}}}\",[]]Bc[\"{{{AGNj{AHNf}}}{{KM`{CJf}}}}\",[]]Bg[\"{{{AGNj{AHIn}}}{{KMj{HCjBACh}}}}\",[]]Cf[\"{{{AGNj{BOdBAEj}}{KM`{AAMh}}}{{AGNj{BOdBAEj}}}}\",[]]Cf[\"{{{AGNj{BOdBAEl}}{KM`{AAMh}}}{{AGNj{BOdBAEl}}}}\",[]]A`[\"{AGFdCFb}\",[]]A`[\"{AGFfCFb}\",[]]Bl[\"{{{AGNj{BOdAHJb}}NLjNLjCJfCJfCFb}HCj}\",[]]Bj[\"{{{AGNj{BOdLn}}NLjNLjCJfCJfCFb}HCj}\",[]]Bk[\"{{{AGNj{BOdINh}}NLjNLjCJfCJfCFb}HCj}\",[]]Bk[\"{{{AGNj{BOdNMb}}NLjNLjCJfCJfCFb}HCj}\",[]]Bl[\"{{{AGNj{BOdAHJd}}NLjNLjCJfCJfCFb}HCj}\",[]]Bj[\"{{{AGNj{BOdM`}}NLjNLjCJfCJfCFb}HCj}\",[]]Bk[\"{{{AGNj{BOdINj}}NLjNLjCJfCJfCFb}HCj}\",[]]Bk[\"{{{AGNj{BOdNMd}}NLjNLjCJfCJfCFb}HCj}\",[]]A`[\"{AOIlCFb}\",[]]Bk[\"{{{AGNj{AHJ`}}KMf}{{KMj{AHMjAHIf}}}}\",[]]Ak[\"{{{AGNj{NOb}}Ml}CFb}\",[]]Bi[\"{{{AGNj{BOdAHMb}}CFb}{{KMn{HCj}}}}\",[]]=bBi0bCe00bFnbIdbOkb[]beq000000000000000000000000000000000000000000000000000000000000000000000000bid000000000000brtbtybu8cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dPath00dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop0000dfrom00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash0000000000000000000dinto0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnextdopen00dpath0dperfdping00dpoll0000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop0dtake00000du128dunitdvzip00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCodeceCubic00eDebugeDerefeError00000000000eEvent00eOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteearrayebenchebuild00000000ebytes00000ecauseeclone000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerror0eframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats00000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosed0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStreamfStringfTypeIdfVarInt0faccept000fborrow0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode00000000000fdst_ipfencode0000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000frebindfrefuse0fremote0frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx0fudp_tx0fvarintfverify0fwindow0000000gAeadKey0gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgNewReno00gOpenUnigPathBufgReadBufgRequestgRuntime0gSession0gStopped0000gStreamsgToOwnedgTooLonggTryFromgTryIntogWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000giroh_hp0gis_0rttgmetrics0000000gmin_mtu0gon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgradegversion00000hAcceptBihBytesMuthChacha20hCommandshDatagramhDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hLayerExthLongTypehMAX_SIZE0hNO_ERROR0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut0hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll00000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiCommonOptiConnectediDatagramsiFormatteriFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0ion_closediopen_path0ipath_acks0iping_pathipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint00000istable_idito_string000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code00000jframe_type0jinto_inner0jiroh_quinnjis_drained0jlocal_addr0jlost_bytes0jnew_senderjpad_to_mtu0jpath_stats00jpoll_closejpoll_flush0jpoll_write00jprint_jsonjquinn_perfjread_chunkjread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kQlogFactory0kSERVER_ADDRkSERVER_NAMEkSmolRuntimekStreamEvent0kStreamStats0kUnspecifiedkaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kinitial_mtu000kinitial_rtt00kinto_future000000000000kmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_chunkskread_to_endkrecv_stream0ksample_size0ksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handlekwith_crypto0kwith_prefix0kwrite_chunklAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelLOCALHOST_V4lLastOpenPathlNatTraversallNoneTokenLog0lObservedAddr0lReadDatagramlRemoteStatus0lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000lexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets0lmax_incoming0lmay_fragment00lnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lsegment_size00lsend_streamslsent_packets0lset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunks0mAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeTypemINVALID_TOKEN0mInvalidHeadermLocallyClosed000mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPossibleValuemPrivateKeyDermReadableError0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_read_bufmpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000mtry_size_hint0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidMessage00nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenaead_from_hkdf0nconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000niroh_quinn_udpnis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset0nremote_address0000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose0oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoOpenStreamStatsoProtectedHeader0oQlogFileFactory0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`ApplicationClose0A`CertificateErrorA`ConnectionClosed0A`ConnectionHandleA`DatagramReceivedA`EndpointStopping0A`FINAL_SIZE_ERROR0A`InconsistentKeys00A`KEY_UPDATE_ERROR0A`MaxPathIdReachedA`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailedA`VersionNegotiate0A`check_and_insert00000A`configure_clientA`congestion_state0A`connection_close0A`force_key_update0A`from_arg_matches00000A`iroh_quinn_protoA`max_gso_segmentsA`max_idle_timeout0A`max_streams_bidi0A`open_connections0A`open_path_ensure0A`packet_threshold0A`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed0AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaPacketDecodeError0AaSendDatagramError0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedRead0AbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcMaxRecursionReachedAcRemoteCidsExhaustedAcTransportParametersAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest00000Acaugment_subcommandsAcblack_hole_cooldown0Acearly_data_accepted000Achandshake_confirmedAckeep_alive_interval0Aclost_plpmtud_probes0Acon_congestion_event0000000Acoutgoing_handshakesAcpath_remote_addressAcremote_open_streamsAcsend_data_on_streamAcsent_plpmtud_probes0Acstream_data_blocked0Acstreams_blocked_uni0AdFRAME_ENCODING_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress00AdNoInitialCipherSuite0AdServerSideNotAllowedAdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_frequency_config0Adblack_holes_detected0Adfrom_arg_matches_mut00000Adhandle_client_streamAdincoming_buffer_size0Adis_multipath_enabledAdmax_receive_segments0Admax_udp_payload_size00Admtu_discovery_config0Adpoll_endpoint_eventsAdpreferred_address_v40Adpreferred_address_v60Adread_certs_from_fileAdreordering_threshold0Adretire_connection_id0Adretry_token_lifetime0Adset_max_idle_timeoutAdset_recv_buffer_sizeAdset_send_buffer_sizeAdstreams_blocked_bidi0Adtransport_parameters00000AeConnectionIdGenerator0AeDigitallySignedStructAeNoApplicationProtocol0AeNoDefaultClientConfig0AePATH_UNSTABLE_OR_POOR0AeValidationTokenConfig0Aeconfidentiality_limit0Aeincoming_buffer_bytesAelocal_address_changedAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_platform_verifier0Afwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgset_keep_alive_intervalAgupdate_from_arg_matches00000Agvalidation_token_config0AhAPPLICATION_ABANDON_PATH0AhNoProtectionClientConfigAhNoProtectionServerConfigAhget_max_udp_payload_size0Ahinitial_dst_cid_provider0Ahnew_with_abstract_socketAhremote_address_validated0Ahset_max_concurrent_pathsAhsupported_verify_schemesAiCONNECTION_ID_LIMIT_ERROR0AiEncryptedClientHelloErrorAiExportKeyingMaterialError0AiInvalidCertRevocationList0AiNO_CID_AVAILABLE_FOR_PATH0AiTRANSPORT_PARAMETER_ERROR0AiUnsupportedOperationErrorAiadd_nat_traversal_address0Aidatagram_send_buffer_size0Aigenerate_self_signed_certAiopen_bidirectional_streamAipath_retire_connection_id0Aiset_default_client_configAiset_path_max_idle_timeoutAjDEFAULT_SUPPORTED_VERSIONSAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Alconfigure_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/234/docs/search.index/normalizedName/87730ecd4586.js b/pr/234/docs/search.index/normalizedName/87730ecd4586.js new file mode 100644 index 000000000..176adb2c6 --- /dev/null +++ b/pr/234/docs/search.index/normalizedName/87730ecd4586.js @@ -0,0 +1 @@ +rd_("b()b->bbi0bce00bfnbidbokb[]beq0000000000000000000000000000000000000000000000000000000000000000000000003333333333333brtbtybu8canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecA`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]0Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]>Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]3Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]8C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]A`[5,\"iroh_quinn\"]Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Eo[6,\"iroh_quinn_proto::connection::streams::recv\",\"iroh_quinn_proto::connection::streams::recv\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]Bc[6,\"rustls::error\",\"rustls::error\"]A`[5,\"iroh_quinn\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]A`[6,\"iroh_quinn\"]=Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]3Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]8Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"];C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]=Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Bi[5,\"core::num::error\",\"core::num::error\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]9A`[5,\"iroh_quinn\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]Cc[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]11B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]105Cm[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token_memory_cache\"]Bh[15,\"iroh_quinn_proto::ProtectedHeader\"]f[3,\"\"]Am[10,\"iroh_quinn::congestion\"]Db[10,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion\"]Al[5,\"iroh_quinn::congestion\"]Da[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion\"]Ai[10,\"iroh_quinn::crypto\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Bi[6,\"iroh_quinn\",\"iroh_quinn::connection\"]D`[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::datagrams\"]:A`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Cb[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]21Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Be[5,\"rustls::verify\",\"rustls::verify\"]Ae[10,\"rustls::verify\"]Do[6,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]7Af[5,\"iroh_quinn_proto\"]Bj[15,\"iroh_quinn_proto::PacketDecodeError\"]Ah[5,\"iroh_quinn::crypto\"]Ci[5,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]?Aa[6,\"perf\",\"perf\"]Ak[5,\"arbitrary\",\"arbitrary\"]Ee[5,\"iroh_quinn_proto::transport_parameters\",\"iroh_quinn_proto::transport_parameters\"]Bh[15,\"iroh_quinn::crypto::rustls::Error\"]Bn[15,\"iroh_quinn_proto::crypto::rustls::Error\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Be[6,\"rustls::suites\",\"rustls::suites\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::varint\"]Be[6,\"rustls::webpki\",\"rustls::webpki\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]Af[2,\"iroh_quinn_proto\"]Aa[10,\"iroh_quinn\"]Ci[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::cid_generator\"]Be[5,\"rustls::verify\",\"rustls::verify\"]7Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ao[5,\"certificate\",\"certificate\"]Bc[6,\"rustls::error\",\"rustls::error\"]4Bm[5,\"perf::noprotection\",\"perf::noprotection\"]01Ah[5,\"iroh_quinn::crypto\"]Ci[5,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"rustls::crypto::cipher\",\"rustls::crypto::cipher\"]Ch[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::cid_generator\"]07Bh[15,\"iroh_quinn::crypto::rustls::Error\"]Bn[15,\"iroh_quinn_proto::crypto::rustls::Error\"]") \ No newline at end of file diff --git a/pr/234/docs/search.index/root.js b/pr/234/docs/search.index/root.js new file mode 100644 index 000000000..717804a33 --- /dev/null +++ b/pr/234/docs/search.index/root.js @@ -0,0 +1 @@ +rr_('{"normalizedName":{"I":"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","N":"ACAl","E":"OjAAAAAAAAA=","H":"h3MOzUWG"},"crateNames":{"N":"j","E":"OjAAAAAAAAA=","H":"ijgfzyvU"},"name":{"N":"ACAl","E":"OjAAAAAAAAA=","H":"0QCenBGJ"},"path":{"N":"B@a","E":"OzAAAAEAABoRnwACAAQACQAAAAsAVwBoAAAAbgAEAHkAAQB9ABsAmwDSAHABCAB6ATIAsQEAALUBBQDBAQAAxAEAAMYBAADMAQUA1AETAukDAADrAxAA/QMCAAEEAwAGBAoAEgQIAB0EzgD1BAAABwUBAAoFAwAPBQAAEQUAABkFiQCkBQEApwUAAKkFCACzBQgAvQUCAMMFBwDPBQIA0wUCANkFAgDdBQAA5QUBAPEFAwD5BdoA1QYEANsGAADdBgAA4AY5ABsHDQArBwMAMAcDADUHFABLBwkAVwcCAF8HAQBjBwAAagcAAHQHAwB6BwAAfwdmAOcHEQD7BwYAAwjfAOUIAADpCAEA8AgAAPUIAwD9CAAAAQkAAAQJAQAJCQEAEgkFABoJGAA0CRQASglHApMLAACZCwEAnwsAAKQLAACoCwEAswsAALoLCQDFCwcAzgslAPULRwA+DAMARgwAAEgMAQB4DE0ByA0CAM0N2wCqDhIAvg4GAMYOAADVDgEA2Q4BAOEOAwDmDj8AJw8CACsPEwBFDwEASA8CAFIPAQBXDwUAYA8AAGMPAAByDzAApA8fAMYPAwDODwAA0g8CANYPAwDmDwMA6w8CAO8PNAAqEAUAMxABADcQAwA+EAEARBABAEgQIABqEC4AnRAAAK8QAQCyEAMAtxCQAEsRAQBOEQQAVBEEAFoRAQBiEQEAZRETAHoRHQCeEQEApBENALMRAgC4EQMAvREIAMgRAwDNEQMA2BEAANoRAQDdERYA9RECAPoRAAD8ESsAKhIEADESAAA3EiUAXhIEAGYSBQBuEhcAiBIQAJsSFwC1EgoAwxIFAMoSEwDfEgMA5BIWAP4SHQA=","H":"aquQ3VTf"},"entry":{"N":"ABAn","E":"OjAAAAEAAAAAAP0AEAAAAAAAAQAHAAgACgBjAGQAZQBpAGoAcwB3AHgAewB8AJkAmgBuAW8BeQGtAa4BrwGwAbIBswG0AbsBvAG9AcABwgHFAccBygHLAdIB6AP8AwAEBQQRBBsEHATsBO0E8ATxBPYE9wT4BPkE+gT7BPwE/QT+BP8EBAUJBQ4FEAUSBRUFFgUXBaMFpgWoBbIFvAXABcEFwgXLBcwFzQXOBdYF1wXcBd4F3wXgBeEF4wXkBecF6AXrBewF7QXuBe8F9QX2BdQG2gbcBt4G3wYaBykHKgcvBzQHSgdaB1sHXAddB14HZAdnB2wHbQduB28Hcwd5B3sHfAfmB/kH+gcCCOQI6AjrCOwI7wjzCPsI/Aj+CP8IBgkQCREJGAkZCTMJSQmSC5ULlguXC5gLnQuhC6cLrAuuC68Ltgu4C7kLxAvNC/QLPQxCDEQMRQxHDEsMTQxSDFMMVgxXDFkMWgxbDFwMXQxeDGIMaAxrDGwMbQxuDMYNxw3LDcwNyQ7MDtQO3A7gDiYPKg8/D0APTA9PD18PYg9kD2cPaA9tD24PcQ+jD8oP0Q/VD9wP3g/fD+AP4g/qDyUQJhAoECkQMhA2ED0QoRCjEKcQqBCrEK4QthBKEVMRWRFkEaARzBHREdIR0xHXEfQR+REoEikSMhI1El0SZRKZEpoSwBLJEvwS/RI=","H":"GltXGL+T"},"desc":{"N":"FHh","E":"OzAAAAEAAJMMZQEAAAEABwABAAoASQBhAAAAYwACAGgAAwBzAAEAdgACAHsAFQCZANMAbgEBAHkBAgCbAQQAowEBAKYBAACrAQkAuwECAMABAADCAQAAxQEAAMcBAADKAQEAzgEBANIBAQDYAQQA8gEAAPsBAAD9AQIAAgIBAAgCAAAKAgIAEwIAABUCAwAbAgAAIwIAACUCAQAsAgAAMgIBADYCAAA4AgAAPAIAAEECAABOAgAAVgIAAFgCCQBzAgQAfQIAAJACAACTAgAAmQIAAKACAQCjAgAAqAIAALECAAC2AgAAuAIAAMACAADEAgAAyQIAANECCQDcAgAA5gIAAPcCEwDfAwQA6AMAAOoDAQDtAwAA7wMLAPwDAAAABAAAAgQAAAUEAAAOBAAAEQQAABYEAQAZBNQA8AQBAPUECgAEBQAACQUAAA4FAAAQBQAAEgUGABoFAgAeBQMAKQV1AKMFAwCoBQAAsQUBALQFAgC8BQAAvgUFAMgFAADLBQMA0wUBANYFAQDcBQAA3gUDAOMFAQDnBQEA6wUEAPUFAQD9BdMA1AYBANcGAQDaBgAA3AYAAN4GAQDiBgAA5gYAAOkGBwDyBgEA9gYJAAMHAAAKBwEADQcBABMHAgAXBwMAKQcCAC0HAAAvBwEAMgcLAEMHAQBKBwAATgcCAFIHAgBaBwQAZAcAAGcHAABqBwAAbAcDAHMHAAB5BwAAewcBAIIHAACFBxkApQcwANcHAQDmBwAA6gcCAO4HAgD0BwAA9gcAAPkHAQACCAAABAgAAAgI0wDkCAIA6AgAAOsIAQDvCAAA8wgAAPsIAQD+CAEABgkAABAJAQAVCQQAJAkAAC0JBAAzCQAANwkCADsJAgBGCQEASQkBAFYJoAD4CQAA+wlIAEcKRQGRCwEAlQsDAJ0LAQChCwAApwsAAKwLAACuCwEAtgsAALgLAQC7CwkAxgsCAMoLAwDRCwEA4wsDAOoLAQDtCwEA8gsCAPgLAAD6CwcAAww2AD0MAABCDAAARAwBAEcMAABLDAAATQwAAFIMAQBWDAEAWQwFAGIMAABoDAAAawwDAHsMSQHGDQEAyw3TALAOAAC2DgMAuw4CAMMOAQDGDgMAzA4AANQOAADZDgEA3A4AAN4OAgDiDgMA9Q4OAAUPAQAIDwAACg8BAA4PEwAmDwAAKg8AAD8PAQBMDwAATw8AAFoPAABfDwAAYg8AAGQPAABnDwEAbQ8BAHEPBQB5DwEAfg8CAIIPAACEDwAAiw8BAJIPAQCWDwQAog8CAKcPAQCsDwEAuA8DAMoPAADRDwAA1Q8AANwPAADeDwIA4g8AAOcPAQDqDwAA7g8AAPAPAgD4DwIA/A8CAAAQBgANEAAAGRABAB0QBQAlEAEAKBABADIQAAA2EAAAPRAAAEMQAABMEAAAThAAAFAQAABVEAAAVxADAFwQAABeEAAAYBAAAGMQAABlEAAAZxAAAGsQAABuEAAAcBAAAHIQAAB2EAAAeBACAH4QAACAEAAAjBABAJQQAACXEAEAoRAAAKMQAACnEAEAqxAAAK4QAAC2EHYAMxEEADkRAAA/EQEAQhEBAEURAABHEQAAShEAAFMRAABZEQAAZBEAAGcRAQBqEQIAbxEBAHMRBQB9EQEAhREAAIoRAACgEQAAqxEDALIRAQDJEQAAzBEAANERAgDXEQAA3REEAOsRAQD0EQEA+BEBAAASBgAKEgAADBIAABMSAgAXEgIAHRIAACASAwAoEgEAMhIAADUSAAA7EgYAURIAAFQSAQBbEgIAXxIAAGISAABlEgAAeBIBAIcSAQCLEgAAjRIAAJESAwCZEgEAoRIEAKkSBQCzEgEAvRIAAMASAADJEgAAzhIDANoSAADoEgYA9BIAAPYSAQD8EgEABhMAAAoTAAA=","H":"8Qzmi4wx"},"function":{"N":"A@Ak","E":"OzAAAAEAAAADVgAAAAoAYwAZAJkAAQBuAQEAeQEAAK0BIADSAQEA6AMAAOoDAAD8AwAAAAQAAAUEAAARBAAAGwQBAOwELACjBQAApgUAAKgFAACyBQAAvAUAAMAFAwDIBQAAywUDANIFJgDUBgAA1wYAANoGAgDeBgIA5AYAABoHAAApBwYAMgcAADQHAABKBwAAVQcqAOYHAQD5BwEAAggAAOMILwAYCQEAMwkAAEkJAACSCycAxAsAAM0LAAD0CwAAPQwAAEIMAwBHDDAAxg0DAMsNAQCpDgAAvQ4AAMUOAADHDhEA2w4FAOUOAAAmDwAAKg8AAD4PGwBbDxYAow8BAMIPIwDqDwAA7g8AACMQJABpEAAAmRAdAEgRHQB5EQAAlBERALIRAAC0ERQAyhESAPQRAAD4EQYAJBISAF0SAABgEgAAYxIKAH0SCgCZEgUAsRIDAL4SCwDeEgUA+xIEAA==","H":"b09M/wRv"},"type":{"N":"AIi","E":"OzAAAAEAAIIRhgACAAQACQAAAAsAVwBoAAAAbgAFAHcAAAB5AAEAfAAcAJsA0gBvAQkAegEyALEBAQC0AQgAwQEAAMQBAADGAQAAywEGANMBRwIdBM4A8AQAAPUEAgD8BAEABAUAAAcFAQAKBQMADwUAABEFAAAYBaMAvQUPAM8FAgDTBQMA2QUCAN0FAADlBQEA8QUDAPkFWwFXBwIAXAcEAGMHAABqBwAAdAcDAHkHAwB/B3kA+gfoAOUIAADoCAIA8AgAAPMIAAD1CAMA/QgAAAEJAAAECQEACQkBABIJfwKTCwAAlQsCAJkLAQCdCwAAnwsAAKQLAACoCwEArAsAAK4LAACzCwAAtgsAALkLOgD1C0wARAwCAEgMAQBLDAAAUgwBAFYMAABdDAAAbQwAAHgMTALGDgAA1A4CANkOAQDcDgAA4Q5dAEUPAQBIDwIAUg8BAFcPBQBgDwAAYw8AAHIPUQDGDwQAzg8AANIPAgDWDwMA5g89ACgQAAAqEAUAMhACADcQAwA+EAEARBABAEgQUACdEAAAoxAAAK8QAQCyEJUASxEBAE4RBABUEQQAWhEBAGIRAQBlETIAnhECAKQREQC4EQMAvREIAMgRAwDNEQMA0hEAANgRAADaEQEA3REaAPoRAAD8ESsAKhIEADESAAA3EisAZhIFAG4SFwCIEhAAmxIXALUSCgDDEgUAyhITAN8SAwDkEhYA/hIdAA==","H":"KqnPdH/P"},"alias":{"N":"`","E":"OzAAAAEAABsTAQAAABsT","H":"p2IVDFMs"},"generic_inverted_index":{"N":"b","E":"OjAAAAAAAAA=","H":"YYPFENJB"}}') \ No newline at end of file diff --git a/pr/234/docs/search.index/type/2aa9cf747fcf.js b/pr/234/docs/search.index/type/2aa9cf747fcf.js new file mode 100644 index 000000000..bfa1cfb8d --- /dev/null +++ b/pr/234/docs/search.index/type/2aa9cf747fcf.js @@ -0,0 +1 @@ +rd_("CAc[\"\",\"ADswAAABAAAFAAIA3wMEAO4SAAACBwcAACUHAAA6MAAAAQAAAAAACgAQAAAAqwGsAbYFQAw5EXERhRHPEtoS9xIGEzowAAABAAAAAAAWABAAAADYAdkB2gHbAdwB7AO1BboF2AYDBzAHSgn6DzwRchHwEXES8xL0EvUS9hIJEw0TOzAAAAEAAAUBEAC0BQAACAcAACQHAAAzBwAA/AsFAE8NdQCODwAA+w8HAFEQAAC3EHUAPREAAD8RAACwEQAA8REAAA0SAADSEgAAOjAAAAEAAAAAABwAEAAAALcFvQW+Bb8FAQcCBwkHCgcLBwwHDQcOB44LjwuQC5EL2Au8DsAOtg8SEDgRHRJZEloSyhLOEtsS+BI7MAAAAQAANwAUAPcCEwAHBAAAEwQBAJ8FAQCiBQAAuAUBANEFAAD2BgoA8wcDAI0LAADsCwAACw8AAD0PAAC3DwAAIRABAEoQAACxEQAAURIAAMsSAAD5EgAAOzAAAAEAAFUADAC7ADwAXQEBAAYEAACiBwAApAcAAOMHAADlBwAAgQ8DAGYRBQBBEgAAqRIFALwSAAA7MAAAAQAAFQAHAO4DAABBDAAAxQ0AAAcPAwA6EQAAEhIHAOgSBQA7MAAAAQAAEQAIAAwEAQASBwcA4gcAAOQHAAC2DgIAvw4AAA0QAACTEQAAA0sHAABMBwAAghEAAAMDBAAABAQAAPELAAAFoAcAAKEHAACjBwAA7QsAAAwQAAAAOzAAAAEAAAUAAgCSAQAAiRIEAAAAAfwFAAA=\"]Bo[\"AAAAAAAAAAACtxIAALgSAAAAAAAAAusPAADsDwAA\",\"\"]0Bg[\"\",\"AAAFLQkAAC4JAAAvCQAAMAkAADEJAAA=\"]AIg[\"AAAAAAKDAQAAkwEAAAAABJ8FAACgBQAASBAAAEkQAAAEogcAAKQHAADjBwAA5QcAADswAAABAAANAAUAzwUBAFIJAQD3CwQAQQwAAH0QAwA6MAAAAQAAAAAADAAQAAAAiwEMBA0E4gfkB78OwA/BD28QcBBxEHIQkxEE/QMAAEsHAABMBwAAJA8AAAQDBAAABAQAAJMSAACUEgAABQIEAAChBwAAowcAALkOAAC7DgAAA+4LAADvCwAAug4AADowAAABAAAAAAAIABAAAACSAeUD5wPxBokSihKLEowSjRIB9REAAAHwCwAA\",\"AAAAAZgQAAAAAqQFAAClBQAAAALRCwAA0gsAAAAF9wsAAPgLAAD5CwAA+gsAAPsLAAAAAAAAAALlAwAA5wMAAA==\"]ANk[\"AAAEFgIAAHUCAADhBgAA5QYAAAPTCwAA1AsAANULAAACgwEAAJMBAAA7MAAAAQAAdQABALcQdQAABp8FAACgBQAASBAAAEkQAAAZEwAAGhMAAASiBwAApAcAAOMHAADlBwAAOzAAAAEAABIABwDPBQEAJgcAAFIJAQD3CwQAQQwAAH0QAwBEEQMAOjAAAAEAAAAAAAkAEAAAAIsBDAQNBOIH5Ae/Dm8QcBBxEHIQBf0DAAC7BQAASwcAAEwHAAAOEAAABAMEAAAEBAAAkxIAAJQSAAAEoQcAAKMHAAC5DgAAuw4AAAPuCwAA7wsAALoOAAA7MAAAAQAABgADAOUDAADnAwAAiRIEAAIEBwAA9REAAAAB/AUAAA==\",\"AAAAAAACpAUAAKUFAAAAAtELAADSCwAAASUPAAAF9wsAAPgLAAD5CwAA+gsAAPsLAAAAAAAAAALlAwAA5wMAAAAAAx0HAABYEAAAWRAAAA==\"]Dc[\"\",\"AAAAADswAAABAAAHAAEANgkHADswAAABAAAJAAIABxAEAFMQBAAAAVIQAAA=\"]AHi[\"AAAAOjAAAAEAAAAAAAcAEAAAAIABggGPAZEBnAGdATkPOg8EhgEAAJgBAAArCgAALgoAADowAAABAAAAAAAJABAAAACBAYcBiAGQAZkBmgGQCpIKlxKYEjswAAABAAAMAAMAGQUHAOEOAAC8DwMABhgQAAAaEAAAHBAAAB4QAAAgEAAAsREAADswAAABAAAVAAoAdgEBAH0BAAD1CwEAOgwBADEPAwCqDwEAixEDAEESAAC3EgEAABMBAAG5EgAAAYsBAAAFFxAAABkQAAAbEAAAHRAAAB8QAAAAAAAAAAAB/AUAAA==\",\"AAABewEAAAIhBQAALREAAAAAAAKwDgAAog8AAAAAAAAAAAAAAAAE4wYAAOcGAAA+DAAAPwwAAA==\",1]Fk[\"AAAABFMFAABOBwAA6gcAAFwQAAACNwkAAMYLAAAB/A8AAAAB9AcAAAHqAAAAAggPAAATEgAAARMHAAA=\",\"AAAAAVMFAAABhgEAAA==\"]Fk[\"AAAABJMFAABSBwAA7gcAAGMQAAACOwkAAMoLAAABABAAAAFfAQAAAfYHAAAAAgoPAAAXEgAAARcHAAA=\",\"AAAAAZMFAAABmAEAAA==\"]AEa[\"AAAAAn4BAACKAQAAOzAAAAEAAAcAAQA2CQcAAAS9BQAAvgUAAL8FAAA4EQAAAAABuRIAAAAAAALrDwAA7A8AAA==\",\"AAAAADswAAABAAAQAAMA2AYAADYJBwDFCwcAOzAAAAEAAAsABAAzBwAA7Q8AAAcQBABTEAQAOzAAAAEAAA8ABQAZBQcA4gsCAEERAgBtEQAAzhIAADowAAABAAAAAAAMABAAAAAYEBoQHBAeECAQSBBJEFIQdRB2EHcQeBBREgSBDwAAgg8AAIMPAACEDwAAAAAFFxAAABkQAAAbEAAAHRAAAB8QAAAAAAAAAAAB/AUAAA==\",1]Ec[\"AAAAAAAAOzAAAAEAAAUAAgDuBgIA8gYCADswAAABAAAGAAIA6QYEAB8JAQAD/wMAALAFAACxBQAA\",\"\"]Dg[\"AAAAAAAAAAABrRIAAAHsEgAAAAAAAAAAAAABigcAAA==\",\"AAAAAAABdxEAAAE/EgAA\"]Eo[\"AAAAAVEFAAABhQEAAAIlAAAAggAAAAETDwAAAfwCAAAC5QAAAOYAAAA=\",\"AAABkQAAAAFRBQAAAQESAAAAAb0LAAA=\"]Ic[\"AAAAAYwFAAADlwEAABwSAACOEgAAAkwAAACPAAAABk4BAABPAQAA5gMAAPsFAAAgDwAA2xIAAAEJAwAA\",\"AAABkgAAAAKMBQAABBIAAAABwQsAAAAAAAAAAAAAAAAAAAKTAAAAlAAAAA==\"]Ik[\"AAAAAikFAACKEQAAATAHAAAB1QYAAAGbAAAAAUoQAAABqRIAAAI6EQAA6BIAAAAAAAAAAAAAOjAAAAEAAAAAAAgAEAAAAMQF0waEBwMI7g4EDzUP7w+BEA==\",\"AAAAASkFAAAAAXMRAAABOxIAAA==\"]Ck[\"AAAAAasBAAAAAAAAAasSAAAB6hIAAA==\",\"AAAAAAABdREAAAE9EgAA\"]Ck[\"AAAAAawBAAAAAAAAAawSAAAB6xIAAA==\",\"AAAAAAABdhEAAAE+EgAA\"]Jk[\"AAAAAAAABLcFAAC9BQAAvgUAAL8FAAAAOzAAAAEAAAgAAQCECwgAOzAAAAEAAAwAAQDuAwwABLYOAAC3DgAAuA4AAA0QAAAAAfELAAAD7QsAALkOAAC7DgAAAu4LAAC6DgAA\",\"AAAAAAAB7Q8AAAPiCwAA4wsAAOQLAAA=\"]Mg[\"AAAAAAAAApERAACSEQAAAALyEQAA8xEAAAUmBwAARBEAAEURAABGEQAARxEAAAACuwUAAA4QAAACAwQAAAQEAAAAAAABBAcAAAAB/AUAAA==\",\"AAAAAAKzBQAAvRIAAAAF8RIAABQTAAAVEwAAFhMAABcTAAABURIAAAIlDwAA8hIAAAAAAAAAAAAAAASFDwAAhg8AAIcPAACIDwAA\"]Bg[\"AAAAAAAAAASVEAAAlhAAAHISAABzEgAA\",\"\"]Bk[\"\",\"AAAAAZQQAAAAAAAAAAAAAAAAAAAAAAHDDgAA\"]Cc[\"AAAAAAAAAbYPAAABtw8AAA==\",\"AAAAAAACTAkAAE0JAAA=\"]FHc[\"AAAAADswAAABAAASAAkA2AEEAOwDAADHBQAABQgAAM8LAACJDwMAchEAAHESAADzEgMAOzAAAAEAANgBEQD7AwAAAQQAAAgHAAC/CwMAewxJAScPAAArDwAAjg8AAKkPAAD7DwcAURAAALcQdQA9EQAAPxEAAPERAABMEgAA0hIAADswAAABAACrAA8AmwAfAPgAZABfAQ0A5gMAALcFAAD7BQAA7gYCAPIGAgABBwEAuwsDALQOAACADwAAHRIAADsSBQDbEgAAOzAAAAEAAJkAQAD3AhMA/gMAAAgEAQATBAEAogUAALgFAQDRBQAA6QYEAPYGCgDxBwUA3QgBAB8JAQA0CQEAPwkBANELAQDgCwEA7AsAAHkMAQCxDgEAwQ4BAO8OAQDyDgEACw8AACwPAQA2DwEAPQ8AAI8PAACdDwEAtA8BALcPAAATEAEAUhAAAF0QAABfEAAAYRAAAGQQAABmEAAAaBAAAHMQBQCCEAEAhRABAJUQAQAuEQEAgxEBALERAADiEQEA5xEBAAcSAQBCEgEAUhIBAFYSAQByEgEAehIBAJ8SAQCvEgEAyxICANYSAwDkEgMA+RIAAAcTAQALEwEADhMBABITAQAZEwEAOzAAAAEAAIoAJAC7ADwAXQEBAHYBAQDkAwAA/wMAAAYEAACwBQEAyQUBAKIHAACkBwAA4wcAAOUHAACECwgA9QsBADoMAQDpDgEAJQ8AADEPAwCqDwEA9Q8BAA8QAQA7EQAAexEBAIsRAwDyEQEADhIBADcSAQBKEgEATRIDAKkSBQC3EgEAvBIAAO8SAQDyEgAAABMDABATAQA7MAAAAQAAMQAOAO4DDADPBQEAJgcAAFIJAQDpCwAAQQwAAMUNAAC+DgAABw8DAH0QAwBEEQMAEhIHAEcSAQDoEgUAOjAAAAEAAAAAABUAEAAAAAwEDQQSBxMHFAcVBxYHFwcYBxkH4gfkB7YOtw64Dr8OwA/BDw0QkxHTEvoSB/0DAAC7BQAAJA8AAA4QAAB7EAAAfBAAAIIRAAADAwQAAAQEAADxCwAAOjAAAAEAAAAAAAkAEAAAAAIEoAehB6MH7Qu5DrsO6w/sDwwQA+4LAADvCwAAug4AADowAAABAAAAAAAHABAAAADlA+cD8QaJEooSixKMEo0SAQQHAAAB8AsAAAH8BQAA\",\"AAAAAAA7MAAAAQAA0wABAHsM0wAAOjAAAAEAAAAAAFkAEAAAAAgECQTxB/IH3QjeCDQJNQk/CUAJ0QvSC+AL4Qt5DHoMsQ6yDsEOwg7vDvAO8g7zDiwPLQ82DzcPnQ+eD7QPtQ8TEBQQXRBfEGEQZBBmEGgQcxB0EIIQgxCVEJYQLhEvEYMRhBHiEeMR5xHoEQcSCBJCEkMSUhJTElYSVxJyEnMSehJ7Ep8SoBKvErASzBLNEtYS1xLYEtkS5BLlEuYS5xIHEwgTCxMMEw4TDxMSExMTGRMaEzswAAABAAAvABQAdgEBAMkFAQD1CwEAOgwBAOkOAQAxDwMAqg8BAPUPAQAPEAEAexEBAIsRAwDyEQEADhIBADcSAQBKEgEATRIDALcSAQDvEgEAABMDABATAQAEUgkAAFMJAABHEgAASBIAAAACexAAAHwQAAAAAusPAADsDwAAAALlAwAA5wMAAA==\"]Ho[\"AAAAAVoQAAAAA7QFAAAkCQAANxEAADswAAABAAAMAAMAFQQFAAkHBQCIEgAAAAOfBwAADxAAABAQAAABfA8AAAAHFxAAABkQAAAbEAAAHRAAAB8QAAB7EAAAfBAAAAABoAcAAA==\",\"\"]Lk[\"AAACEwIAAHcCAAAAAoYBAACYAQAAOzAAAAEAAAsAAwCHAQEAmQEBAPsPBwA7MAAAAQAABwABABkFBwA6MAAAAQAAAAAABwAQAAAA8QfyB+8O8A42DzcPcxB0EAACRxIAAEgSAAA=\",\"AAAAAecOAAAAAAAAAAAAAAAAAAAAAAXmDgAA6A4AAO4OAADxDgAASRIAAA==\"]AAg[\"AAAEFQIAAHMCAAAlCQAAJgkAAAR8AQAAhAEAAIkBAACWAQAAAhsCAAB9AgAAAAA6MAAAAQAAAAAAEwAQAAAACAQJBN0I3giRD5MPlQ+XD5gPGBAaEBwQHhAgEIMRhBELEwwTDhMPEwLyEQAA8xEAAAX3CwAA+AsAAPkLAAD6CwAA+wsAAAAFFxAAABkQAAAbEAAAHRAAAB8QAAAAAAACkgEAAPEGAAA=\",\"AAAAAAAAAAAAAAAAAAAAAAAABN8IAADgCAAA4QgAAOIIAAA=\"]BKg[\"AAADIwkAAOQRAADlEQAABHoBAACUEAAA6REAAOoRAAAHhQEAAJcBAAAnCQAAKAkAAPcJAABECgAAPBEAAAE9EQAAAR0SAAA6MAAAAQAAAAAAEQAQAAAA8wf0B/UH9gewDiwPLQ+iD10QXxBhEGQQZhBoEEISQxLWEtcSAUESAAA7MAAAAQAACwACAAcPAwASEgcAOzAAAAEAAAkAAwASBwcA4gcAAOQHAAAAAAKhBwAAowcAAA==\",\"AAAEqgUAAKsFAACnDgAAqA4AADswAAABAAAWAAoATQcHAFsQAQBeEAAAYBAAAGIQAQBlEAAAZxAAADARAQBuEgIAtRIBAAIcEgAAjhIAAAE3EQAAAAAAAAAAAAAAAAAAOjAAAAEAAAAAAG0AEAAAAHABcQFzAXQBzgHPAdUB1gHrA+0DDgQjBSYF4gbmBhAHEQdDB0QHFQkWCUYJRwnOC9AL5QvmC+oL6wvyC/MLrg6vDsQO4g7jDgIPAw81D34Pfw+bD5wPpw+oD6wPrQ+yD7MPuA+5D+cP6A/vD/EP8g/4D/kPAxAEEAUQBhB5EHoQjBCNEDMRNBE1ETYRbxFwEX0RfhGHEYkRphGnEagRqRGrEawRrRGuEeYR6xHsEe0R/xEQEhESGhIeEh8SIBIhEiISIxI5EjoSVBJVElsSXBJ4EnkSkRKSEtAS0RI=\"]Ac[\"AAAAAAHmAgAA\",\"\"]Ca[\"AAAAAAIbAgAAfQIAADswAAABAAAFAAEAjhAFAA==\",\"\",1]Ek[\"AAAAAAAAAAAAAAAAAAAAAAAAAgUHAAAbBwAA\",\"AAAAAAACkA8AAJIPAAAAApEPAACTDwAAAAJEEQAARREAAA==\"]Eo[\"AAAAAAAAAAAAAAAAAAAAAAAAAgYHAAAcBwAA\",\"AAAAAAAClA8AAJYPAAAAA5UPAACXDwAAmA8AAAACRhEAAEcRAAA=\"]Ck[\"AAAAAAAAAAAAAAAAAAAAAAAABWIAAAB0AQAA3gcAAP0HAADhCAAA\",\"\"]Bg[\"AAAAAAAAAbwOAAAAAAAAAnsQAAB8EAAA\",\"\"]Ec[\"AAAAAVQAAAAABKEFAABBBwAAihAAAJASAAACqgAAAMAOAAAAAlgSAACoEgAA\",\"AAAAAAAB6QMAAA==\"]Hk[\"\",\"AAAAAAAAAAA7MAAAAQAACAABAIQLCAA7MAAAAQAADAABAO4DDAAEtg4AALcOAAC4DgAADRAAAAAB8QsAAATtCwAAuQ4AALsOAAAMEAAAA+4LAADvCwAAug4AAAAAAfALAAA=\"]Co[\"AAAAAAAAAAAAAAAAAALrDwAA7A8AAA==\",\"AAAAAAAAAAAAAAABAAgAAA==\"]Hk[\"AAADngEAANkLAADcCwAAAVAFAAABhQEAAAQkAAAAgQAAAJAPAACSDwAAARIPAAAB+wIAAAPkAAAATRAAAE4QAAA=\",\"AAACngEAANYLAAACCgQAAFAFAAABABIAAAABvAsAAA==\"]Hk[\"AAADnwEAANoLAADdCwAAAYsFAAABlwEAAARLAAAAjgAAAJQPAACWDwAAAk0BAAAfDwAAAQgDAAAD/wMAAE8QAABQEAAA\",\"AAACnwEAANcLAAADCwQAAIsFAAADEgAAAAHACwAA\"]2AKo[\"AAAAAAAAAYAPAAA6MAAAAQAAAAAAEAAQAAAA/gPgC+ELeQx6DLEOsg4TEBQQLhEvEeQS5RIHEwgTEhMTEwIGBAAAQRIAADswAAABAAAMAAMAvg4AAAcPAwASEgcAOzAAAAEAAAgAAgCLAQAAEgcHAAAAAAACkgEAAPEGAAA=\",\"AAAHrAUAAK0FAADZCwAA2gsAANwLAADdCwAAPhEAADowAAABAAAAAAAWABAAAADoBzIJPgnbC94L3wuqDqsOnw+gD6EP5g9aEGoQaxBsEG0QbhBEEqYSpxK6ErsSOzAAAAEAANUAAgDNDdEACRIDADswAAABAABIAAEACwBIAAAAAAR9EAAAfhAAAH8QAACAEAAABG8QAABwEAAAcRAAAHIQAAABghEAAAAAAAAAAAcSBAAAxAUAAA8HAADKDQAA6Q8AAPcPAACBEAAA\"]Cc[\"AAAAAAL6CQAARgoAAAAAADswAAABAAAFAAEAZhEFAA==\",\"\"]BMg[\"\",\"ADswAAABAAAFAAIA3wMEAO4SAAACBwcAACUHAAA6MAAAAQAAAAAACgAQAAAAqwGsAbYFQAw5EXERhRHPEtoS9xIGEzowAAABAAAAAAAWABAAAADYAdkB2gHbAdwB7AO1BboF2AYDBzAHSgn6DzwRchHwEXES8xL0EvUS9hIJEw0TOzAAAAEAAP8ADwC0BQAACAcAACQHAAAzBwAATw11AI4PAAD7DwcAURAAALcQdQA9EQAAPxEAALARAADxEQAADRIAANISAAA6MAAAAQAAAAAAGgAQAAAAtwW9Bb4FvwUBBwIHCQcKBwsHDAcNBw4HjguPC5ALkQu8DsAOtg8SEDgRHRJZEloSyhLbEvgSOzAAAAEAADMAEQD3AhMABwQAABMEAQCfBQEAogUAALgFAQDRBQAA9gYKAPMHAwCNCwAA7AsAAAsPAAA9DwAAtw8AALERAADLEgAA+RIAADswAAABAABQAAoAuwA8AF0BAQAGBAAAogcAAKQHAADjBwAA5QcAAGYRBQCpEgUAvBIAADswAAABAAAUAAYA7gMAAEEMAADFDQAABw8DABISBwDoEgUAOzAAAAEAABEACAAMBAEAEgcHAOIHAADkBwAAtg4CAL8OAAANEAAAkxEAAAJLBwAATAcAAAMDBAAABAQAAPELAAAEoQcAAKMHAADtCwAADBAAAAAFiRIAAIoSAACLEgAAjBIAAI0SAAA=\"]Bc[\"AAAAAAAAAAAAAAACexAAAHwQAAA=\",\"\"]Kc[\"AAAAAAHvEQAAADswAAABAAAGAAIACQcFABIQAAACogUAAD0PAAABBgQAAAADwA8AAMEPAACTEQAAASQPAAA=\",\"AAAAAAABAQQAADswAAABAAAFAAEAFQQFAAAAAAAAAAAAAAAAB9cBAAAiBQAAJQUAAB4HAAAfBwAAIAcAACEHAAA=\"]Dk[\"AAAAAAABFQAAAAAAAb8AAAAAAAAAAAAAAAACIgUAABAHAAA=\",\"AAAAAAAAAAAAAb4OAAA=\"]Dk[\"AAAAAAABKwAAAAH/AAAAAAAAAAAAAAAAAAACJQUAABEHAAA=\",\"AAAAAAAAAAAB5AMAAA==\"]Ac[\"AAABJQIAAA==\",\"\"]Bg[\"AAAAAAAAAAEABwAA\",\"AAAAAAAAAfQGAAA=\"]Fg[\"AAAABFUFAABPBwAA6wcAAF4QAAADOAkAAMcLAAD0EgAAAf0PAAAAAAHsAAAAARQSAAABFAcAAA==\",\"AAAAAVUFAAAAAYcBAAA=\"]Fg[\"AAAABJUFAABTBwAA7wcAAGUQAAADPAkAAMsLAAD2EgAAAQEQAAABYQEAAAAAARgSAAABGAcAAA==\",\"AAAAAZUFAAAAAZkBAAA=\"]Bk[\"\",\"AAAAAAAAAAA7MAAAAQAAPgACALsAPABdAQEA\"]Ck[\"\",\"AAAAAAHYBgAAOzAAAAEAAAkAAgAzBwAANQcIAAHOEgAAAVESAAA=\"]Bo[\"\",\"AAAAAAAAAAAAAAAAApMSAACUEgAAAAAAAfURAAA=\"]Eo[\"\",\"AAAAAAA7MAAAAQAABQABAHMRBQA7MAAAAQAABQABADsSBQAAOzAAAAEAAAUAAQCpEgUAOzAAAAEAAAUAAQDoEgUA\"]Ck[\"AAACJgIAADYCAAA=\",\"AAAAAAAAAl4SAABfEgAAAAACfRAAAH4QAAA=\"]Cg[\"\",\"AAAAAAMAEgAAARIAAAISAAAABLsLAAC8CwAAvQsAAL4LAAA=\"]Eo[\"AAAAAVoFAAAAASgAAAAAAAL2AAAA9wAAAA==\",\"AAA7MAAAAQAACgACAFYCAABYAgkAAloFAADcEgAAAAABkREAAA==\"]Eo[\"AAAAAZIFAAAAAVIAAAAAAAJdAQAAXgEAAA==\",\"AAA7MAAAAQAACgACANECCQDcAgAAApIFAADdEgAAAAABkhEAAA==\"]Dg[\"AAABtgIAAAGQBQAAAAFQAAAAAlYBAABXAQAA\",\"AAABxAIAAAGQBQAAAAAAAAH/AwAA\"]Eo[\"AAAAAAAAAmYBAABnAQAA\",\"AAAAAAAAB8oSAADxEgAA+BIAABQTAAAVEwAAFhMAABcTAAACyxIAAPkSAAAB8hIAAA==\"]Bk[\"AAAAAAAAASIBAAA=\",\"AAABkwIAAAAAAfsDAAA=\"]Bo[\"AAAAAZkFAAAAAAFoAQAA\",\"AAAAAZkFAAABdhIAAA==\"]Bg[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAATwMAAA=\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA98HAAD+BwAA4ggAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAhIEAADgBwAA\",\"\"]Cc[\"AAAAAAAAAAIfCQAAIAkAAAAAAAAAAAACkgEAAPEGAAA=\",\"\"]Fo[\"AAAAAAACtAUAAD8RAAAAAAAAAAAAAAAAAAAEBAgAAMQOAAC6DwAAlxAAAA==\",\"AAGmBwAAAAAAAAAAAAAAAAAAAAAAAAK7DwAATBAAAA==\"]Bk[\"AAAAAbYFAAAAAAG8DgAAAQsPAAA=\",\"AAHXBwAA\"]Bg[\"AAAAAAAABLwPAAC9DwAAvg8AAL8PAAA=\",\"\"]Ag[\"AAAAAn4BAACKAQAA\",\"\"]Do[\"AAAAAAAAAAAAAlIJAABTCQAAAYsBAAA=\",\"AAAAAAAAAAAABfcLAAD4CwAA+QsAAPoLAAD7CwAA\"]AGc[\"AAAAAAKDAQAAkwEAAAAABJ8FAACgBQAASBAAAEkQAAAEogcAAKQHAADjBwAA5QcAADswAAABAAALAAQAzwUBAPcLBABBDAAAfRADADowAAABAAAAAAALABAAAAAMBA0E4gfkB78OwA/BD28QcBBxEHIQkxEE/QMAAEsHAABMBwAAJA8AAAQDBAAABAQAAJMSAACUEgAABQIEAAChBwAAowcAALkOAAC7DgAAA+4LAADvCwAAug4AADowAAABAAAAAAAIABAAAACSAeUD5wPxBokSihKLEowSjRIB9REAAAHwCwAA\",\"AAAAAZgQAAAAAqQFAAClBQAAAALRCwAA0gsAAAAAAAAAAAAC5QMAAOcDAAA=\"]Hm[\"\",\"AAAAAAA7MAAAAQAABQABAPwLBQAC2AsAAM4SAAAEIRAAACIQAABKEAAAURIAAAWBDwAAgg8AAIMPAACEDwAAQRIAAAE6EQAAAAGCEQAAAAGgBwAAAAGSAQAAAAAB/AUAAA==\",1]AOk[\"AAACjAEAAJUBAAAGfAEAAIQBAACJAQAAlgEAAOkRAADqEQAABLUFAABKCQAA+QkAAEUKAAA7MAAAAQAABQABAPwLBQACWRIAAFoSAAA6MAAAAQAAAAAACQAQAAAAHwkgCZ0Png8hECIQ4hHjEcwSzRI6MAAAAQAAAAAABwAQAAAA5AOiB6QH4wflByUP7xLwEgG+DgAAAuIHAADkBwAAAQ4QAAA=\",\"AAAAOjAAAAEAAAAAABYAEAAAAHIBgAeBB4IHgwcGCAcIAgwoDykPjQ+xDxUQFhB6EX8RgBGvEUUSRhJ0EnUS1RICSAkAAKoRAAA7MAAAAQAABwADAPwLBQCGEQAAiBEAAAGADwAAAiEQAAAiEAAAAALPBQAA0AUAAAAC/QMAACQPAAAAAwIEAAC5DgAAuw4AAAPuCwAA7wsAALoOAAAAAALwCwAARgwAADowAAABAAAAAAALABAAAAB1AQ8EEAQkBScF0QbSBkUHhAcDCAQIBA8=\"]Bg[\"AAAAAAAAAAAAAfkDAAA=\",\"AAAAAfkFAAA=\"]Bk[\"AAAAAAAAADswAAABAAALAAIA0QUAAPYGCgA=\",\"\"]Cc[\"AAABGgkAAAAB3AEAAAAAAAHkAwAA\",\"AAAAAAAAAAH+AwAA\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlgAAACjDgAA\",\"\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAfEGAAA=\",\"\"]Cm[\"AAAAAAAAA70FAAC+BQAAvwUAAA==\",\"AAAAAAAAAAAAAAABAAgAAA==\",1]Bg[\"AAAAAAAAADswAAABAAATAAEA9wITAA==\",\"\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAEEBwAA\",\"AAAAAAVBCQAAQgkAAEMJAABECQAARQkAAAAAAAAAAAAAAAAAAAFaDwAA\"]Bg[\"AAAAAAAAAAAAAfQDAAA=\",\"AAAAAfcHAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAVcAAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAZMAAAA=\",\"\"]BMm[\"AAAAAAABsBEAAAABsREAADowAAABAAAAAAAaABAAAAB9AckFygXpDuoO9Q/2D3sRfBEOEg8SNxI4EkoSSxJNEk4STxJQElgSqBLvEvASAhMDExATERM7MAAAAQAABgADAFIJAQAHDwMAuRIAAAOLAQAA0xIAAPoSAAAAAAAAAAEEBwAAAAH8BQAA\",\"AAAFLQkAAC4JAAAvCQAAMAkAADEJAAAE0wsAANQLAADVCwAALREAADswAAABAAASAAgA+AcAANwIAABBCQQASAkAAHcPBACqEQAAsxEAAAkSAwA7MAAAAQAAIgAOAKcBAACpAQAA6QMAAPsDAAABBAAANQcIAPwLBQCzDgAAqQ8AAAcQBABTEAQA7hEAAEwSAACPEgAAOzAAAAEAACMACQDmAwAAFQQFAPsFAAABCAAAtA4AAA4PEwBtEQEAXhIBAGESAQA6MAAAAQAAAAAACQAQAAAAjw8hECIQdRB2EHcQeBCFEIYQdxI7MAAAAQAACgAFAOQDAACBDwMATRADAFgSAACoEgAABb4OAABEEQAARREAAEYRAABHEQAAAtMSAAD6EgAABP0DAAAACAAAJA8AAA4QAAAAAAABkgEAAAEEBwAAADowAAABAAAAAAAfABAAAACVAJYAlwCYAKkF6Ab1Bj4HPwdABxMJTglPCVAJUQlUCVUJpQ6mDvQODA8NDy4PLw8wD64Prw+wD4QQMhF8EhgT\",1]Co[\"AAAAAAAAAAAAAAAAAALrDwAA7A8AAA==\",\"AAAAAAAAAAAAAAABAAgAAA==\"]Jg[\"AAAAATgFAAAABxYAAAB+AAAApwEAAOkDAACzDgAAjxAAAJIQAAACDw8AAG0RAAAC+AIAAPcGAAACwAAAAMEAAAAAAeIHAAAAAAGhBwAA\",\"AAGrBwAAAAJUAAAAOAUAAAIbAgAA+AcAAAKPEAAAkhAAAAAB6gYAAA==\"]Og[\"AAAAAV4FAAAABi0AAACGAAAAqQEAAJEQAACTEAAA7hEAAAcCAQAAAwEAALQOAAAXDwAAIw8AAG4RAAAbEgAABAADAAD8BgAA7AsAAHcSAAABOxEAAAHFDQAAA+QHAADTEgAA+hIAAAAAAaMHAAA=\",\"AAHABwAAAAFeBQAAAn0CAACzBQAAApEQAACTEAAAAe8GAAAAAAHpCwAAAAGCEQAAAAAAAAAAOzAAAAEAAAUAAQBXAAUA\"]Bo[\"\",\"AAAAAAAAOzAAAAEAAJIAAwCbAB8A+ABkAF8BDQA=\"]CCm[\"\",\"AAA7MAAAAQAA0wABALAK0wA7MAAAAQAA2QALAMwJKgD4CQAA+wkvACwKAQAvChQARwodAGYKKQCTCgAAlQoaAGwRAADcEgEAOjAAAAEAAAAAABIAEAAAALoF+gXYBicJKAn3CfkJ+gkrCi4KRApFCkYKZQqRCpQKABIBEgISOjAAAAEAAAAAABEAEAAAAKEFMwdBB0wJTQmQCpIKihBzEXQRdRF2EXcReBENEpASlxKYEjowAAABAAAAAAApABAAAAABBwIHAQiOC48LkAuRC7sLvAu9C74L2AvADuEOIw+2D7wPvQ++D78PEhCREZIRGxI7EjwSPRI+Ej8SQBJeEl8SYRJiEsoSzhLxEvgSFBMVExYTFxM6MAAAAQAAAAAAGgAQAAAA/gMTBBQEuAW5BekG6gbrBuwG7QbsCz0PkQ+TD5UPlw+YD7cPIRAiEFIQhRCGEFESdxLLEvkSOzAAAAEAAFYADgC7ADwAXQEBAOQDAAD/AwAABgQAALAFAQCfBwAAJQ8AADsRAABmEQUAWBIAAKgSBgC8EgAA8hIAADswAAABAAASAAkAzwUBAOkLAABBDAAAxQ0AAL4OAAB8DwAAfRADAEcSAQDoEgUABr8OAADADwAAwQ8AAJMRAADTEgAA+hIAADowAAABAAAAAAALABAAAAD9A7sFSwdMBwAIJA8XEBkQGxAdEB8QghEEAwQAAAQEAACTEgAAlBIAAASgBwAAoQcAAKMHAAAMEAAAAu8LAAC6DgAAOjAAAAEAAAAAAAgAEAAAAJIB5QPnA/EGiRKKEosSjBKNEgH1EQAAAAH8BQAA\",1]Le[\"\",\"AAAAApsBAADWBgAAAzEHAACsDgAArQ4AAAMkBwAAhhEAAIgRAAA6MAAAAQAAAAAABwAQAAAAvA44EUERQhFDEVkSWhKIEgIHBAAAjQsAAAF9AQAAAbkSAAAEtg4AALcOAAC4DgAADRAAAAAB8QsAAAQCBAAA7QsAALkOAAC7DgAAAe4LAAAAAAHwCwAA\",1]Fo[\"\",\"AAAAOjAAAAEAAAAAAAcAEAAAAKsBrAE5EYURzxLaEvcSBhMBMAcAAATVBgAAJAkAAOQOAAA3EQAAAoAPAAAdEgAAAUoQAAABQRIAAA==\"]Cg[\"\",\"AAAAAwMSAAAEEgAABRIAAAAEvwsAAMALAADBCwAAwgsAAA==\"]Bk[\"\",\"AAAAAAAAOzAAAAEAAAUAAgDuBgIA8gYCAA==\"]Fc[\"AAAAAZQBAAAAAAAABMkFAADKBQAA6Q4AAOoOAAA=\",\"AAA7MAAAAQAANgABAAMMNgAAAAAAAAAAAAAAAAAAAAABIgcAAA==\"]Ao[\"\",\"AAA7MAAAAQAA0wABAAgI0wA=\"]AAc[\"AAAELAIAADwCAABBAgAApw4AAAE2BQAAA/ARAAAJEwAADRMAAAMTAAAAfQAAAI4QAAABDg8AADowAAABAAAAAAALABAAAAD3AhMEnwWgBbgF9gaCEFISehKfEtgS5hICuwAAALwAAAA=\",\"AAGqBwAABxMCAAAVAgAAFgIAABcCAAAYAgAAJQkAAOQRAAABNgUAAAQnCQAA9wkAAPkJAAD6CQAAAY4QAAABuwsAAALpBgAAhRAAAAAAAAEACAAAAAAAAAAAAaAOAAA=\"]AAg[\"AAAEmQIAALgCAADAAgAAqA4AAAFbBQAAAAQpAAAAhAAAAJAQAADxEQAABPsAAAD8AAAAFQ8AANsSAAA6MAAAAQAAAAAACgAQAAAA/gIUBKIFuQX7BoMQUxJ7EqAS2RLnEgABxQ0AAA==\",\"AAG/BwAAOzAAAAEAAAYAAwBzAgQAJgkAAOURAAABWwUAAAQoCQAARAoAAEUKAABGCgAAAr8LAACQEAAAAu4GAAABCAAAAYYQAAAAAAAAAAAAAAAAA6IOAACjDgAApA4AAA==\"]Cg[\"AAAAAAAAAAAAAAAAAQMEAAAAAAHlAwAA\",\"AAAAAAAAAAFIEAAA\"]Cg[\"AAAAAAAAAAAAAAAAAQQEAAAAAAHnAwAA\",\"AAAAAAAAAAFJEAAA\"]Fo[\"AAA7MAAAAQAACwAEAHIPBAAGEgAAoRIEAAoTAAA=\",\"ADswAAABAAAJAAIAhQcEAN0RBAA7MAAAAQAACwAEAHIPBAAGEgAAoRIEAAoTAAA=\"]Hk[\"AAAAAAAAAAA7MAAAAQAACAABAIQLCAA7MAAAAQAADAABAO4DDAAEtg4AALcOAAC4DgAADRAAAAAB8QsAAATtCwAAuQ4AALsOAAAMEAAAA+4LAADvCwAAug4AAAAAAfALAAA=\",\"\"]Cc[\"AAAAAoABAAAVEAAAAAAAAAH1CwAAAAEMBAAAAUsHAAA=\",\"\"]Cc[\"AAAAAo8BAAAWEAAAAAAAAAH2CwAAAAENBAAAAUwHAAA=\",\"\"]AAo[\"AAAAAvYRAAD3EQAAAAWHAQAAiAEAAJkBAACaAQAAURAAADswAAABAAALAAMAGQUHALcFAADiCwIAOjAAAAEAAAAAAAcAEAAAAKIF8wf0B/UH9gfBDsIOCw8GTRAAAE4QAABPEAAAUBAAALwSAADyEgAAOzAAAAEAAA0ABADpCwAAxQ0AAAcPAwASEgcAOzAAAAEAAAcAAQASBwcAAg4QAACCEQAAAAGgBwAAAAABBAcAAAAB/AUAAA==\",\"AAAAA6EBAACiAQAAowEAAAABqQ8AAA==\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACrgUAACcHAAA=\",\"AAAAAAAAAAJ1EAAAdhAAAAAAAAAAAAAAAAACBQcAABsHAAA=\"]Ek[\"AAAAAAAAAAAAAAAAAAAAAAACrwUAACgHAAA=\",\"AAAAAAAAAAJ3EAAAeBAAAAAAAAAAAAAAAAACBgcAABwHAAA=\"]Ak[\"\",\"AAAAAAIFDwAABg8AAA==\"]Eo[\"AAAABFcFAABQBwAA7AcAAGAQAAACOQkAAMgLAAAB/g8AAAAAAe4AAAABFRIAAAEVBwAA\",\"AAAAAVcFAAAAAYgBAAA=\"]Eo[\"AAAABJcFAABUBwAA8AcAAGcQAAACPQkAAMwLAAABAhAAAAFjAQAAAAABGRIAAAEZBwAA\",\"AAAAAZcFAAAAAZoBAAA=\"]Bg[\"AAAAAAAAAAAAAfMDAAA=\",\"AAAAAUsJAAA=\"]Ag[\"AAACfwEAAI4BAAA=\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAALtCwAADBAAAA==\",\"\"]Ag[\"\",\"AAACmQ8AAJoPAAA=\"]Bc[\"AAAAAAAAAAAAAToRAAA=\",\"AAFhAAAA\"]Dg[\"AAAAAqEBAACiAQAAAAADvQUAAOILAABBEQAAAAF9AQAAAbkSAAA=\",\"AAAAAS0RAAA=\"]Hg[\"AAAAAWoQAAABCRIAAAOQDwAACBAAAFQQAAABXhIAAAF1EAAAAYEPAAACfRAAAEQRAAABbxAAAAAAAAABihIAAA==\",\"AAAAAAAAAAIYEAAAGhAAAAAAAAIXEAAAGRAAAA==\"]Ig[\"AAAAAW0QAAABCxIAAAOUDwAAChAAAFYQAAABYRIAAAF3EAAAAYMPAAACfxAAAEYRAAABcRAAAAAAAAABjBIAAA==\",\"AAAAAAFLEAAAAAADHBAAAB4QAAAgEAAAAAAAAxsQAAAdEAAAHxAAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAmAAAACkDgAA\",\"\"]Cc[\"AAAAAbEPAAABHBIAAAAC5gMAAPsFAAA=\",\"AAAAAAEFCAAA\"]Ek[\"AAAAAT0FAAAAARgAAAAAAAHGAAAAAAAAAAAAAAAAAiQFAADRBgAA\",\"AAGwBwAAAAE9BQAAAAAAAAAAAcAPAAA=\"]Ek[\"AAAAAWoFAAAAATUAAAABEwEAAAAAAAAAAAAAAAAAAicFAADSBgAA\",\"AAHHBwAAAAFqBQAAAAAAAAAAAcEPAAA=\"]Be[\"AAAAAAA7MAAAAQAAdQABALcQdQA=\",\"\",1]Bg[\"AAAAAAAAAAAAAfYDAAA=\",\"AAAAAboLAAA=\"]Eg[\"AAAAAAAAAAAAAAAAAAKhBwAAowcAAAACkgEAAPEGAAABBAcAAA==\",\"AAAAAAAAAAAAAAAAAAAAAZIBAAA=\"]Dk[\"AAAAAAAAAAABrhIAAAHtEgAA\",\"AAAAAAABeBEAAAFAEgAAAAAAAAAAAAAAAAABigcAAA==\"]Co[\"AAAAAX4FAAAAAAE0AQAAAAAAAAAAAAAAAAAB1wEAAA==\",\"AAAAAX4FAAA=\"]ABc[\"AAAAA3oBAAAKCgAAlBAAAAMDBwAAZQoAAPoPAAAAADowAAABAAAAAAANABAAAAA0CTUJPwlACfIO8w60D7UP5xHoEQcSCBJWElcSOzAAAAEAAA8ABwD1DwEAZhEFAA4SAQBYEgAAqBIAAAITAQAQEwEAAALTEgAA+hIAAA==\",\"AAAAA20BAAB4AQAAfQ8AAAR3DwAAeA8AAHkPAAB6DwAAAqcBAACpAQAAAAACWBIAAKgSAAAAAtMSAAD6EgAAAAAAAAAAAASoAQAAqgEAABcJAACXEAAA\"]Bk[\"AAAAAAEwBwAA\",\"AAAAAAAAAAFKEAAAAAE6EQAA\"]Hg[\"AAAABS4FAADGBQAA+QUAAEAMAAB/EQAAAa0OAAADJAcAALARAADSEgAAAqQAAAA4EQAAAaAFAAABnwcAAAF8DwAA\",\"AAAAAi4FAADWBgAAATEHAAAAAAABfQEAAAG5EgAA\"]Gg[\"AAAAAoARAABwEgAAAd0GAAABCAcAAAH5AAAAAo8PAACxEQAAAAEmBwAAAAG7BQAAAAGgBwAAAAABBAcAAAAB/AUAAA==\",\"AAAAAAAAAAAAAAGLAQAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAV8AAAA=\",\"\"]Fc[\"AAADBwcAACUHAAAADwAABN4LAAClDwAAiRAAALoSAAAE2QEAALoFAAD6BQAAQwkAAAACpgAAAOEOAAA=\",\"AAAB8w8AAA==\"]Fc[\"AAAABN8LAACmDwAAixAAALsSAAABRQkAAAEIBwAAAfoAAAAAAAEmBwAAAAG7BQAAAAAAAAAAAfwFAAA=\",\"AAAB9A8AAA==\"]Dg[\"AAAAAYMFAAAAAUUAAAABPgEAAA==\",\"AAAAAYMFAAAAAAAAAAAAAAAAAAAAAAFiAAAA\"]Bk[\"AAAAAAHYAQAAAAAAAAHwAwAA\",\"AAAAAecLAAA=\"]Cc[\"AAAAAAH4BwAAAAAAAAHxAwAA\",\"AAAAAAAC6AsAAIERAAA=\"]Dk[\"\",\"AAAAADswAAABAAATAAEAiwcTADswAAABAAATAAEAfQATADswAAABAAATAAEADg8TAA==\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAV0AAAA=\",\"\"]Ak[\"\",\"AAAAAAIiDwAAdhIAAA==\"]Ag[\"\",\"AAAAAAABjxIAAA==\"]Fg[\"AAAAAZoFAAAAAAFqAQAAAAAAAAAAAQIEAAAAAAAB8AsAAAaXAAAAdQEAANEBAAD1BgAARQcAADIRAAA=\",\"AAHWBwAAAAGaBQAA\"]Hk[\"AAAEkQAAABgCAACqBQAA1gsAAAFSBQAAAAImAAAAgwAAAAEUDwAAAv0CAAD6BgAAAucAAADoAAAA\",\"AAABQQIAAANVAAAAVgAAAFIFAAAChQEAAAISAAAAAb4LAAAB7QYAAA==\"]Kk[\"AAAEkgAAAHQCAACrBQAA1wsAAAGNBQAAAARNAAAAkAAAACcPAAArDwAAA1ABAABRAQAAIQ8AAAIKAwAA/wYAAA==\",\"AAABwAIAAAKNBQAABRIAAAGXAQAAAcILAAAD5gMAAPsFAADzBgAAAAAAAAAAAAAAAAAEXQAAAF4AAABfAAAAYAAAAA==\"]Ak[\"AAAAAAI7DwAAPA8AAA==\",\"\"]Ao[\"AAAAAAAAAAACdgEAAGYRAAA=\",\"\"]Ao[\"AAAAAAAAAAACdwEAAGkRAAA=\",\"\"]Dc[\"AAAAAAAAAAAB6QAAAAAAAAAAAAAAAAWVAAAADwQAAD4HAADrDgAArg8AAA==\",\"\"]Fc[\"AAAAAAAAAVIBAAAAAAAAAAAAAAAAAAWWAAAAEAQAAD8HAADsDgAArw8AAA==\",\"AAAAAAAAAAAAASYHAAAAArsFAAAOEAAA\"]Fc[\"AAAAAZsFAAAAAAFrAQAAAgcEAACNCwAAAAAAAAHxCwAAAAAAAAAFmAAAAEAHAAAUCQAA7Q4AALAPAAA=\",\"AAAAAZsFAAA=\"]Fk[\"AAAAATkFAAAAAAAAAcIAAAAAAAAAAAAAAAADcAEAACMFAADOCwAA\",\"AAGsBwAAAAE5BQAAAAAAAAAAAAAAAAAAAAACRgcAAEgHAAA=\"]Fk[\"AAAAAWQFAAAAAAELAQAAAAAAAAAAAAAAAAADcQEAACYFAADQCwAA\",\"AAHDBwAAAAFkBQAAAAAAAAAAAAAAAAAAAAACRwcAAEkHAAA=\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAH1EQAA\",\"\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAV4AAAA=\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlkAAAC1DgAA\",\"\"]Bg[\"AAAAAAAAAAAAAfUDAAA=\",\"AAAAAXgMAAA=\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAAZQAAAA=\",\"\"]Dk[\"AAAAAVQFAAABhgEAAAABGgUAAAFdEAAAAesAAAA=\",\"AAG5BwAAAAFUBQAAAAAAAV0QAAA=\"]Dk[\"AAAAAZQFAAABmAEAAAACYAEAAB4FAAABZBAAAA==\",\"AAHSBwAAAAGUBQAAAAAAAWQQAAA=\"]Hc[\"AAAAAAAB5A4AAAGIEgAAAAGqEgAAAekSAAAAAAAAAAAAADowAAABAAAAAAANABAAAAAPBxMJUAnKDfEO9A4uD+kP9w+EEIcRiRFJEnwS\",\"AAAAAAABdBEAAAE8EgAA\"]Cg[\"AAAAAa8RAAABSAkAAAEBBAAAAAABBgQAAA==\",\"AAAAAAHPCwAA\"]Dk[\"AAAAAAAAOzAAAAEAAJIAAwCbAB8A+ABkAF8BDQAAOzAAAAEAAD4AAgC7ADwAXQEBAA==\",\"\"]Dg[\"AAAAAUkFAAAAAR0AAAAAAfgGAAAC1gAAANcAAAA=\",\"AAAAAUkFAAAAAAAB6wYAAA==\"]Dc[\"AAAAAXsFAAAAAUAAAAACLgEAAC8BAAAB/QYAAA==\",\"AAAAAXsFAAAAAAHwBgAA\"]Ck[\"AAAAASgPAAAAAAAAAqIHAADjBwAA\",\"AAAAAAAAAAACgQ8AAIIPAAA=\"]Ck[\"AAAAASkPAAAAAAAAAqQHAADlBwAA\",\"AAAAAAAAAAACgw8AAIQPAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAvAPAABAEQAA\"]Ek[\"AAAAAwYIAAAwEQAAbhIAAAAAAAAAAAHiBwAAAAABoQcAAA==\",\"AAAAAAAAAAJ1EAAAdhAAAAKBDwAAgg8AAA==\"]Ek[\"AAAAAwcIAAAxEQAAbxIAAAAAAAAAAAHkBwAAAAABowcAAA==\",\"AAAAAAAAAAJ3EAAAeBAAAAKDDwAAhA8AAA==\"]Eg[\"AAAAAXAFAAAAATwAAAACHgEAAB8BAAA=\",\"AAAAAXAFAAAAAAAAAAHpCwAAAAGCEQAAAAAAAAAAAacFAAA=\"]Ac[\"\",\"AAAAAAEiDwAA\"]Cc[\"AAABkwIAAAFhBQAAAAEvAAAAAQYBAAA=\",\"AAAAAWEFAAA=\"]Gg[\"AAAAATsFAAAAARcAAAAAAAHEAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAkAEAAAAKgB1QGuDuYOmw+yD6YREBIeEjkS\",\"AAGuBwAAAAE7BQAAAAGzDgAA\"]Ho[\"AAAAAWYFAAAAATIAAAABDQEAAAAAAAAAAAAAAAAAOjAAAAEAAAAAAAkAEAAAAKoB1gGvDugOnA+zD6cRERIfEjoS\",\"AAHFBwAAAAFmBQAAAAABtA4AAAAAAAAAAAAAAAAAAbUOAAA=\"]Gk[\"AAAC+wEAAAMCAAABMQUAAAACDgAAADcHAAACrgAAAK8AAAA=\",\"AAAD/QEAAP4BAAD/AQAAATEFAAAAAAAAAAG+DgAAAAL9AwAAJA8AAAABDBAAAAHvCwAA\"]Do[\"AAAAAWgFAAAAAjMAAACHAAAAAw8BAAAQAQAAGA8AAAEBAwAA\",\"AAAAAWgFAAAAAAAAAeQDAAA=\"]Ck[\"AAAAAnQSAAB1EgAAAAAAAAAAAAAB8QsAAA==\",\"AAAAAAAB7Q8AAA==\"]Co[\"AAAAAAABJAcAAANBEQAAQhEAAEMRAAAAAX0BAAA=\",\"AAAAAAAB5A4AAA==\"]JLa[\"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\",1]Eo[\"AAAAAAA7MAAAAQAABQABAHMRBQA7MAAAAQAABQABADsSBQAAOzAAAAEAAAUAAQCpEgUAOzAAAAEAAAUAAQDoEgUA\",\"\"]Ck[\"AAAAAAAAAbcFAAAAAAHuAwAA\",\"AAAAAAAAA+ILAADjCwAA5AsAAA==\"]Bo[\"\",\"AAAAAAADQQcAAIoQAACQEgAAAAACWBIAAKgSAAA=\"]Fc[\"AAAAAWMFAAAAATEAAAACCQEAAAoBAAA=\",\"AAHCBwAAAAFjBQAAAAACIw8AABsSAAAC7AsAAHcSAAAAAALTEgAA+hIAAA==\"]Eo[\"AAAAAYcQAAABQQkAAAACnwAAANgLAAABUhAAAAAB8gMAAA==\",\"AAAAAAH6BQAAAALYCwAA4Q4AAAABnwcAAAF8DwAA\"]Dk[\"AAAABjkRAACFEQAAzxIAANoSAAD3EgAABhMAAAAAAAABQRIAAA==\",\"AAAAAAAAAAFKEAAA\"]A@g[\"AAAAOjAAAAEAAAAAAA8AEAAAAAoEKwXFBdkG9wdLCboL5wsCDHgMOA8REIgQcRFEEtUSOjAAAAEAAAAAAAkAEAAAAEIJIg97DzwRqhHvEfARdhIJEw0TOjAAAAEAAAAAAAgAEAAAAKcB6QPoC7MOBxBTEIERDRKPEjowAAABAAAAAAAHABAAAACgABIQbRHKEvES+BIUExYTAZ8FAAAAAAAAAAAAAYkSAAA=\",\"AAAAASsFAAAB3AgAAAAB2AsAAA==\"]ACk[\"AAAAOjAAAAEAAAAAAAgAEAAAAAsEMgnbC6oO5w7mD2wQphKnEjowAAABAAAAAAAJABAAAADsA7MFxwUFCEQJzwtLEHIRcRKOEjowAAABAAAAAAAKABAAAACpAfsDJw8rD44PqQ9RED0R7hHxEUwSOjAAAAEAAAAAAAcAEAAAAPgAtA4jD24RGxLbEhUTFxMFogUAAOwLAAB3EgAAyxIAAPkSAAADOxEAALwSAADyEgAAAukLAADFDQAAAtMSAAD6EgAAAg4QAACCEQAA\",\"AAAAAAAAAAAAAAAAAAGgBwAAAAAAAAH8BQAA\"]Ho[\"AAAAA00HAADpBwAAWxAAAAM2CQAAxQsAAPMSAAAB+w8AAAAB8wcAAAACBw8AABISAAABEgcAAA==\",\"AAAAAATFCwAAxgsAAMcLAADICwAAAAUZBQAAGgUAABsFAAAcBQAAbREAAA==\"]Ho[\"AAAAA1EHAADtBwAAYhAAAAM6CQAAyQsAAPUSAAAB/w8AAAAB9QcAAAACCQ8AABYSAAABFgcAAA==\",\"AAAAAATJCwAAygsAAMsLAADMCwAAAAUdBQAAHgUAAB8FAAAgBQAAbhEAAA==\"]Mc[\"AAAAAToFAAAAAAAAAcMAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAAzgHrA+IGQwcVCUYJ5QvqC/IL4g4CD34Ppw+sD7gP5w/xD/gPAxAFEFgQeRCMEDMRNRFvEX0RqxGtEesRIBIiElQSWxJ4EpES0BI=\",\"AAGtBwAAAAE6BQAAAAAAAAAAAAAAAAAAAAACGwkAAB0JAAA=\"]Mc[\"AAAAAWUFAAAAAAEMAQAAAAAAAAAAAAAAAAA6MAAAAQAAAAAAJAAQAAAAzwHtA+YGRAcWCUcJ5gvrC/ML4w4DD38PqA+tD7kP6A/yD/kPBBAGEFkQehCNEDQRNhFwEX4RrBGuEewRIRIjElUSXBJ5EpIS0RI=\",\"AAHEBwAAAAFlBQAAAAAAAAAAAAAAAAAAAAACHAkAAB4JAAA=\"]Ag[\"\",\"AAAAAAAAAY4LAAA=\"]Ck[\"AAAAAYkFAAAAAAFKAQAA\",\"AAAAAYkFAAAAAAOPCwAAkAsAAJELAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAECBAAAAAAAAfALAAA=\",\"\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAgQTAAAFEwAA\"]Ck[\"AAAAAAAAAAABvBIAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAABGxMAAA==\"]Ag[\"AAACWAIAANYCAAA=\",\"\"]Cc[\"AAAAAAAC6AsAAIERAAABwA4AAA==\",\"AAAAAAABQQcAAA==\"]Gc[\"AAAAAWAFAAAAAS4AAAABBQEAAAABOxEAAAHpCwAAAAGCEQAA\",\"AAHBBwAAAAFgBQAAAAHuEQAAASMPAAAAATsRAAAAAAAAAAAAAAABQgcAAA==\"]Do[\"AAAAAAAAAAHBDgAAAskFAADpDgAA\",\"AAAAAX4BAAAAAAABwQ4AAATJBQAA6Q4AAE0QAABOEAAA\"]Do[\"AAAAAAAAAAHCDgAAAsoFAADqDgAA\",\"AAAAAYoBAAAAAAABwg4AAATKBQAA6g4AAE8QAABQEAAA\"]Ao[\"AAAAAAAAAYAPAAABog8AAA==\",\"\"]Fk[\"AAAAAlUAAADoBwAAAdoBAAAAAasAAAACEwQAAIUQAAABJQ8AAAG+DgAAAb8OAAAC/QMAACQPAAAAAu0LAAAMEAAAAu4LAADvCwAA\",\"\"]Cc[\"AAAAAAAAAAP+AwAAFAQAAIYQAAA=\",\"AAAAAAABJw8AAA==\"]Do[\"AAAB/wEAAAEyBQAAAAIPAAAAOAcAAAKwAAAAsQAAAA==\",\"AAABAgIAAAEyBQAAAAAAAYUQAAA=\"]Bo[\"AAAB8w8AAAAAAAKnAAAAqAAAAA==\",\"AAAAAAG6BQAA\"]Ck[\"AAAB9A8AAAAAAAI5AQAAOgEAAA==\",\"AAAAAAAAAAAAAAABuwUAAA==\"]Ao[\"AAAAAAAAAR0SAAABsA4AAA==\",\"\"]Hg[\"AAAAAVYAAAAB2wEAAAFMCQAAA7YAAAABBwAAtg8AAAK4BQAAPQ8AAAACzwUAAEEMAAAGtg4AALcOAAC4DgAAwA8AAA0QAACTEQAAAQAIAAAAArkOAAC7DgAAAboOAAA=\",\"\"]Dk[\"AAAAAAABTQkAAAICBwAAAQgAAAK5BQAAtw8AAAAB0AUAAAHBDwAA\",\"AAAAAAABKw8AAA==\"]Bc[\"AAAAAAAAAAAAAAAAAAAAAAH1EQAA\",\"\"]ABk[\"AAAAAdYGAAABMQcAAATVBgAA6AsAAOQOAACBEQAAAsoSAAD4EgAAA0oQAADLEgAA+RIAAAWfBwAATRAAAE4QAABPEAAAUBAAAALpCwAAfA8AAAABghEAAAABoAcAAAAAAQQHAAA=\",\"AAAABIcQAACIEAAAiRAAAIsQAAACrA4AAK0OAAACihAAAI8SAAAGGxIAAPESAAAUEwAAFRMAABYTAAAXEwAAAXcSAAAB8hIAAAE6EQAAAAAAAAAAAALGDgAA2Q4AAAXQAQAA0QEAAOsOAADsDgAA7Q4AAA==\"]Dc[\"AAAAAAAAAAA7MAAAAQAABQABAGYRBQA=\",\"AAAABKABAACkAQAApQEAAKYBAAA=\"]Ao[\"AAAAAaABAAAAAAAAATEPAAA=\",\"\"]Ao[\"AAAAAaUBAAAAAAAAATIPAAA=\",\"\"]Eo[\"AAAAAWsQAAABChIAAAOSDwAACRAAAFUQAAABXxIAAAF2EAAAAYIPAAACfhAAAEURAAABcBAAAAAAAAABixIAAA==\",\"\"]Eo[\"AAAAAW4QAAABDBIAAAOWDwAACxAAAFcQAAABYhIAAAF4EAAAAYQPAAACgBAAAEcRAAABchAAAAAAAAABjRIAAA==\",\"\"]Bc[\"AAAAAAAAAhUEAAAJBwAAAAEzDwAA\",\"\"]Bc[\"AAAAAAAAAhgEAAAMBwAAAAE0DwAA\",\"\"]Ec[\"AAAAAZsBAAAAAoYRAACIEQAAAlkSAABaEgAAAgcEAACNCwAAAAAAAAABAgQAAA==\",\"AAAAAAHmAgAA\"]Gg[\"AAAAATQFAAAAAhEAAAA6BwAAArcAAAC4AAAA\",\"AAADCAIAAAoCAAALAgAAATQFAAAAAAABPQ8AAAACzwUAAEEMAAACwA8AAJMRAAAAAAABug4AAA==\"]Eg[\"AAAAAWsFAAAAAjYAAACJAAAAAxQBAAAVAQAAGg8AAAEDAwAA\",\"AAAAAWsFAAAAAAAAAAHQBQAAAcEPAAA=\"]Ec[\"AAAAAAAAATgBAAAAAAAAAAAAAAAAAAIoBQAAUQkAAA==\",\"AAAAAAAAAAAAAAAAAAAAAAAAAfwFAAA=\"]Ec[\"AAABwwsAAAEqBQAAAAELAAAAAZwAAAA=\",\"AAAAASoFAAAAASQJAAAAAAAAAAAAAAAAAAAB0wYAAA==\"]Dg[\"AAAAAAG1BQAAAAAAAAAAAAAAAAAAAAK7DwAATBAAAA==\",\"AAGnBwAAAAAAAdUGAAA=\"]Ac[\"AAABIwIAAA==\",\"\"]Do[\"AAAAAUgFAAAAARwAAAAAAALUAAAA1QAAAA==\",\"AAACMgIAADMCAAABSAUAAAAAAAAAAUcSAAA=\"]Dk[\"AAAAAXoFAAAAAT8AAAACLAEAAC0BAAA=\",\"AAACoAIAAKECAAABegUAAAAAAAAAAUgSAAA=\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAikJAAArCQAA\"]Dk[\"AAAAAAAAAAAB8gAAAA==\",\"AAABTgIAAAAAAAAAAAAAAUsHAAABAwQAAAGhBwAAAAHlAwAA\"]Dg[\"AAAAAAAAAVkBAAA=\",\"AAAByQIAAAAAAAAAAAAAAUwHAAABBAQAAAGjBwAAAAHnAwAA\"]Dk[\"AAAAAVYFAAAAAYcBAAABGwUAAAFfEAAAAe0AAAA=\",\"AAG6BwAAAAFWBQAAAAAAAV8QAAA=\"]Dk[\"AAAAAZYFAAAAAZkBAAACYgEAAB8FAAABZhAAAA==\",\"AAHTBwAAAAGWBQAAAAAAAWYQAAA=\"]Ck[\"AAAAAWwFAAAAATcAAAACFgEAABcBAAA=\",\"AAAAAWwFAAAAAAECBwAA\"]Eg[\"AAAAAUMFAAAAAhsAAAB/AAAAARAPAAAB+QIAAALPAAAAexEAAA==\",\"AAG1BwAAASwCAAACQwUAAAoKAAA=\"]Eg[\"AAAAAXUFAAAAAj4AAACKAAAAAicBAAAbDwAAAQQDAAABfBEAAA==\",\"AAHNBwAAAZkCAAABdQUAAAFlCgAA\"]Ao[\"AAAAAAAAAAACqg8AAE0QAAA=\",\"\"]Ao[\"AAAAAAAAAAACqw8AAE8QAAA=\",\"\"]Bg[\"AAAAAAAABLUAAAC/BQAA5AsAAEMRAAA=\",\"\"]Ak[\"AAAAAAABOAAAAAEYAQAA\",\"\"]Do[\"AAAAAAAAAAAAAAAAAAAAAAAAAvAPAABAEQAA\",\"AAGlBwAAAAAAAAAAAAAAAAAAAAAAAAG6DwAA\"]Cg[\"AAAAAAMDBwAASgkAAPoPAAA=\",\"AAAAAAAAAAKwDgAAog8AAA==\"]Ag[\"AAACTgIAAMkCAAA=\",\"\"]Cc[\"AAAAASoKAAACKwoAAJEKAAABkAoAAAK8DwAAvg8AAA==\",\"\"]Ac[\"\",\"AAAAAWwRAAA=\"]Gk[\"AAAAAUcFAAAAAdISAAAAAd0IAAAE0wAAADMPAACMEQAAtxIAAAF8DwAA\",\"AAGVEgAAAAOCAQAARwUAANwSAAAAAZcSAAAAAd0IAAADMw8AAIwRAAC3EgAA\"]Bc[\"AAAAAYIBAAAAAAAAAAAAARcQAAA=\",\"\"]Gk[\"AAAAAXkFAAAAAAErAQAAAd4IAAADNA8AAI4RAAC4EgAAAAAAAAGgBwAA\",\"AAGWEgAAAAORAQAAeQUAAN0SAAAAAZgSAAAAAd4IAAADNA8AAI4RAAC4EgAA\"]Bc[\"AAAAAZEBAAAAAAAAAAAAARsQAAA=\",\"\"]Eo[\"AAABCwIAAAE+BQAAAAEZAAAAAAACxwAAAMgAAAA=\",\"AAGxBwAAAAE+BQAAAAFMCQAAAgEHAAC2DwAAAhMEAAC4BQAA\"]Eo[\"AAAAAW4FAAAAAToAAAACGgEAABsBAAA=\",\"AAHJBwAAAAFuBQAAAAFNCQAAAQEIAAAEFAQAALkFAAC3DwAAhhAAAA==\"]Ao[\"\",\"AAAAAAAAAAAAAAABHxAAAA==\"]Ek[\"AAAAAUwFAAAAASAAAAAAAALcAAAA3QAAAA==\",\"AAABOAIAAAFMBQAAAAAAAAGfBwAAAXwPAAAAAhcQAAAZEAAA\"]Eg[\"AAAAAYAFAAAAAUQAAAACNgEAADcBAAA=\",\"AAABqAIAAAGABQAAAAAAAAAAAAIbEAAAHRAAAAABoAcAAA==\"]Ao[\"AAAAAAAAAAGYDwAAAAH7CwAA\",\"\"]Jk[\"AAAAAU0FAAAABSEAAACAAAAApAUAAJAPAACSDwAAAo4LAAARDwAABPoCAADRCwAAkQ8AAJMPAAAE3gAAAN8AAABNEAAAThAAAAL3CwAA+AsAAAJvEAAAcBAAAA==\",\"AAAAAk0FAAClDwAAAoMBAACJDwAAAAABHwkAAA==\"]Og[\"AAAAAYUFAAAABUcAAACNAAAApQUAAJQPAACWDwAABkMBAABEAQAAjwsAAJALAACRCwAAHg8AAAQHAwAA0gsAAJUPAACXDwAAAk8QAABQEAAAAvkLAAD6CwAAAnEQAAByEAAA\",\"AAAABIUFAADaBwAA2wcAAKYPAAAEkwEAAIoPAACLDwAAjA8AAAAAASAJAAACsAUAALEFAAAAAAAAAAAAAAA7MAAAAQAACgACANwHBQD7BwQA\"]Ck[\"AAABrAUAAAFOBQAAAAEiAAAAAAAB4AAAAA==\",\"AAAAAk4FAADTCwAA\"]Dk[\"AAABrQUAAAGGBQAAAAFIAAAAAUUBAAAAAAAAAAAAAAABBAcAAA==\",\"AAAAAoYFAADUCwAA\"]Cc[\"AAAAAZwFAAAAAVMAAAABbAEAAA==\",\"AAAAApwFAADVCwAA\"]Ak[\"AAAAAAAAAAABaBEAAA==\",\"\"]Ak[\"AAAAAAAAAAABaxEAAA==\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlwAAADQAQAA\",\"\"]Bg[\"AAAAAAAAAAAAAfcDAAA=\",\"AAAAAREQAAA=\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAlsAAABCBwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAQSBwAAEwcAABQHAAAVBwAA\",\"\"]Ec[\"AAAABG0BAAB4AQAAXwUAAH0PAAAAAAEEAQAAAAAABBYHAAAXBwAAGAcAABkHAAA=\",\"AAAAAV8FAAA=\"]Bg[\"AAAAAAAAAAAAAfgDAAA=\",\"AAAAAAHvEQAA\"]Cc[\"AAAAAS0KAAACLgoAAJQKAAABkgoAAAK9DwAAvw8AAA==\",\"\"]Ag[\"AAAAAAABMwcAAA==\",\"\"]No[\"AAAAAUUFAAABMQcAAAGwEQAAAeEOAAAG4AsAAJ0PAABCEgAAVhIAAK8SAADWEgAAB9EAAAB9AQAAOgwAADEPAACLEQAATRIAAE8SAAABuRIAAAAAAAAAAAAAA+MGAAA+DAAAjxEAAA==\",\"AAAAAkUFAAA5DwAAAAGBAQAAAZERAAAG4AsAAJ0PAABCEgAAVhIAAK8SAADWEgAABToMAAAxDwAAixEAAE0SAABPEgAA\"]Bo[\"AAAAATkPAAAAAYEBAAAAApEPAAAYEAAAAAH3CwAA\",\"\"]Nc[\"AAAAAXcFAAAAAAEpAQAAB+ELAACeDwAAsREAAEMSAABXEgAAsBIAANcSAAAFOwwAADIPAACNEQAAThIAAFASAAAAAYsBAAAAAAAAAAAABPwFAADnBgAAPwwAAJARAAA=\",\"AAAAAncFAAA6DwAAAAGQAQAAAZIRAAAG4QsAAJ4PAABDEgAAVxIAALASAADXEgAABTsMAAAyDwAAjREAAE4SAABQEgAA\"]Bo[\"AAAAAToPAAAAAZABAAAAApUPAAAcEAAAAAH5CwAA\",\"\"]Ao[\"AAAAAAAAAAACTRIAAE4SAAA=\",\"\"]Ao[\"AAAAAAAAAAACTxIAAFASAAA=\",\"\"]Ec[\"AAABCgIAAAE1BQAAAAISAAAAOwcAAAK5AAAAugAAAA==\",\"AAABDAIAAAE1BQAAAAAAAAAAAAEACAAA\"]Bk[\"AAAAAaMBAAAAAAS0AAAAvgUAAOMLAABCEQAA\",\"\"]Ek[\"AAAAAwMSAAAEEgAABRIAAAMAEgAAARIAAAISAAAEvwsAAMALAADBCwAAwgsAAAS7CwAAvAsAAL0LAAC+CwAA\",\"\"]Cc[\"AAAAAAAAAAABZxEAAA==\",\"AAGoBwAAAAJ8AQAA6REAAA==\"]Cc[\"AAAAAAAAAAABahEAAA==\",\"AAG9BwAAAAKJAQAA6hEAAA==\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAAAAAEEBwAA\"]Cg[\"AAAAAasOAAAAAAFCAQAAAY8PAAA=\",\"AAHZBwAAAAAAAUwSAAA=\"]Ek[\"AAAAAS8FAAAAAAGlAAAAAAAAAAAAAAAAAAAE5hEAAO0RAAD/EQAAGhIAAA==\",\"AAGpBwAAAAIvBQAAxgUAAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA04JAAAvDwAABBMAAA==\",\"\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA08JAAAwDwAABRMAAA==\",\"\"]Bk[\"\",\"AAAAAAAAAAAAAAAAAAAAAAAAAioJAAAsCQAA\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAloAAACnBQAA\",\"\"]Ec[\"AAAAAVgFAAAAAYgBAAABHAUAAAJhEAAAchIAAAHvAAAA\",\"AAG7BwAAAAFYBQAAAAAAAmEQAAByEgAA\"]Ec[\"AAAAAZgFAAAAAZoBAAACZAEAACAFAAACaBAAAHMSAAA=\",\"AAHUBwAAAAGYBQAAAAAAAmgQAABzEgAA\"]Ag[\"\",\"AAAAAAABNxEAAA==\"]Co[\"AAAAAnIBAADaBwAAAAABOwEAAA==\",\"AAAAAAAAAAAAAAAAAAAAAZIBAAA=\"]Ac[\"\",\"AAAAAAGzEQAA\"]Cg[\"AAAAAAAAApERAACSEQAAAAABOhEAAA==\",\"AAAAAAAAAAFREgAA\"]Ac[\"AAAB/gEAAA==\",\"\"]Do[\"AAAAAWkFAAAAAjQAAACIAAAAAxEBAAASAQAAGQ8AAAECAwAA\",\"AAAAAWkFAAAAAAAB/gMAAA==\"]Ak[\"AAAAAAAClxIAAJgSAAA=\",\"\"]Ac[\"AAAAAaQBAAA=\",\"\"]Ac[\"AAAAAaYBAAA=\",\"\"]Eo[\"AAACsQIAAMQCAAABjgUAAAABTgAAAAJTAQAAVAEAAA==\",\"AAAAAY4FAAAAAAAF6QYAAOoGAADrBgAA7AYAAO0GAAA=\"]Fk[\"AAAABJ8PAACgDwAARRIAAEYSAAABrA4AAAHtDwAAATgRAAAAAAG5EgAAAAAAAAAAAAHwCwAA\",\"AAAAAAAAA0ERAABCEQAAQxEAAA==\"]Fc[\"AAAAAAAB1QYAAAKREQAAkhEAAAFKEAAAAAE6EQAAAAACkxIAAJQSAAAAAAAB9REAAA==\",\"AAAAAAAAAc4SAAABURIAAA==\"]Ag[\"\",\"AAAAAAABoQUAAA==\"]Co[\"AAAAAXEFAAAAAT0AAAACIAEAACEBAAA=\",\"AAAAAXEFAAAAAAAAAAHFDQAA\"]Bg[\"AAAAAAAAAAAAAAAAAAAAAAAAASMHAAA=\",\"\"]Jc[\"AAAAAkQFAAC1EgAAAAAAAi4RAADnEQAABNAAAAB9AQAA9QsAAPIRAAADUgkAAEcSAAC5EgAAAAAAAesPAAA=\",\"AAG2BwAAAAKAAQAARAUAAAAAAAIuEQAA5xEAAAL1CwAA8hEAAAJSCQAARxIAAAAAAAHrDwAA\"]Jg[\"AAAAAnYFAAC2EgAAAAABKAEAAAIvEQAA6BEAAAL2CwAA8xEAAAJTCQAASBIAAAGLAQAAAAAB7A8AAA==\",\"AAHOBwAAAAKPAQAAdgUAAAHdBgAAAAACLxEAAOgRAAAC9gsAAPMRAAACUwkAAEgSAAAAAAAB7A8AAA==\"]Ao[\"AAAAAAAAAakAAAAAAAH6AwAA\",\"\"]Ag[\"AAACXwIAANoCAAA=\",\"\"]Ak[\"AAAAAAAAAhYEAAAKBwAA\",\"\"]Ak[\"AAAAAAAAAhkEAAANBwAA\",\"\"]Ag[\"AAACXgIAANgCAAA=\",\"\"]Dk[\"AAAAATMFAAAAAhAAAAA5BwAAArIAAACzAAAA\",\"AAABAwIAAAEzBQAAAAAAAAAAAb8OAAA=\"]Dg[\"AAAAATAFAAAAAg0AAAA2BwAAAqwAAACtAAAA\",\"AAAB+wEAAAEwBQAAAAAAAAElDwAA\"]Co[\"AAABPhEAAAFtBQAAAAE5AAAAARkBAAA=\",\"AAHIBwAAARoJAAABbQUAAA==\"]Ec[\"AAAAAAABPxEAAAGdAAAAAAAAAAAAAAAAAAADDgQAABcJAADDDgAA\",\"AAAAAXoBAAAAAAAAAUESAAA=\"]Hk[\"AAACkAIAAKMCAAABhwUAAAABSQAAAAJGAQAARwEAAAAAAAAAAAAAAAAABNQBAACpBQAA6AYAACIHAAA=\",\"AAABtgIAAAKUAQAAhwUAAAAAAmESAABiEgAAAAACfxAAAIAQAAA=\"]Fc[\"AAAAA40PAAChDwAAehEAAAAChhEAAIgRAAADaQEAAFkSAABaEgAAAgcEAACNCwAAAAAAAAABAgQAAA==\",\"AAAAAZsBAAA=\"]Eg[\"AAAAAUoFAAAAAR4AAAAAAALYAAAA2QAAAAAAAAAAAAAAAAMeBwAAnw4AAKUOAAA=\",\"AAABNgIAAAFKBQAA\"]Eg[\"AAAAAXwFAAAAAUEAAAACMAEAADEBAAAAAAAAAAAAAAAAAAMgBwAAoQ4AAKYOAAA=\",\"AAABowIAAAF8BQAA\"]Hk[\"AAAF8gEAAP0BAAACAgAACAIAAAwCAAABPwUAAAACGgAAADwHAAAAAALJAAAAygAAAA==\",\"AAADIwIAACUCAAAmAgAAAj8FAADZBgAAAvoFAAB7DwAAAQ0SAAAB4Q4AAAFSEAAA\"]Ek[\"AAAAAW8FAAAAAjsAAAA9BwAAAhwBAAAdAQAA\",\"AAABkAIAAAFvBQAAAAAAAAAAAAAAAAAAAAACKAUAACMHAAA=\"]Bk[\"AAAAAAABjg8AAAFBAQAA\",\"AAAAAAAAAAGPDwAA\"]Ek[\"AAAAATwFAAAAAAAAAcUAAAAAAAAAAAAAAAAERgcAAEgHAAAbCQAAHQkAAA==\",\"AAGvBwAAAAI8BQAAxQUAAA==\"]Ek[\"AAAAAWcFAAAAAAEOAQAAAAAAAAAAAAAAAAAERwcAAEkHAAAcCQAAHgkAAA==\",\"AAHGBwAAAAFnBQAAAccFAAA=\"]Ag[\"AAACWQIAANICAAA=\",\"\"]Cg[\"AAAAAZ4FAAAAAAADsA4AAAsPAACiDwAA\",\"AAHYBwAAAAGeBQAA\"]Dc[\"AAAAAoEFAADbBwAAAAABPAEAAA==\",\"AAAAAYEFAAAAAAAAAAAAAAAAAAHxBgAA\"]Dg[\"AAAAAfYRAAABOw8AAAAAAALLAAAAThAAAA==\",\"AAEhCQAAAX8BAAAB9hEAAAE7DwAA\"]Dg[\"AAAAAfcRAAABPA8AAAABIwEAAAABUBAAAA==\",\"AAEiCQAAAY4BAAAB9xEAAAE8DwAA\"]Ac[\"\",\"AAAAAAG9EgAA\"]Ag[\"AAACWgIAANcCAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAADZhEAAGcRAABoEQAA\"]Bc[\"\",\"AAAAAAAAAAADaREAAGoRAABrEQAA\"]Ak[\"\",\"AAAAAYoRAAAAAAGIEgAA\"]ADc[\"AAAAAUAFAAAAAAA6MAAAAQAAAAAAFAAQAAAA8Qd5DLEO7w7yDiwPExCCEJUQgxHiEXoSzBLYEuQS5hIHEwsTDhMSExkTOjAAAAEAAAAAAAwAEAAAAMwAqg8PEHsRixGMEQ4SNxJKEu8SABMCExATAAABexAAAA==\",\"AAGyBwAAAAFABQAAAAAAOjAAAAEAAAAAABQAEAAAAPEHeQyxDu8O8g4sDxMQghCVEIMR4hF6EswS2BLkEuYSBxMLEw4TEhMZEzowAAABAAAAAAAJABAAAACqDw8QexEOEjcSShLvEgATAhMQEwAAAXsQAAA=\"]ADc[\"AAAAAXIFAAAAAAEkAQAAOjAAAAEAAAAAABQAEAAAAPIHegyyDvAO8w4tDxQQgxCWEIQR4xF7Es0S2RLlEucSCBMMEw8TExMaEzowAAABAAAAAAALABAAAACrDxAQfBGNEY4RDxI4EksS8BIBEwMTERMAAAF8EAAA\",\"AAHKBwAAAAFyBQAAAAAAOjAAAAEAAAAAABQAEAAAAPIHegyyDvAO8w4tDxQQgxCWEIQR4xF7Es0S2RLlEucSCBMMEw8TExMaEzowAAABAAAAAAAJABAAAACrDxAQfBEPEjgSSxLwEgETAxMREwAAAXwQAAA=\"]Ag[\"AAACMwIAAKACAAA=\",\"\"]Bk[\"AAAAAAAAAAAAAe8DAAA=\",\"AAAAAAAAAdgLAAA=\"]Do[\"AAAAAUsFAAAAAR8AAAAAAALaAAAA2wAAAAAAAAAAAAAAAAIfBwAAoA4AAA==\",\"AAAAAUsFAAA=\"]Do[\"AAAAAX0FAAAAAUIAAAACMgEAADMBAAAAAAAAAAAAAAAAAAIhBwAAog4AAA==\",\"AAAAAX0FAAA=\"]Ag[\"AAACXAIAANQCAAA=\",\"\"]Fc[\"AAAAAX8FAAAAAkMAAACLAAAAAjUBAAAcDwAAAgUDAACPDwAA\",\"AAAAAX8FAAAAAAAAAAAAAAABoAcAAAAAAAAB/AUAAA==\"]Ag[\"AAACYAIAANUCAAA=\",\"\"]Ag[\"AAACXQIAANMCAAA=\",\"\"]Ck[\"AAAAAAAAAAAAAAABGRAAAA==\",\"AAAAASoKAAABKwoAAAABvA8AAA==\"]Co[\"AAAAAZwBAAAAAAAAAAAAAR0QAAA=\",\"AAAAAAGRCgAAAZAKAAABvg8AAA==\"]Dc[\"AAAAAAAAAAKTDwAAGhAAAAAB+AsAAA==\",\"AAAAAS0KAAABLgoAAAABvQ8AAA==\"]Dg[\"AAAAAZ0BAAAAAAAClw8AAB4QAAAAAfoLAAA=\",\"AAAAAAGUCgAAAZIKAAABvw8AAA==\"]Cc[\"AAAAAAAAAhcEAAALBwAAAAHjAAAA\",\"AAG4BwAAAAGEAQAA\"]Cc[\"AAAAAAAAA0wBAAAaBAAADgcAAA==\",\"AAHRBwAAAAGWAQAA\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAAx0HAADhBwAA/wcAAA==\",\"\"]Ao[\"AAAAAAAAARkFAAAAAQATAAA=\",\"\"]Bk[\"AAAAAAAAAR0FAAAAAQETAAA=\",\"AAAAASEFAAA=\"]Eg[\"AAAAAAAAAAAB8AAAAAAAAAAAAAAAAANUCQAADA8AAKgRAAA=\",\"AAG8BwAAAATpBwAA6gcAAOsHAADsBwAA\"]Eg[\"AAAAAAAAAWUBAAAAAAAAAAAAAAAAAANVCQAADQ8AAKkRAAA=\",\"AAHVBwAAAATtBwAA7gcAAO8HAADwBwAA\"]Bc[\"AAAAAAABgQEAAAABSBAAAAE6DAAA\",\"\"]Bc[\"AAAAAAABkAEAAAABSRAAAAE7DAAA\",\"\"]Fo[\"AAAAAYQFAAAAAkYAAACMAAAAAz8BAABAAQAAHQ8AAAEGAwAA\",\"AAABsQIAAAGEBQAAAAAAAAKwBQAAsQUAAAAAAAAAAAKSAQAA8QYAAA==\"]Dg[\"AAAAAS0FAAAAAgwAAAA1BwAAAqIAAACjAAAA\",\"AAAB8gEAAAEtBQAAAAABEhAAAA==\"]Do[\"AAAAAV0FAAAAAiwAAACFAAAAAwABAAABAQAAFg8AAAH/AgAA\",\"AAAAAV0FAAAAAAAAAQYEAAA=\"]Ek[\"AAAAAUEFAAAAAAACUhIAAJ8SAAADzQAAAPUPAAA3EgAA\",\"AAGzBwAAAAFBBQAAAAAAAlISAACfEgAAAfUPAAA=\"]Ek[\"AAAAAXMFAAAAAAElAQAAAlMSAACgEgAAAvYPAAA4EgAA\",\"AAHLBwAAAAFzBQAAAAAAAlMSAACgEgAAAfYPAAA=\"]Bo[\"AAAAAAAAAAABsAUAAAAAAAAAAAKSAQAA8QYAAA==\",\"\"]Fg[\"AAAAAUIFAAAAAAAENAkAADYPAABzEAAABxIAAALOAAAAShIAAA==\",\"AAG0BwAAAAFCBQAAAAAABDQJAAA2DwAAcxAAAAcSAAA=\"]Fg[\"AAAAAXQFAAAAAAEmAQAABDUJAAA3DwAAdBAAAAgSAAABSxIAAA==\",\"AAHMBwAAAAF0BQAAAAAABDUJAAA3DwAAdBAAAAgSAAA=\"]Ag[\"\",\"AAAAAAAAAc4SAAA=\"]Bc[\"\",\"AAAAAAAAAAAAAAAAAAAAAAH1EQAA\"]Ag[\"\",\"AAAAAAAAAcAOAAA=\"]Co[\"AAAAAWIFAAAAATAAAAACBwEAAAgBAAA=\",\"AAAAAWIFAAAAAAAAATsRAAA=\"]Fg[\"AAABFwIAAAFPBQAAAAEjAAAAAAH5BgAAAuEAAADiAAAA\",\"AAACPAIAAOEGAAABTwUAAAAAAAHsBgAAAAAAAAAAAAAAAAGfDgAA\"]Gc[\"AAABdgIAAAKUAQAAiAUAAAABSgAAAAJIAQAASQEAAAH+BgAA\",\"AAACuAIAAOUGAAABiAUAAAAAAfIGAAAAAAAAAAAAAAAAAALUAQAAoQ4AAA==\"]Cc[\"AAAAAAAAAAAAAAAAAAAAAAAAA90HAAD8BwAA4AgAAA==\",\"\"]Cg[\"AAABOAIAAAAAAAAAAfMAAAA=\",\"AAAAAAAAAAKRDwAAkw8AAA==\"]Cg[\"AAABqAIAAAAAAAFaAQAA\",\"AAAAAAAAAAOVDwAAlw8AAJgPAAA=\"]Bo[\"AAAAAiEFAACdBQAAAbMRAAA=\",\"AAAAAp0FAACYEAAA\"]Ao[\"\",\"AAAAAAAAAAIhEAAAIhAAAA==\"]Io[\"AAAAAY8FAAAAAU8AAAABVQEAAAbRBQAAGBAAABoQAAAcEAAAHhAAACAQAAAAAAAFFxAAABkQAAAbEAAAHRAAAB8QAAAAAAAAAAABGBMAAA==\",\"AAAAAY8FAAAAAAReEgAAXxIAAGESAABiEgAAAAH/AwAA\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAikJAACFDwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAisJAACHDwAA\",\"\"]Co[\"AAAAAVkFAAAAAAAAAvQAAAD1AAAA\",\"AAAAAVkFAAAAAAK8DwAAvQ8AAA==\"]Dc[\"AAAAAZEFAAAAAAJbAQAAXAEAAA==\",\"AAAAApEFAABsEQAAAAACvg8AAL8PAAA=\"]Ac[\"\",\"AAABwwsAAA==\"]Ec[\"AAABMgIAAAE3BQAAAAEUAAAAAAACvQAAAL4AAAA=\",\"AAAAATcFAAAEJwkAAPcJAAD5CQAA+gkAAA==\"]Do[\"AAABoQIAAAFcBQAAAAEqAAAAAv0AAAD+AAAA\",\"AAAAAVwFAAAEKAkAAEQKAABFCgAARgoAAA==\"]Ak[\"\",\"AAAAAAAClxIAAJgSAAA=\"]Cg[\"AAAAAiwFAAA+CQAAAdwIAAAAAaEAAAA=\",\"AAAAAiwFAAA4DwAA\"]Cg[\"AAAAAYAHAAACdw8AAIkPAAAAAY4LAAAAAAAAAAAB6w8AAA==\",\"\"]Ck[\"AAAAAYEHAAACeA8AAIoPAAAAAo8LAACQCwAAAAAAAAAAAewPAAA=\",\"\"]Bc[\"AAAAAAAAAAAAAAAAApMSAACUEgAA\",\"\"]Fo[\"AAAAAUYFAAAAAAADCAQAAD8JAACvEgAAAtIAAAB2AQAA\",\"AAG3BwAAAAFGBQAAAAAAAggEAAA/CQAAAXYBAAAAAAAAAAAAAAABjxEAAA==\"]Fo[\"AAAAAXgFAAAAAAEqAQAAAwkEAABACQAAsBIAAAF3AQAA\",\"AAHPBwAAAAF4BQAAAAAAAgkEAABACQAAAXcBAAAAAAAAAAAAAAABkBEAAA==\"]Ec[\"AAAAAYIFAAAAAAE9AQAAAAAAAAAAAAAAAAAFcwEAANwHAAD7BwAA3wgAADwMAAA=\",\"AAAAAYIFAAA=\"]Dg[\"AAAAAAG9EgAAAAGeAAAAAAAAAAACkxIAAJQSAAAAAAAB9REAAA==\",\"AAABewEAAA==\"]Ag[\"AAACVgIAANkCAAA=\",\"\"]Bc[\"\",\"AAAAAAAAAAAAAAAAApMSAACUEgAA\"]Bk[\"AAAAAAAAAAAAAAABHxAAAA==\",\"AAAAAZwBAAA=\"]Cc[\"AAAAAAAAAAKYDwAAIBAAAAAB+wsAAA==\",\"AAAAAZ0BAAA=\"]Ag[\"AAACWwIAANwCAAA=\",\"\"]Dg[\"AAAAAAABJwAAAAAAAfEAAAA=\",\"AAAAAAAAAAAAAAAAAAAAA4kSAACKEgAAixIAAA==\"]Ck[\"AAAAAAABUQAAAAFYAQAA\",\"AAAAAAAAAAAAAAAAAAAAAowSAACNEgAA\"]Ag[\"AAACYQIAANECAAA=\",\"\"]Do[\"AAAAAYIHAAACeQ8AAIsPAAAAAZELAAABtA8AAA==\",\"AAKNAQAAvgcAAAEjCQAAAAAAAAG0DwAA\"]Ec[\"AAAAAooFAACDBwAAAnoPAACMDwAAAAFLAQAAAbUPAAA=\",\"AAHQBwAAAZUBAAABigUAAAAAAAG1DwAA\"]Bg[\"AAAAAAAAAAABsQUAAA==\",\"AAABjAEAAA==\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAioJAACGDwAA\",\"\"]Bk[\"AAAAAAAAAAAAAAAAAAAAAAAAAiwJAACIDwAA\",\"\"]") \ No newline at end of file diff --git a/pr/234/docs/set_up_connection/all.html b/pr/234/docs/set_up_connection/all.html new file mode 100644 index 000000000..93799f9a4 --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/constant.CLIENT_ADDR.html b/pr/234/docs/set_up_connection/constant.CLIENT_ADDR.html new file mode 100644 index 000000000..480b4b634 --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/constant.LOCALHOST_V4.html b/pr/234/docs/set_up_connection/constant.LOCALHOST_V4.html new file mode 100644 index 000000000..49f7c0f99 --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/constant.SERVER_ADDR.html b/pr/234/docs/set_up_connection/constant.SERVER_ADDR.html new file mode 100644 index 000000000..046828f6b --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/constant.SERVER_NAME.html b/pr/234/docs/set_up_connection/constant.SERVER_NAME.html new file mode 100644 index 000000000..84fc92733 --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/fn.client.html b/pr/234/docs/set_up_connection/fn.client.html new file mode 100644 index 000000000..0b2708bd9 --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/fn.main.html b/pr/234/docs/set_up_connection/fn.main.html new file mode 100644 index 000000000..23244837f --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/fn.server.html b/pr/234/docs/set_up_connection/fn.server.html new file mode 100644 index 000000000..2cfe5a51d --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/index.html b/pr/234/docs/set_up_connection/index.html new file mode 100644 index 000000000..9d78e0b7f --- /dev/null +++ b/pr/234/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/234/docs/set_up_connection/sidebar-items.js b/pr/234/docs/set_up_connection/sidebar-items.js new file mode 100644 index 000000000..3f14772bc --- /dev/null +++ b/pr/234/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/234/docs/settings.html b/pr/234/docs/settings.html new file mode 100644 index 000000000..0e08333cc --- /dev/null +++ b/pr/234/docs/settings.html @@ -0,0 +1 @@ +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/234/docs/src-files.js b/pr/234/docs/src-files.js new file mode 100644 index 000000000..bdda437df --- /dev/null +++ b/pr/234/docs/src-files.js @@ -0,0 +1,2 @@ +createSrcSidebar('[["bench",["",[],["lib.rs","stats.rs"]]],["bulk",["",[],["bulk.rs"]]],["certificate",["",[],["certificate.rs"]]],["data_transfer",["",[],["data-transfer.rs"]]],["iroh_quinn",["",[["runtime",[],["mod.rs","smol.rs","tokio.rs"]]],["connection.rs","endpoint.rs","incoming.rs","lib.rs","mutex.rs","path.rs","recv_stream.rs","send_stream.rs","work_limiter.rs"]]],["iroh_quinn_proto",["",[["config",[],["mod.rs","qlog.rs","transport.rs"]],["congestion",[["bbr",[],["bw_estimation.rs","min_max.rs","mod.rs"]]],["cubic.rs","new_reno.rs"]],["connection",[["streams",[],["mod.rs","recv.rs","send.rs","state.rs"]]],["ack_frequency.rs","assembler.rs","cid_state.rs","datagrams.rs","mod.rs","mtud.rs","pacing.rs","packet_builder.rs","packet_crypto.rs","paths.rs","qlog.rs","send_buffer.rs","spaces.rs","state.rs","stats.rs","timer.rs","transmit_buf.rs"]],["crypto",[],["ring_like.rs","rustls.rs"]],["range_set",[],["array_range_set.rs","mod.rs"]]],["address_discovery.rs","bloom_token_log.rs","cid_generator.rs","cid_queue.rs","coding.rs","congestion.rs","constant_time.rs","crypto.rs","endpoint.rs","frame.rs","iroh_hp.rs","lib.rs","packet.rs","shared.rs","token.rs","token_memory_cache.rs","transport_error.rs","transport_parameters.rs","varint.rs"]]],["iroh_quinn_udp",["",[["cmsg",[],["mod.rs","unix.rs"]]],["lib.rs","unix.rs"]]],["perf",["",[],["client.rs","lib.rs","noprotection.rs","server.rs","stats.rs"]]],["quinn_perf",["",[],["perf.rs"]]],["set_up_connection",["",[],["set-up-connection.rs"]]]]'); +//{"start":19,"fragment_lengths":[39,29,43,47,197,883,80,81,35,55]} \ No newline at end of file diff --git a/pr/234/docs/src/bench/lib.rs.html b/pr/234/docs/src/bench/lib.rs.html new file mode 100644 index 000000000..0c4b29daf --- /dev/null +++ b/pr/234/docs/src/bench/lib.rs.html @@ -0,0 +1,254 @@ +lib.rs - source

bench/
lib.rs

1use std::{
+2    convert::TryInto,
+3    net::{IpAddr, Ipv6Addr, SocketAddr},
+4    num::ParseIntError,
+5    str::FromStr,
+6    sync::Arc,
+7};
+8
+9use anyhow::{Context, Result};
+10use bytes::Bytes;
+11use clap::Parser;
+12use quinn::crypto::rustls::QuicClientConfig;
+13use rustls::{
+14    RootCertStore,
+15    pki_types::{CertificateDer, PrivateKeyDer},
+16};
+17use tokio::runtime::{Builder, Runtime};
+18use tracing::trace;
+19
+20pub mod stats;
+21
+22pub fn configure_tracing_subscriber() {
+23    tracing::subscriber::set_global_default(
+24        tracing_subscriber::FmtSubscriber::builder()
+25            .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
+26            .finish(),
+27    )
+28    .unwrap();
+29}
+30
+31/// Creates a server endpoint which runs on the given runtime
+32pub fn server_endpoint(
+33    rt: &tokio::runtime::Runtime,
+34    cert: CertificateDer<'static>,
+35    key: PrivateKeyDer<'static>,
+36    opt: &Opt,
+37) -> (SocketAddr, quinn::Endpoint) {
+38    let cert_chain = vec![cert];
+39    let mut server_config = quinn::ServerConfig::with_single_cert(cert_chain, key).unwrap();
+40    server_config.transport = Arc::new(transport_config(opt));
+41
+42    let endpoint = {
+43        let _guard = rt.enter();
+44        quinn::Endpoint::server(
+45            server_config,
+46            SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0),
+47        )
+48        .unwrap()
+49    };
+50    let server_addr = endpoint.local_addr().unwrap();
+51    (server_addr, endpoint)
+52}
+53
+54/// Create a client endpoint and client connection
+55pub async fn connect_client(
+56    server_addr: SocketAddr,
+57    server_cert: CertificateDer<'_>,
+58    opt: Opt,
+59) -> Result<(quinn::Endpoint, quinn::Connection)> {
+60    let endpoint =
+61        quinn::Endpoint::client(SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 0)).unwrap();
+62
+63    let mut roots = RootCertStore::empty();
+64    roots.add(server_cert)?;
+65
+66    let default_provider = rustls::crypto::ring::default_provider();
+67    let provider = rustls::crypto::CryptoProvider {
+68        cipher_suites: vec![opt.cipher.as_rustls()],
+69        ..default_provider
+70    };
+71
+72    let crypto = rustls::ClientConfig::builder_with_provider(provider.into())
+73        .with_protocol_versions(&[&rustls::version::TLS13])
+74        .unwrap()
+75        .with_root_certificates(roots)
+76        .with_no_client_auth();
+77
+78    let mut client_config = quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(crypto)?));
+79    client_config.transport_config(Arc::new(transport_config(&opt)));
+80
+81    let connection = endpoint
+82        .connect_with(client_config, server_addr, "localhost")
+83        .unwrap()
+84        .await
+85        .context("unable to connect")?;
+86    trace!("connected");
+87
+88    Ok((endpoint, connection))
+89}
+90
+91pub async fn drain_stream(stream: &mut quinn::RecvStream, read_unordered: bool) -> Result<usize> {
+92    let mut read = 0;
+93
+94    if read_unordered {
+95        while let Some(chunk) = stream.read_chunk(usize::MAX, false).await? {
+96            read += chunk.bytes.len();
+97        }
+98    } else {
+99        // These are 32 buffers, for reading approximately 32kB at once
+100        #[rustfmt::skip]
+101        let mut bufs = [
+102            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+103            Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+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        ];
+111
+112        while let Some(n) = stream.read_chunks(&mut bufs[..]).await? {
+113            read += bufs.iter().take(n).map(|buf| buf.len()).sum::<usize>();
+114        }
+115    }
+116
+117    Ok(read)
+118}
+119
+120pub async fn send_data_on_stream(stream: &mut quinn::SendStream, stream_size: u64) -> Result<()> {
+121    const DATA: &[u8] = &[0xAB; 1024 * 1024];
+122    let bytes_data = Bytes::from_static(DATA);
+123
+124    let full_chunks = stream_size / (DATA.len() as u64);
+125    let remaining = (stream_size % (DATA.len() as u64)) as usize;
+126
+127    for _ in 0..full_chunks {
+128        stream
+129            .write_chunk(bytes_data.clone())
+130            .await
+131            .context("failed sending data")?;
+132    }
+133
+134    if remaining != 0 {
+135        stream
+136            .write_chunk(bytes_data.slice(0..remaining))
+137            .await
+138            .context("failed sending data")?;
+139    }
+140
+141    stream.finish().unwrap();
+142    // Wait for stream to close
+143    _ = stream.stopped().await;
+144
+145    Ok(())
+146}
+147
+148pub fn rt() -> Runtime {
+149    Builder::new_current_thread().enable_all().build().unwrap()
+150}
+151
+152pub fn transport_config(opt: &Opt) -> quinn::TransportConfig {
+153    // High stream windows are chosen because the amount of concurrent streams
+154    // is configurable as a parameter.
+155    let mut config = quinn::TransportConfig::default();
+156    config.max_concurrent_uni_streams(opt.max_streams.try_into().unwrap());
+157    config.initial_mtu(opt.initial_mtu);
+158
+159    let mut acks = quinn::AckFrequencyConfig::default();
+160    acks.ack_eliciting_threshold(10u32.into());
+161    config.ack_frequency_config(Some(acks));
+162
+163    config
+164}
+165
+166#[derive(Parser, Debug, Clone, Copy)]
+167#[clap(name = "bulk")]
+168pub struct Opt {
+169    /// The total number of clients which should be created
+170    #[clap(long = "clients", short = 'c', default_value = "1")]
+171    pub clients: usize,
+172    /// The total number of streams which should be created
+173    #[clap(long = "streams", short = 'n', default_value = "1")]
+174    pub streams: usize,
+175    /// The amount of concurrent streams which should be used
+176    #[clap(long = "max_streams", short = 'm', default_value = "1")]
+177    pub max_streams: usize,
+178    /// Number of bytes to transmit from server to client
+179    ///
+180    /// This can use SI suffixes for sizes. For example, 1M will transfer
+181    /// 1MiB, 10G will transfer 10GiB.
+182    #[clap(long, default_value = "1G", value_parser = parse_byte_size)]
+183    pub download_size: u64,
+184    /// Number of bytes to transmit from client to server
+185    ///
+186    /// This can use SI suffixes for sizes. For example, 1M will transfer
+187    /// 1MiB, 10G will transfer 10GiB.
+188    #[clap(long, default_value = "0", value_parser = parse_byte_size)]
+189    pub upload_size: u64,
+190    /// Show connection stats the at the end of the benchmark
+191    #[clap(long = "stats")]
+192    pub stats: bool,
+193    /// Whether to use the unordered read API
+194    #[clap(long = "unordered")]
+195    pub read_unordered: bool,
+196    /// Allows to configure the desired cipher suite
+197    ///
+198    /// Valid options are: aes128, aes256, chacha20
+199    #[clap(long = "cipher", default_value = "aes128")]
+200    pub cipher: CipherSuite,
+201    /// Starting guess for maximum UDP payload size
+202    #[clap(long, default_value = "1200")]
+203    pub initial_mtu: u16,
+204}
+205
+206fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+207    let s = s.trim();
+208
+209    let multiplier = match s.chars().last() {
+210        Some('T') => 1024 * 1024 * 1024 * 1024,
+211        Some('G') => 1024 * 1024 * 1024,
+212        Some('M') => 1024 * 1024,
+213        Some('k') => 1024,
+214        _ => 1,
+215    };
+216
+217    let s = match multiplier {
+218        1 => s,
+219        _ => &s[..s.len() - 1],
+220    };
+221
+222    Ok(u64::from_str(s)? * multiplier)
+223}
+224
+225#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+226pub enum CipherSuite {
+227    Aes128,
+228    Aes256,
+229    Chacha20,
+230}
+231
+232impl CipherSuite {
+233    pub fn as_rustls(self) -> rustls::SupportedCipherSuite {
+234        use rustls::crypto::ring::cipher_suite;
+235        match self {
+236            CipherSuite::Aes128 => cipher_suite::TLS13_AES_128_GCM_SHA256,
+237            CipherSuite::Aes256 => cipher_suite::TLS13_AES_256_GCM_SHA384,
+238            CipherSuite::Chacha20 => cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+239        }
+240    }
+241}
+242
+243impl FromStr for CipherSuite {
+244    type Err = anyhow::Error;
+245
+246    fn from_str(s: &str) -> Result<Self, Self::Err> {
+247        match s.to_lowercase().as_str() {
+248            "aes128" => Ok(CipherSuite::Aes128),
+249            "aes256" => Ok(CipherSuite::Aes256),
+250            "chacha20" => Ok(CipherSuite::Chacha20),
+251            _ => Err(anyhow::anyhow!("Unknown cipher suite {}", s)),
+252        }
+253    }
+254}
\ No newline at end of file diff --git a/pr/234/docs/src/bench/stats.rs.html b/pr/234/docs/src/bench/stats.rs.html new file mode 100644 index 000000000..959a01e82 --- /dev/null +++ b/pr/234/docs/src/bench/stats.rs.html @@ -0,0 +1,95 @@ +stats.rs - source

bench/
stats.rs

1use std::time::Duration;
+2
+3use hdrhistogram::Histogram;
+4
+5#[derive(Default)]
+6pub struct Stats {
+7    pub total_size: u64,
+8    pub total_duration: Duration,
+9    pub streams: usize,
+10    pub stream_stats: StreamStats,
+11}
+12
+13impl Stats {
+14    pub fn stream_finished(&mut self, stream_result: TransferResult) {
+15        self.total_size += stream_result.size;
+16        self.streams += 1;
+17
+18        self.stream_stats
+19            .duration_hist
+20            .record(stream_result.duration.as_millis() as u64)
+21            .unwrap();
+22        self.stream_stats
+23            .throughput_hist
+24            .record(stream_result.throughput as u64)
+25            .unwrap();
+26    }
+27
+28    pub fn print(&self, stat_name: &str) {
+29        println!("Overall {stat_name} stats:\n");
+30        println!(
+31            "Transferred {} bytes on {} streams in {:4.2?} ({:.2} MiB/s)\n",
+32            self.total_size,
+33            self.streams,
+34            self.total_duration,
+35            throughput_bps(self.total_duration, self.total_size) / 1024.0 / 1024.0
+36        );
+37
+38        println!("Stream {stat_name} metrics:\n");
+39
+40        println!("      │  Throughput   │ Duration ");
+41        println!("──────┼───────────────┼──────────");
+42
+43        let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
+44            println!(
+45                " {} │ {:7.2} MiB/s │ {:>9.2?}",
+46                label,
+47                get_metric(&self.stream_stats.throughput_hist) as f64 / 1024.0 / 1024.0,
+48                Duration::from_millis(get_metric(&self.stream_stats.duration_hist))
+49            );
+50        };
+51
+52        print_metric("AVG ", |hist| hist.mean() as u64);
+53        print_metric("P0  ", |hist| hist.value_at_quantile(0.00));
+54        print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
+55        print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
+56        print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
+57        print_metric("P100", |hist| hist.value_at_quantile(1.00));
+58    }
+59}
+60
+61pub struct StreamStats {
+62    pub duration_hist: Histogram<u64>,
+63    pub throughput_hist: Histogram<u64>,
+64}
+65
+66impl Default for StreamStats {
+67    fn default() -> Self {
+68        Self {
+69            duration_hist: Histogram::<u64>::new(3).unwrap(),
+70            throughput_hist: Histogram::<u64>::new(3).unwrap(),
+71        }
+72    }
+73}
+74
+75#[derive(Debug)]
+76pub struct TransferResult {
+77    pub duration: Duration,
+78    pub size: u64,
+79    pub throughput: f64,
+80}
+81
+82impl TransferResult {
+83    pub fn new(duration: Duration, size: u64) -> Self {
+84        let throughput = throughput_bps(duration, size);
+85        TransferResult {
+86            duration,
+87            size,
+88            throughput,
+89        }
+90    }
+91}
+92
+93pub fn throughput_bps(duration: Duration, size: u64) -> f64 {
+94    (size as f64) / (duration.as_secs_f64())
+95}
\ No newline at end of file diff --git a/pr/234/docs/src/bulk/bulk.rs.html b/pr/234/docs/src/bulk/bulk.rs.html new file mode 100644 index 000000000..4ede668ae --- /dev/null +++ b/pr/234/docs/src/bulk/bulk.rs.html @@ -0,0 +1,219 @@ +bulk.rs - source

bulk/
bulk.rs

1use std::{
+2    net::SocketAddr,
+3    sync::{Arc, Mutex},
+4    time::Instant,
+5};
+6
+7use anyhow::{Context, Result};
+8use clap::Parser;
+9use rustls::pki_types::{CertificateDer, PrivatePkcs8KeyDer};
+10use tokio::sync::Semaphore;
+11use tracing::{info, trace};
+12
+13use bench::{
+14    Opt, configure_tracing_subscriber, connect_client, drain_stream, rt, send_data_on_stream,
+15    server_endpoint,
+16    stats::{Stats, TransferResult},
+17};
+18
+19fn main() {
+20    let opt = Opt::parse();
+21    configure_tracing_subscriber();
+22
+23    let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
+24    let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
+25    let cert = CertificateDer::from(cert.cert);
+26
+27    let server_span = tracing::error_span!("server");
+28    let runtime = rt();
+29    let (server_addr, endpoint) = {
+30        let _guard = server_span.enter();
+31        server_endpoint(&runtime, cert.clone(), key.into(), &opt)
+32    };
+33
+34    let server_thread = std::thread::spawn(move || {
+35        let _guard = server_span.entered();
+36        if let Err(e) = runtime.block_on(server(endpoint, opt)) {
+37            eprintln!("server failed: {e:#}");
+38        }
+39    });
+40
+41    let mut handles = Vec::new();
+42    for id in 0..opt.clients {
+43        let cert = cert.clone();
+44        handles.push(std::thread::spawn(move || {
+45            let _guard = tracing::error_span!("client", id).entered();
+46            let runtime = rt();
+47            match runtime.block_on(client(server_addr, cert, opt)) {
+48                Ok(stats) => Ok(stats),
+49                Err(e) => {
+50                    eprintln!("client failed: {e:#}");
+51                    Err(e)
+52                }
+53            }
+54        }));
+55    }
+56
+57    for (id, handle) in handles.into_iter().enumerate() {
+58        // We print all stats at the end of the test sequentially to avoid
+59        // them being garbled due to being printed concurrently
+60        if let Ok(stats) = handle.join().expect("client thread") {
+61            stats.print(id);
+62        }
+63    }
+64
+65    server_thread.join().expect("server thread");
+66}
+67
+68async fn server(endpoint: quinn::Endpoint, opt: Opt) -> Result<()> {
+69    let mut server_tasks = Vec::new();
+70
+71    // Handle only the expected amount of clients
+72    for _ in 0..opt.clients {
+73        let handshake = endpoint.accept().await.unwrap();
+74        let connection = handshake.await.context("handshake failed")?;
+75
+76        server_tasks.push(tokio::spawn(async move {
+77            loop {
+78                let (mut send_stream, mut recv_stream) = match connection.accept_bi().await {
+79                    Err(quinn::ConnectionError::ApplicationClosed(_)) => break,
+80                    Err(e) => {
+81                        eprintln!("accepting stream failed: {e:?}");
+82                        break;
+83                    }
+84                    Ok(stream) => stream,
+85                };
+86                trace!("stream established");
+87
+88                tokio::spawn(async move {
+89                    drain_stream(&mut recv_stream, opt.read_unordered).await?;
+90                    send_data_on_stream(&mut send_stream, opt.download_size).await?;
+91                    Ok::<_, anyhow::Error>(())
+92                });
+93            }
+94
+95            if opt.stats {
+96                println!("\nServer connection stats:\n{:#?}", connection.stats());
+97            }
+98        }));
+99    }
+100
+101    // Await all the tasks. We have to do this to prevent the runtime getting dropped
+102    // and all server tasks to be cancelled
+103    for handle in server_tasks {
+104        if let Err(e) = handle.await {
+105            eprintln!("Server task error: {e:?}");
+106        };
+107    }
+108
+109    Ok(())
+110}
+111
+112async fn client(
+113    server_addr: SocketAddr,
+114    server_cert: CertificateDer<'static>,
+115    opt: Opt,
+116) -> Result<ClientStats> {
+117    let (endpoint, connection) = connect_client(server_addr, server_cert, opt).await?;
+118
+119    let start = Instant::now();
+120
+121    let connection = Arc::new(connection);
+122
+123    let mut stats = ClientStats::default();
+124    let mut first_error = None;
+125
+126    let sem = Arc::new(Semaphore::new(opt.max_streams));
+127    let results = Arc::new(Mutex::new(Vec::new()));
+128    for _ in 0..opt.streams {
+129        let permit = sem.clone().acquire_owned().await.unwrap();
+130        let results = results.clone();
+131        let connection = connection.clone();
+132        tokio::spawn(async move {
+133            let result =
+134                handle_client_stream(connection, opt.upload_size, opt.read_unordered).await;
+135            info!("stream finished: {:?}", result);
+136            results.lock().unwrap().push(result);
+137            drop(permit);
+138        });
+139    }
+140
+141    // Wait for remaining streams to finish
+142    let _ = sem.acquire_many(opt.max_streams as u32).await.unwrap();
+143
+144    for result in results.lock().unwrap().drain(..) {
+145        match result {
+146            Ok((upload_result, download_result)) => {
+147                stats.upload_stats.stream_finished(upload_result);
+148                stats.download_stats.stream_finished(download_result);
+149            }
+150            Err(e) => {
+151                if first_error.is_none() {
+152                    first_error = Some(e);
+153                }
+154            }
+155        }
+156    }
+157
+158    stats.upload_stats.total_duration = start.elapsed();
+159    stats.download_stats.total_duration = start.elapsed();
+160
+161    // Explicit close of the connection, since handles can still be around due
+162    // to `Arc`ing them
+163    connection.close(0u32.into(), b"Benchmark done");
+164
+165    endpoint.wait_idle().await;
+166
+167    if opt.stats {
+168        println!("\nClient connection stats:\n{:#?}", connection.stats());
+169    }
+170
+171    match first_error {
+172        None => Ok(stats),
+173        Some(e) => Err(e),
+174    }
+175}
+176
+177async fn handle_client_stream(
+178    connection: Arc<quinn::Connection>,
+179    upload_size: u64,
+180    read_unordered: bool,
+181) -> Result<(TransferResult, TransferResult)> {
+182    let start = Instant::now();
+183
+184    let (mut send_stream, mut 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(&mut recv_stream, read_unordered).await?;
+195    let download_result = TransferResult::new(start.elapsed(), size as u64);
+196
+197    Ok((upload_result, download_result))
+198}
+199
+200#[derive(Default)]
+201struct ClientStats {
+202    upload_stats: Stats,
+203    download_stats: Stats,
+204}
+205
+206impl ClientStats {
+207    pub fn print(&self, client_id: usize) {
+208        println!();
+209        println!("Client {client_id} stats:");
+210
+211        if self.upload_stats.total_size != 0 {
+212            self.upload_stats.print("upload");
+213        }
+214
+215        if self.download_stats.total_size != 0 {
+216            self.download_stats.print("download");
+217        }
+218    }
+219}
\ No newline at end of file diff --git a/pr/234/docs/src/certificate/certificate.rs.html b/pr/234/docs/src/certificate/certificate.rs.html new file mode 100644 index 000000000..e8d65d513 --- /dev/null +++ b/pr/234/docs/src/certificate/certificate.rs.html @@ -0,0 +1,106 @@ +certificate.rs - source

certificate/
certificate.rs

1use std::{error::Error, sync::Arc};
+2
+3use quinn::{
+4    ClientConfig,
+5    crypto::rustls::{NoInitialCipherSuite, QuicClientConfig},
+6};
+7use rustls::{
+8    DigitallySignedStruct, SignatureScheme,
+9    client::danger,
+10    crypto::{CryptoProvider, verify_tls12_signature, verify_tls13_signature},
+11    pki_types::{
+12        CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime, pem::PemObject,
+13    },
+14};
+15
+16#[allow(unused_variables)]
+17fn main() {
+18    let (self_signed_certs, self_signed_key) = generate_self_signed_cert().unwrap();
+19    let (certs, key) = read_certs_from_file().unwrap();
+20    let server_config = quinn::ServerConfig::with_single_cert(certs, key);
+21    let client_config = quinn::ClientConfig::try_with_platform_verifier().unwrap();
+22}
+23
+24#[allow(dead_code)] // Included in `certificate.md`
+25fn configure_client() -> Result<ClientConfig, NoInitialCipherSuite> {
+26    let crypto = rustls::ClientConfig::builder()
+27        .dangerous()
+28        .with_custom_certificate_verifier(SkipServerVerification::new())
+29        .with_no_client_auth();
+30
+31    Ok(ClientConfig::new(Arc::new(QuicClientConfig::try_from(
+32        crypto,
+33    )?)))
+34}
+35
+36// Implementation of `ServerCertVerifier` that verifies everything as trustworthy.
+37#[derive(Debug)]
+38struct SkipServerVerification(Arc<CryptoProvider>);
+39
+40impl SkipServerVerification {
+41    fn new() -> Arc<Self> {
+42        Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
+43    }
+44}
+45
+46impl danger::ServerCertVerifier for SkipServerVerification {
+47    fn verify_server_cert(
+48        &self,
+49        _end_entity: &CertificateDer<'_>,
+50        _intermediates: &[CertificateDer<'_>],
+51        _server_name: &ServerName<'_>,
+52        _ocsp: &[u8],
+53        _now: UnixTime,
+54    ) -> Result<danger::ServerCertVerified, rustls::Error> {
+55        Ok(danger::ServerCertVerified::assertion())
+56    }
+57    fn verify_tls12_signature(
+58        &self,
+59        message: &[u8],
+60        cert: &CertificateDer<'_>,
+61        dss: &DigitallySignedStruct,
+62    ) -> Result<danger::HandshakeSignatureValid, rustls::Error> {
+63        verify_tls12_signature(
+64            message,
+65            cert,
+66            dss,
+67            &self.0.signature_verification_algorithms,
+68        )
+69    }
+70
+71    fn verify_tls13_signature(
+72        &self,
+73        message: &[u8],
+74        cert: &CertificateDer<'_>,
+75        dss: &DigitallySignedStruct,
+76    ) -> Result<danger::HandshakeSignatureValid, rustls::Error> {
+77        verify_tls13_signature(
+78            message,
+79            cert,
+80            dss,
+81            &self.0.signature_verification_algorithms,
+82        )
+83    }
+84
+85    fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
+86        self.0.signature_verification_algorithms.supported_schemes()
+87    }
+88}
+89
+90fn generate_self_signed_cert()
+91-> Result<(CertificateDer<'static>, PrivatePkcs8KeyDer<'static>), Box<dyn Error>> {
+92    let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])?;
+93    let cert_der = CertificateDer::from(cert.cert);
+94    let key = PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der());
+95    Ok((cert_der, key))
+96}
+97
+98fn read_certs_from_file()
+99-> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Box<dyn Error>> {
+100    let certs = CertificateDer::pem_file_iter("./fullchain.pem")
+101        .unwrap()
+102        .map(|cert| cert.unwrap())
+103        .collect();
+104    let key = PrivateKeyDer::from_pem_file("./privkey.pem").unwrap();
+105    Ok((certs, key))
+106}
\ No newline at end of file diff --git a/pr/234/docs/src/data_transfer/data-transfer.rs.html b/pr/234/docs/src/data_transfer/data-transfer.rs.html new file mode 100644 index 000000000..fb76e6aea --- /dev/null +++ b/pr/234/docs/src/data_transfer/data-transfer.rs.html @@ -0,0 +1,56 @@ +data-transfer.rs - source

data_transfer/
data-transfer.rs

1use bytes::Bytes;
+2use quinn::Connection;
+3
+4fn main() {}
+5
+6#[allow(dead_code, unused_variables)] // Included in `data-transfer.md`
+7async fn open_bidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+8    let (mut send, mut recv) = connection.open_bi().await?;
+9    send.write_all(b"test").await?;
+10    send.finish()?;
+11    let received = recv.read_to_end(10).await?;
+12    Ok(())
+13}
+14
+15#[allow(dead_code)] // Included in `data-transfer.md`
+16async fn receive_bidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+17    while let Ok((mut send, mut recv)) = connection.accept_bi().await {
+18        // Because it is a bidirectional stream, we can both send and receive.
+19        println!("request: {:?}", recv.read_to_end(50).await?);
+20        send.write_all(b"response").await?;
+21        send.finish()?;
+22    }
+23    Ok(())
+24}
+25
+26#[allow(dead_code)] // Included in `data-transfer.md`
+27async fn open_unidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+28    let mut send = connection.open_uni().await?;
+29    send.write_all(b"test").await?;
+30    send.finish()?;
+31    Ok(())
+32}
+33
+34#[allow(dead_code)] // Included in `data-transfer.md`
+35async fn receive_unidirectional_stream(connection: Connection) -> anyhow::Result<()> {
+36    while let Ok(mut recv) = connection.accept_uni().await {
+37        // Because it is a unidirectional stream, we can only receive not send back.
+38        println!("{:?}", recv.read_to_end(50).await?);
+39    }
+40    Ok(())
+41}
+42
+43#[allow(dead_code)] // Included in `data-transfer.md`
+44async fn send_unreliable(connection: Connection) -> anyhow::Result<()> {
+45    connection.send_datagram(Bytes::from(&b"test"[..]))?;
+46    Ok(())
+47}
+48
+49#[allow(dead_code)] // Included in `data-transfer.md`
+50async fn receive_datagram(connection: Connection) -> anyhow::Result<()> {
+51    while let Ok(received_bytes) = connection.read_datagram().await {
+52        // Because it is a unidirectional stream, we can only receive not send back.
+53        println!("request: {:?}", received_bytes);
+54    }
+55    Ok(())
+56}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/connection.rs.html b/pr/234/docs/src/iroh_quinn/connection.rs.html new file mode 100644 index 000000000..965bb4de4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/connection.rs.html @@ -0,0 +1,1731 @@ +connection.rs - source

iroh_quinn/
connection.rs

1use std::{
+2    any::Any,
+3    fmt,
+4    future::Future,
+5    io,
+6    net::{IpAddr, SocketAddr},
+7    pin::Pin,
+8    sync::{Arc, Weak},
+9    task::{Context, Poll, Waker, ready},
+10};
+11
+12use bytes::Bytes;
+13use pin_project_lite::pin_project;
+14use rustc_hash::FxHashMap;
+15use thiserror::Error;
+16use tokio::sync::{Notify, futures::Notified, mpsc, oneshot, watch};
+17use tracing::{Instrument, Span, debug_span};
+18
+19use crate::{
+20    ConnectionEvent, Duration, Instant, Path, VarInt,
+21    endpoint::ensure_ipv6,
+22    mutex::Mutex,
+23    path::OpenPath,
+24    recv_stream::RecvStream,
+25    runtime::{AsyncTimer, Runtime, UdpSender},
+26    send_stream::SendStream,
+27    udp_transmit,
+28};
+29use proto::{
+30    ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, PathError, PathEvent,
+31    PathId, PathStats, PathStatus, Side, StreamEvent, StreamId, TransportError, TransportErrorCode,
+32    congestion::Controller, iroh_hp,
+33};
+34
+35/// In-progress connection attempt future
+36#[derive(Debug)]
+37pub struct Connecting {
+38    conn: Option<ConnectionRef>,
+39    connected: oneshot::Receiver<bool>,
+40    handshake_data_ready: Option<oneshot::Receiver<()>>,
+41}
+42
+43impl Connecting {
+44    pub(crate) fn new(
+45        handle: ConnectionHandle,
+46        conn: proto::Connection,
+47        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+48        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+49        sender: Pin<Box<dyn UdpSender>>,
+50        runtime: Arc<dyn Runtime>,
+51    ) -> Self {
+52        let (on_handshake_data_send, on_handshake_data_recv) = oneshot::channel();
+53        let (on_connected_send, on_connected_recv) = oneshot::channel();
+54
+55        let conn = ConnectionRef(Arc::new(ConnectionInner {
+56            state: Mutex::new(State::new(
+57                conn,
+58                handle,
+59                endpoint_events,
+60                conn_events,
+61                on_handshake_data_send,
+62                on_connected_send,
+63                sender,
+64                runtime.clone(),
+65            )),
+66            shared: Shared::default(),
+67        }));
+68
+69        let driver = ConnectionDriver(conn.clone());
+70        runtime.spawn(Box::pin(
+71            async {
+72                if let Err(e) = driver.await {
+73                    tracing::error!("I/O error: {e}");
+74                }
+75            }
+76            .instrument(Span::current()),
+77        ));
+78
+79        Self {
+80            conn: Some(conn),
+81            connected: on_connected_recv,
+82            handshake_data_ready: Some(on_handshake_data_recv),
+83        }
+84    }
+85
+86    /// Convert into a 0-RTT or 0.5-RTT connection at the cost of weakened security
+87    ///
+88    /// Returns `Ok` immediately if the local endpoint is able to attempt sending 0/0.5-RTT data.
+89    /// If so, the returned [`Connection`] can be used to send application data without waiting for
+90    /// the rest of the handshake to complete, at the cost of weakened cryptographic security
+91    /// guarantees. The returned [`ZeroRttAccepted`] future resolves when the handshake does
+92    /// complete, at which point subsequently opened streams and written data will have full
+93    /// cryptographic protection.
+94    ///
+95    /// ## Outgoing
+96    ///
+97    /// For outgoing connections, the initial attempt to convert to a [`Connection`] which sends
+98    /// 0-RTT data will proceed if the [`crypto::ClientConfig`][crate::crypto::ClientConfig]
+99    /// attempts to resume a previous TLS session. However, **the remote endpoint may not actually
+100    /// _accept_ the 0-RTT data**--yet still accept the connection attempt in general. This
+101    /// possibility is conveyed through the [`ZeroRttAccepted`] future--when the handshake
+102    /// completes, it resolves to true if the 0-RTT data was accepted and false if it was rejected.
+103    /// If it was rejected, the existence of streams opened and other application data sent prior
+104    /// to the handshake completing will not be conveyed to the remote application, and local
+105    /// operations on them will return `ZeroRttRejected` errors.
+106    ///
+107    /// A server may reject 0-RTT data at its discretion, but accepting 0-RTT data requires the
+108    /// relevant resumption state to be stored in the server, which servers may limit or lose for
+109    /// various reasons including not persisting resumption state across server restarts.
+110    ///
+111    /// If manually providing a [`crypto::ClientConfig`][crate::crypto::ClientConfig], check your
+112    /// implementation's docs for 0-RTT pitfalls.
+113    ///
+114    /// ## Incoming
+115    ///
+116    /// For incoming connections, conversion to 0.5-RTT will always fully succeed. `into_0rtt` will
+117    /// always return `Ok` and the [`ZeroRttAccepted`] will always resolve to true.
+118    ///
+119    /// If manually providing a [`crypto::ServerConfig`][crate::crypto::ServerConfig], check your
+120    /// implementation's docs for 0-RTT pitfalls.
+121    ///
+122    /// ## Security
+123    ///
+124    /// On outgoing connections, this enables transmission of 0-RTT data, which is vulnerable to
+125    /// replay attacks, and should therefore never invoke non-idempotent operations.
+126    ///
+127    /// On incoming connections, this enables transmission of 0.5-RTT data, which may be sent
+128    /// before TLS client authentication has occurred, and should therefore not be used to send
+129    /// data for which client authentication is being used.
+130    pub fn into_0rtt(mut self) -> Result<(Connection, ZeroRttAccepted), Self> {
+131        // This lock borrows `self` and would normally be dropped at the end of this scope, so we'll
+132        // have to release it explicitly before returning `self` by value.
+133        let conn = (self.conn.as_mut().unwrap()).state.lock("into_0rtt");
+134
+135        let is_ok = conn.inner.has_0rtt() || conn.inner.side().is_server();
+136        drop(conn);
+137
+138        if is_ok {
+139            let conn = self.conn.take().unwrap();
+140            Ok((Connection(conn), ZeroRttAccepted(self.connected)))
+141        } else {
+142            Err(self)
+143        }
+144    }
+145
+146    /// Parameters negotiated during the handshake
+147    ///
+148    /// The dynamic type returned is determined by the configured
+149    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+150    /// be [`downcast`](Box::downcast) to a
+151    /// [`crypto::rustls::HandshakeData`](crate::crypto::rustls::HandshakeData).
+152    pub async fn handshake_data(&mut self) -> Result<Box<dyn Any>, ConnectionError> {
+153        // Taking &mut self allows us to use a single oneshot channel rather than dealing with
+154        // potentially many tasks waiting on the same event. It's a bit of a hack, but keeps things
+155        // simple.
+156        if let Some(x) = self.handshake_data_ready.take() {
+157            let _ = x.await;
+158        }
+159        let conn = self.conn.as_ref().unwrap();
+160        let inner = conn.state.lock("handshake");
+161        inner
+162            .inner
+163            .crypto_session()
+164            .handshake_data()
+165            .ok_or_else(|| {
+166                inner
+167                    .error
+168                    .clone()
+169                    .expect("spurious handshake data ready notification")
+170            })
+171    }
+172
+173    /// The local IP address which was used when the peer established
+174    /// the connection
+175    ///
+176    /// This can be different from the address the endpoint is bound to, in case
+177    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+178    ///
+179    /// This will return `None` for clients, or when the platform does not expose this
+180    /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+181    /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
+182    ///
+183    /// Will panic if called after `poll` has returned `Ready`.
+184    pub fn local_ip(&self) -> Option<IpAddr> {
+185        let conn = self.conn.as_ref().unwrap();
+186        let inner = conn.state.lock("local_ip");
+187
+188        inner.inner.local_ip()
+189    }
+190
+191    /// The peer's UDP address
+192    ///
+193    /// Will panic if called after `poll` has returned `Ready`.
+194    pub fn remote_address(&self) -> SocketAddr {
+195        let conn_ref: &ConnectionRef = self.conn.as_ref().expect("used after yielding Ready");
+196        // TODO: another unwrap
+197        conn_ref
+198            .state
+199            .lock("remote_address")
+200            .inner
+201            .path_remote_address(PathId::ZERO)
+202            .expect("path exists when connecting")
+203    }
+204}
+205
+206impl Future for Connecting {
+207    type Output = Result<Connection, ConnectionError>;
+208    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+209        Pin::new(&mut self.connected).poll(cx).map(|_| {
+210            let conn = self.conn.take().unwrap();
+211            let inner = conn.state.lock("connecting");
+212            if inner.connected {
+213                drop(inner);
+214                Ok(Connection(conn))
+215            } else {
+216                Err(inner
+217                    .error
+218                    .clone()
+219                    .expect("connected signaled without connection success or error"))
+220            }
+221        })
+222    }
+223}
+224
+225/// Future that completes when a connection is fully established
+226///
+227/// For clients, the resulting value indicates if 0-RTT was accepted. For servers, the resulting
+228/// value is meaningless.
+229pub struct ZeroRttAccepted(oneshot::Receiver<bool>);
+230
+231impl Future for ZeroRttAccepted {
+232    type Output = bool;
+233    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+234        Pin::new(&mut self.0).poll(cx).map(|x| x.unwrap_or(false))
+235    }
+236}
+237
+238/// A future that drives protocol logic for a connection
+239///
+240/// This future handles the protocol logic for a single connection, routing events from the
+241/// `Connection` API object to the `Endpoint` task and the related stream-related interfaces.
+242/// It also keeps track of outstanding timeouts for the `Connection`.
+243///
+244/// If the connection encounters an error condition, this future will yield an error. It will
+245/// terminate (yielding `Ok(())`) if the connection was closed without error. Unlike other
+246/// connection-related futures, this waits for the draining period to complete to ensure that
+247/// packets still in flight from the peer are handled gracefully.
+248#[must_use = "connection drivers must be spawned for their connections to function"]
+249#[derive(Debug)]
+250struct ConnectionDriver(ConnectionRef);
+251
+252impl Future for ConnectionDriver {
+253    type Output = Result<(), io::Error>;
+254
+255    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+256        let conn = &mut *self.0.state.lock("poll");
+257
+258        let span = debug_span!("drive", id = conn.handle.0);
+259        let _guard = span.enter();
+260
+261        if let Err(e) = conn.process_conn_events(&self.0.shared, cx) {
+262            conn.terminate(e, &self.0.shared);
+263            return Poll::Ready(Ok(()));
+264        }
+265        let mut keep_going = conn.drive_transmit(cx)?;
+266        // If a timer expires, there might be more to transmit. When we transmit something, we
+267        // might need to reset a timer. Hence, we must loop until neither happens.
+268        keep_going |= conn.drive_timer(cx);
+269        conn.forward_endpoint_events();
+270        conn.forward_app_events(&self.0.shared);
+271
+272        if !conn.inner.is_drained() {
+273            if keep_going {
+274                // If the connection hasn't processed all tasks, schedule it again
+275                cx.waker().wake_by_ref();
+276            } else {
+277                conn.driver = Some(cx.waker().clone());
+278            }
+279            return Poll::Pending;
+280        }
+281        if conn.error.is_none() {
+282            unreachable!("drained connections always have an error");
+283        }
+284        Poll::Ready(Ok(()))
+285    }
+286}
+287
+288/// A QUIC connection.
+289///
+290/// If all references to a connection (including every clone of the `Connection` handle, streams of
+291/// incoming streams, and the various stream types) have been dropped, then the connection will be
+292/// automatically closed with an `error_code` of 0 and an empty `reason`. You can also close the
+293/// connection explicitly by calling [`Connection::close()`].
+294///
+295/// Closing the connection immediately abandons efforts to deliver data to the peer.  Upon
+296/// receiving CONNECTION_CLOSE the peer *may* drop any stream data not yet delivered to the
+297/// application. [`Connection::close()`] describes in more detail how to gracefully close a
+298/// connection without losing application data.
+299///
+300/// May be cloned to obtain another handle to the same connection.
+301///
+302/// [`Connection::close()`]: Connection::close
+303#[derive(Debug, Clone)]
+304pub struct Connection(ConnectionRef);
+305
+306impl Connection {
+307    /// Returns a weak reference to the inner connection struct.
+308    pub fn weak_handle(&self) -> WeakConnectionHandle {
+309        WeakConnectionHandle(Arc::downgrade(&self.0.0))
+310    }
+311
+312    /// Initiate a new outgoing unidirectional stream.
+313    ///
+314    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+315    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+316    /// actually used.
+317    pub fn open_uni(&self) -> OpenUni<'_> {
+318        OpenUni {
+319            conn: &self.0,
+320            notify: self.0.shared.stream_budget_available[Dir::Uni as usize].notified(),
+321        }
+322    }
+323
+324    /// Initiate a new outgoing bidirectional stream.
+325    ///
+326    /// Streams are cheap and instantaneous to open unless blocked by flow control. As a
+327    /// consequence, the peer won't be notified that a stream has been opened until the stream is
+328    /// actually used. Calling [`open_bi()`] then waiting on the [`RecvStream`] without writing
+329    /// anything to [`SendStream`] will never succeed.
+330    ///
+331    /// [`open_bi()`]: crate::Connection::open_bi
+332    /// [`SendStream`]: crate::SendStream
+333    /// [`RecvStream`]: crate::RecvStream
+334    pub fn open_bi(&self) -> OpenBi<'_> {
+335        OpenBi {
+336            conn: &self.0,
+337            notify: self.0.shared.stream_budget_available[Dir::Bi as usize].notified(),
+338        }
+339    }
+340
+341    /// Accept the next incoming uni-directional stream
+342    pub fn accept_uni(&self) -> AcceptUni<'_> {
+343        AcceptUni {
+344            conn: &self.0,
+345            notify: self.0.shared.stream_incoming[Dir::Uni as usize].notified(),
+346        }
+347    }
+348
+349    /// Accept the next incoming bidirectional stream
+350    ///
+351    /// **Important Note**: The `Connection` that calls [`open_bi()`] must write to its [`SendStream`]
+352    /// before the other `Connection` is able to `accept_bi()`. Calling [`open_bi()`] then
+353    /// waiting on the [`RecvStream`] without writing anything to [`SendStream`] will never succeed.
+354    ///
+355    /// [`accept_bi()`]: crate::Connection::accept_bi
+356    /// [`open_bi()`]: crate::Connection::open_bi
+357    /// [`SendStream`]: crate::SendStream
+358    /// [`RecvStream`]: crate::RecvStream
+359    pub fn accept_bi(&self) -> AcceptBi<'_> {
+360        AcceptBi {
+361            conn: &self.0,
+362            notify: self.0.shared.stream_incoming[Dir::Bi as usize].notified(),
+363        }
+364    }
+365
+366    /// Receive an application datagram
+367    pub fn read_datagram(&self) -> ReadDatagram<'_> {
+368        ReadDatagram {
+369            conn: &self.0,
+370            notify: self.0.shared.datagram_received.notified(),
+371        }
+372    }
+373
+374    /// Opens a new path if no path exists yet for the remote address.
+375    ///
+376    /// Otherwise behaves exactly as [`open_path`].
+377    ///
+378    /// [`open_path`]: Self::open_path
+379    pub fn open_path_ensure(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+380        let mut state = self.0.state.lock("open_path");
+381
+382        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+383        // If not, we do not support IPv6.  We can not access endpoint::State from here
+384        // however, but either all our paths use an IPv6 address, or all our paths use an
+385        // IPv4 address.  So we can use that information.
+386        let ipv6 = state
+387            .inner
+388            .paths()
+389            .iter()
+390            .filter_map(|id| {
+391                state
+392                    .inner
+393                    .path_remote_address(*id)
+394                    .map(|ip| ip.is_ipv6())
+395                    .ok()
+396            })
+397            .next()
+398            .unwrap_or_default();
+399        if addr.is_ipv6() && !ipv6 {
+400            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+401        }
+402        let addr = if ipv6 {
+403            SocketAddr::V6(ensure_ipv6(addr))
+404        } else {
+405            addr
+406        };
+407
+408        let now = state.runtime.now();
+409        let open_res = state.inner.open_path_ensure(addr, initial_status, now);
+410        state.wake();
+411        match open_res {
+412            Ok((path_id, existed)) if existed => {
+413                match state.open_path.get(&path_id).map(|tx| tx.subscribe()) {
+414                    Some(recv) => OpenPath::new(path_id, recv, self.0.clone()),
+415                    None => OpenPath::ready(path_id, self.0.clone()),
+416                }
+417            }
+418            Ok((path_id, _)) => {
+419                let (tx, rx) = watch::channel(Ok(()));
+420                state.open_path.insert(path_id, tx);
+421                drop(state);
+422                OpenPath::new(path_id, rx, self.0.clone())
+423            }
+424            Err(err) => OpenPath::rejected(err),
+425        }
+426    }
+427
+428    /// Opens an additional path if the multipath extension is negotiated.
+429    ///
+430    /// The returned future completes once the path is either fully opened and ready to
+431    /// carry application data, or if there was an error.
+432    ///
+433    /// Dropping the returned future does not cancel the opening of the path, the
+434    /// [`PathEvent::Opened`] event will still be emitted from [`Self::path_events`] if the
+435    /// path opens.  The [`PathId`] for the events can be extracted from
+436    /// [`OpenPath::path_id`].
+437    ///
+438    /// Failure to open a path can either occur immediately, before polling the returned
+439    /// future, or at a later time.  If the failure is immediate [`OpenPath::path_id`] will
+440    /// return `None` and the future will be ready immediately.  If the failure happens
+441    /// later, a [`PathEvent`] will be emitted.
+442    pub fn open_path(&self, addr: SocketAddr, initial_status: PathStatus) -> OpenPath {
+443        let mut state = self.0.state.lock("open_path");
+444
+445        // If endpoint::State::ipv6 is true we want to keep all our IP addresses as IPv6.
+446        // If not, we do not support IPv6.  We can not access endpoint::State from here
+447        // however, but either all our paths use an IPv6 address, or all our paths use an
+448        // IPv4 address.  So we can use that information.
+449        let ipv6 = state
+450            .inner
+451            .paths()
+452            .iter()
+453            .filter_map(|id| {
+454                state
+455                    .inner
+456                    .path_remote_address(*id)
+457                    .map(|ip| ip.is_ipv6())
+458                    .ok()
+459            })
+460            .next()
+461            .unwrap_or_default();
+462        if addr.is_ipv6() && !ipv6 {
+463            return OpenPath::rejected(PathError::InvalidRemoteAddress(addr));
+464        }
+465        let addr = if ipv6 {
+466            SocketAddr::V6(ensure_ipv6(addr))
+467        } else {
+468            addr
+469        };
+470
+471        let (on_open_path_send, on_open_path_recv) = watch::channel(Ok(()));
+472        let now = state.runtime.now();
+473        let open_res = state.inner.open_path(addr, initial_status, now);
+474        state.wake();
+475        match open_res {
+476            Ok(path_id) => {
+477                state.open_path.insert(path_id, on_open_path_send);
+478                drop(state);
+479                OpenPath::new(path_id, on_open_path_recv, self.0.clone())
+480            }
+481            Err(err) => OpenPath::rejected(err),
+482        }
+483    }
+484
+485    /// Returns the [`Path`] structure of an open path
+486    pub fn path(&self, id: PathId) -> Option<Path> {
+487        // TODO(flub): Using this to know if the path still exists is... hacky.
+488        self.0.state.lock("path").inner.path_status(id).ok()?;
+489        Some(Path {
+490            id,
+491            conn: self.0.clone(),
+492        })
+493    }
+494
+495    /// A broadcast receiver of [`PathEvent`]s for all paths in this connection
+496    pub fn path_events(&self) -> tokio::sync::broadcast::Receiver<PathEvent> {
+497        self.0.state.lock("path_events").path_events.subscribe()
+498    }
+499
+500    /// A broadcast receiver of [`iroh_hp::Event`]s for updates about server addresses
+501    pub fn nat_traversal_updates(&self) -> tokio::sync::broadcast::Receiver<iroh_hp::Event> {
+502        self.0
+503            .state
+504            .lock("nat_traversal_updates")
+505            .nat_traversal_updates
+506            .subscribe()
+507    }
+508
+509    /// Wait for the connection to be closed for any reason
+510    ///
+511    /// Despite the return type's name, closed connections are often not an error condition at the
+512    /// application layer. Cases that might be routine include [`ConnectionError::LocallyClosed`]
+513    /// and [`ConnectionError::ApplicationClosed`].
+514    pub async fn closed(&self) -> ConnectionError {
+515        {
+516            let conn = self.0.state.lock("closed");
+517            if let Some(error) = conn.error.as_ref() {
+518                return error.clone();
+519            }
+520            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+521            // the `Notify` is signaled immediately after we release the lock. `await` it after
+522            // the lock guard is out of scope.
+523            self.0.shared.closed.notified()
+524        }
+525        .await;
+526        self.0
+527            .state
+528            .lock("closed")
+529            .error
+530            .as_ref()
+531            .expect("closed without an error")
+532            .clone()
+533    }
+534
+535    /// Wait for the connection to be closed without keeping a strong reference to the connection
+536    ///
+537    /// Returns a future that resolves, once the connection is closed, to a tuple of
+538    /// ([`ConnectionError`], [`ConnectionStats`]).
+539    ///
+540    /// Calling [`Self::closed`] keeps the connection alive until it is either closed locally via [`Connection::close`]
+541    /// or closed by the remote peer. This function instead does not keep the connection itself alive,
+542    /// so if all *other* clones of the connection are dropped, the connection will be closed implicitly even
+543    /// if there are futures returned from this function still being awaited.
+544    pub fn on_closed(&self) -> OnClosed {
+545        let (tx, rx) = oneshot::channel();
+546        self.0.state.lock("on_closed").on_closed.push(tx);
+547        OnClosed {
+548            conn: self.weak_handle(),
+549            rx,
+550        }
+551    }
+552
+553    /// If the connection is closed, the reason why.
+554    ///
+555    /// Returns `None` if the connection is still open.
+556    pub fn close_reason(&self) -> Option<ConnectionError> {
+557        self.0.state.lock("close_reason").error.clone()
+558    }
+559
+560    /// Close the connection immediately.
+561    ///
+562    /// Pending operations will fail immediately with [`ConnectionError::LocallyClosed`]. No
+563    /// more data is sent to the peer and the peer may drop buffered data upon receiving
+564    /// the CONNECTION_CLOSE frame.
+565    ///
+566    /// `error_code` and `reason` are not interpreted, and are provided directly to the peer.
+567    ///
+568    /// `reason` will be truncated to fit in a single packet with overhead; to improve odds that it
+569    /// is preserved in full, it should be kept under 1KiB.
+570    ///
+571    /// # Gracefully closing a connection
+572    ///
+573    /// Only the peer last receiving application data can be certain that all data is
+574    /// delivered. The only reliable action it can then take is to close the connection,
+575    /// potentially with a custom error code. The delivery of the final CONNECTION_CLOSE
+576    /// frame is very likely if both endpoints stay online long enough, and
+577    /// [`Endpoint::wait_idle()`] can be used to provide sufficient time. Otherwise, the
+578    /// remote peer will time out the connection, provided that the idle timeout is not
+579    /// disabled.
+580    ///
+581    /// The sending side can not guarantee all stream data is delivered to the remote
+582    /// application. It only knows the data is delivered to the QUIC stack of the remote
+583    /// endpoint. Once the local side sends a CONNECTION_CLOSE frame in response to calling
+584    /// [`close()`] the remote endpoint may drop any data it received but is as yet
+585    /// undelivered to the application, including data that was acknowledged as received to
+586    /// the local endpoint.
+587    ///
+588    /// [`ConnectionError::LocallyClosed`]: crate::ConnectionError::LocallyClosed
+589    /// [`Endpoint::wait_idle()`]: crate::Endpoint::wait_idle
+590    /// [`close()`]: Connection::close
+591    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+592        let conn = &mut *self.0.state.lock("close");
+593        conn.close(error_code, Bytes::copy_from_slice(reason), &self.0.shared);
+594    }
+595
+596    /// Wait for the handshake to be confirmed.
+597    ///
+598    /// As a server, who must be authenticated by clients,
+599    /// this happens when the handshake completes
+600    /// upon receiving a TLS Finished message from the client.
+601    /// In return, the server send a HANDSHAKE_DONE frame.
+602    ///
+603    /// As a client, this happens when receiving a HANDSHAKE_DONE frame.
+604    /// At this point, the server has either accepted our authentication,
+605    /// or, if client authentication is not required, accepted our lack of authentication.
+606    pub async fn handshake_confirmed(&self) -> Result<(), ConnectionError> {
+607        {
+608            let conn = self.0.state.lock("handshake_confirmed");
+609            if let Some(error) = conn.error.as_ref() {
+610                return Err(error.clone());
+611            }
+612            if conn.handshake_confirmed {
+613                return Ok(());
+614            }
+615            // Construct the future while the lock is held to ensure we can't miss a wakeup if
+616            // the `Notify` is signaled immediately after we release the lock. `await` it after
+617            // the lock guard is out of scope.
+618            self.0.shared.handshake_confirmed.notified()
+619        }
+620        .await;
+621        if let Some(error) = self.0.state.lock("handshake_confirmed").error.as_ref() {
+622            Err(error.clone())
+623        } else {
+624            Ok(())
+625        }
+626    }
+627
+628    /// Transmit `data` as an unreliable, unordered application datagram
+629    ///
+630    /// Application datagrams are a low-level primitive. They may be lost or delivered out of order,
+631    /// and `data` must both fit inside a single QUIC packet and be smaller than the maximum
+632    /// dictated by the peer.
+633    ///
+634    /// Previously queued datagrams which are still unsent may be discarded to make space for this
+635    /// datagram, in order of oldest to newest.
+636    pub fn send_datagram(&self, data: Bytes) -> Result<(), SendDatagramError> {
+637        let conn = &mut *self.0.state.lock("send_datagram");
+638        if let Some(ref x) = conn.error {
+639            return Err(SendDatagramError::ConnectionLost(x.clone()));
+640        }
+641        use proto::SendDatagramError::*;
+642        match conn.inner.datagrams().send(data, true) {
+643            Ok(()) => {
+644                conn.wake();
+645                Ok(())
+646            }
+647            Err(e) => Err(match e {
+648                Blocked(..) => unreachable!(),
+649                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+650                Disabled => SendDatagramError::Disabled,
+651                TooLarge => SendDatagramError::TooLarge,
+652            }),
+653        }
+654    }
+655
+656    /// Transmit `data` as an unreliable, unordered application datagram
+657    ///
+658    /// Unlike [`send_datagram()`], this method will wait for buffer space during congestion
+659    /// conditions, which effectively prioritizes old datagrams over new datagrams.
+660    ///
+661    /// See [`send_datagram()`] for details.
+662    ///
+663    /// [`send_datagram()`]: Connection::send_datagram
+664    pub fn send_datagram_wait(&self, data: Bytes) -> SendDatagram<'_> {
+665        SendDatagram {
+666            conn: &self.0,
+667            data: Some(data),
+668            notify: self.0.shared.datagrams_unblocked.notified(),
+669        }
+670    }
+671
+672    /// Compute the maximum size of datagrams that may be passed to [`send_datagram()`].
+673    ///
+674    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+675    ///
+676    /// This may change over the lifetime of a connection according to variation in the path MTU
+677    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+678    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+679    ///
+680    /// Not necessarily the maximum size of received datagrams.
+681    ///
+682    /// [`send_datagram()`]: Connection::send_datagram
+683    pub fn max_datagram_size(&self) -> Option<usize> {
+684        self.0
+685            .state
+686            .lock("max_datagram_size")
+687            .inner
+688            .datagrams()
+689            .max_size()
+690    }
+691
+692    /// Bytes available in the outgoing datagram buffer
+693    ///
+694    /// When greater than zero, calling [`send_datagram()`](Self::send_datagram) with a datagram of
+695    /// at most this size is guaranteed not to cause older datagrams to be dropped.
+696    pub fn datagram_send_buffer_space(&self) -> usize {
+697        self.0
+698            .state
+699            .lock("datagram_send_buffer_space")
+700            .inner
+701            .datagrams()
+702            .send_buffer_space()
+703    }
+704
+705    /// The side of the connection (client or server)
+706    pub fn side(&self) -> Side {
+707        self.0.state.lock("side").inner.side()
+708    }
+709
+710    /// The peer's UDP address
+711    ///
+712    /// If [`ServerConfig::migration`] is `true`, clients may change addresses at will,
+713    /// e.g. when switching to a cellular internet connection.
+714    ///
+715    /// If [`multipath`] is enabled this will return the address of *any*
+716    /// path, and may not be consistent. Prefer [`Path::remote_address`] instead.
+717    ///
+718    /// [`ServerConfig::migration`]: crate::ServerConfig::migration
+719    /// [`multipath`]: crate::TransportConfig::max_concurrent_multipath_paths
+720    pub fn remote_address(&self) -> SocketAddr {
+721        // TODO: an unwrap again
+722        let state = self.0.state.lock("remote_address");
+723        state
+724            .inner
+725            .paths()
+726            .iter()
+727            .filter_map(|id| state.inner.path_remote_address(*id).ok())
+728            .next()
+729            .unwrap()
+730    }
+731
+732    /// The local IP address which was used when the peer established
+733    /// the connection
+734    ///
+735    /// This can be different from the address the endpoint is bound to, in case
+736    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+737    ///
+738    /// This will return `None` for clients, or when the platform does not expose this
+739    /// information. See [`quinn_udp::RecvMeta::dst_ip`](udp::RecvMeta::dst_ip) for a list of
+740    /// supported platforms when using [`quinn_udp`](udp) for I/O, which is the default.
+741    pub fn local_ip(&self) -> Option<IpAddr> {
+742        self.0.state.lock("local_ip").inner.local_ip()
+743    }
+744
+745    /// Current best estimate of this connection's latency (round-trip-time)
+746    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+747        self.0.state.lock("rtt").inner.rtt(path_id)
+748    }
+749
+750    /// Returns connection statistics
+751    pub fn stats(&self) -> ConnectionStats {
+752        self.0.state.lock("stats").inner.stats()
+753    }
+754
+755    /// Returns path statistics
+756    pub fn path_stats(&self, path_id: PathId) -> Option<PathStats> {
+757        self.0.state.lock("path_stats").inner.path_stats(path_id)
+758    }
+759
+760    /// Current state of the congestion control algorithm, for debugging purposes
+761    pub fn congestion_state(&self, path_id: PathId) -> Option<Box<dyn Controller>> {
+762        self.0
+763            .state
+764            .lock("congestion_state")
+765            .inner
+766            .congestion_state(path_id)
+767            .map(|c| c.clone_box())
+768    }
+769
+770    /// Parameters negotiated during the handshake
+771    ///
+772    /// Guaranteed to return `Some` on fully established connections or after
+773    /// [`Connecting::handshake_data()`] succeeds. See that method's documentations for details on
+774    /// the returned value.
+775    ///
+776    /// [`Connection::handshake_data()`]: crate::Connecting::handshake_data
+777    pub fn handshake_data(&self) -> Option<Box<dyn Any>> {
+778        self.0
+779            .state
+780            .lock("handshake_data")
+781            .inner
+782            .crypto_session()
+783            .handshake_data()
+784    }
+785
+786    /// Cryptographic identity of the peer
+787    ///
+788    /// The dynamic type returned is determined by the configured
+789    /// [`Session`](proto::crypto::Session). For the default `rustls` session, the return value can
+790    /// be [`downcast`](Box::downcast) to a <code>Vec<[rustls::pki_types::CertificateDer]></code>
+791    pub fn peer_identity(&self) -> Option<Box<dyn Any>> {
+792        self.0
+793            .state
+794            .lock("peer_identity")
+795            .inner
+796            .crypto_session()
+797            .peer_identity()
+798    }
+799
+800    /// A stable identifier for this connection
+801    ///
+802    /// Peer addresses and connection IDs can change, but this value will remain
+803    /// fixed for the lifetime of the connection.
+804    pub fn stable_id(&self) -> usize {
+805        self.0.stable_id()
+806    }
+807
+808    /// Update traffic keys spontaneously
+809    ///
+810    /// This primarily exists for testing purposes.
+811    pub fn force_key_update(&self) {
+812        self.0
+813            .state
+814            .lock("force_key_update")
+815            .inner
+816            .force_key_update()
+817    }
+818
+819    /// Derive keying material from this connection's TLS session secrets.
+820    ///
+821    /// When both peers call this method with the same `label` and `context`
+822    /// arguments and `output` buffers of equal length, they will get the
+823    /// same sequence of bytes in `output`. These bytes are cryptographically
+824    /// strong and pseudorandom, and are suitable for use as keying material.
+825    ///
+826    /// See [RFC5705](https://tools.ietf.org/html/rfc5705) for more information.
+827    pub fn export_keying_material(
+828        &self,
+829        output: &mut [u8],
+830        label: &[u8],
+831        context: &[u8],
+832    ) -> Result<(), proto::crypto::ExportKeyingMaterialError> {
+833        self.0
+834            .state
+835            .lock("export_keying_material")
+836            .inner
+837            .crypto_session()
+838            .export_keying_material(output, label, context)
+839    }
+840
+841    /// Modify the number of remotely initiated unidirectional streams that may be concurrently open
+842    ///
+843    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+844    /// `count`s increase both minimum and worst-case memory consumption.
+845    pub fn set_max_concurrent_uni_streams(&self, count: VarInt) {
+846        let mut conn = self.0.state.lock("set_max_concurrent_uni_streams");
+847        conn.inner.set_max_concurrent_streams(Dir::Uni, count);
+848        // May need to send MAX_STREAMS to make progress
+849        conn.wake();
+850    }
+851
+852    /// See [`proto::TransportConfig::send_window()`]
+853    pub fn set_send_window(&self, send_window: u64) {
+854        let mut conn = self.0.state.lock("set_send_window");
+855        conn.inner.set_send_window(send_window);
+856        conn.wake();
+857    }
+858
+859    /// See [`proto::TransportConfig::receive_window()`]
+860    pub fn set_receive_window(&self, receive_window: VarInt) {
+861        let mut conn = self.0.state.lock("set_receive_window");
+862        conn.inner.set_receive_window(receive_window);
+863        conn.wake();
+864    }
+865
+866    /// Modify the number of remotely initiated bidirectional streams that may be concurrently open
+867    ///
+868    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+869    /// `count`s increase both minimum and worst-case memory consumption.
+870    pub fn set_max_concurrent_bi_streams(&self, count: VarInt) {
+871        let mut conn = self.0.state.lock("set_max_concurrent_bi_streams");
+872        conn.inner.set_max_concurrent_streams(Dir::Bi, count);
+873        // May need to send MAX_STREAMS to make progress
+874        conn.wake();
+875    }
+876
+877    /// Track changed on our external address as reported by the peer.
+878    pub fn observed_external_addr(&self) -> watch::Receiver<Option<SocketAddr>> {
+879        let conn = self.0.state.lock("external_addr");
+880        conn.observed_external_addr.subscribe()
+881    }
+882
+883    /// Is multipath enabled?
+884    // TODO(flub): not a useful API, once we do real things with multipath we can remove
+885    // this again.
+886    pub fn is_multipath_enabled(&self) -> bool {
+887        let conn = self.0.state.lock("is_multipath_enabled");
+888        conn.inner.is_multipath_negotiated()
+889    }
+890
+891    /// Registers one address at which this endpoint might be reachable
+892    ///
+893    /// When the NAT traversal extension is negotiated, servers send these addresses to clients in
+894    /// `ADD_ADDRESS` frames. This allows clients to obtain server address candidates to initiate
+895    /// NAT traversal attempts. Clients provide their own reachable addresses in `REACH_OUT` frames
+896    /// when [`Self::initiate_nat_traversal_round`] is called.
+897    pub fn add_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+898        let mut conn = self.0.state.lock("add_nat_traversal_addresses");
+899        conn.inner.add_nat_traversal_address(address)
+900    }
+901
+902    /// Removes one or more addresses from the set of addresses at which this endpoint is reachable
+903    ///
+904    /// When the NAT traversal extension is negotiated, servers send address removals to
+905    /// clients in `REMOVE_ADDRESS` frames. This allows clients to stop using outdated
+906    /// server address candidates that are no longer valid for NAT traversal.
+907    ///
+908    /// For clients, removed addresses will no longer be advertised in `REACH_OUT` frames.
+909    ///
+910    /// Addresses not present in the set will be silently ignored.
+911    pub fn remove_nat_traversal_address(&self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+912        let mut conn = self.0.state.lock("remove_nat_traversal_addresses");
+913        conn.inner.remove_nat_traversal_address(address)
+914    }
+915
+916    /// Get the current local nat traversal addresses
+917    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+918        let conn = self.0.state.lock("get_local_nat_traversal_addresses");
+919        conn.inner.get_local_nat_traversal_addresses()
+920    }
+921
+922    /// Get the currently advertised nat traversal addresses by the server
+923    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+924        let conn = self.0.state.lock("get_remote_nat_traversal_addresses");
+925        conn.inner.get_remote_nat_traversal_addresses()
+926    }
+927
+928    /// Initiates a new nat traversal round
+929    ///
+930    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+931    /// frames, and initiating probing of the known remote addresses. When a new round is
+932    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+933    ///
+934    /// Returns the server addresses that are now being probed.
+935    pub fn initiate_nat_traversal_round(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+936        let mut conn = self.0.state.lock("initiate_nat_traversal_round");
+937        let now = conn.runtime.now();
+938        conn.inner.initiate_nat_traversal_round(now)
+939    }
+940}
+941
+942pin_project! {
+943    /// Future produced by [`Connection::open_uni`]
+944    pub struct OpenUni<'a> {
+945        conn: &'a ConnectionRef,
+946        #[pin]
+947        notify: Notified<'a>,
+948    }
+949}
+950
+951impl Future for OpenUni<'_> {
+952    type Output = Result<SendStream, ConnectionError>;
+953    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+954        let this = self.project();
+955        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Uni))?;
+956        Poll::Ready(Ok(SendStream::new(conn, id, is_0rtt)))
+957    }
+958}
+959
+960pin_project! {
+961    /// Future produced by [`Connection::open_bi`]
+962    pub struct OpenBi<'a> {
+963        conn: &'a ConnectionRef,
+964        #[pin]
+965        notify: Notified<'a>,
+966    }
+967}
+968
+969impl Future for OpenBi<'_> {
+970    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+971    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+972        let this = self.project();
+973        let (conn, id, is_0rtt) = ready!(poll_open(ctx, this.conn, this.notify, Dir::Bi))?;
+974
+975        Poll::Ready(Ok((
+976            SendStream::new(conn.clone(), id, is_0rtt),
+977            RecvStream::new(conn, id, is_0rtt),
+978        )))
+979    }
+980}
+981
+982fn poll_open<'a>(
+983    ctx: &mut Context<'_>,
+984    conn: &'a ConnectionRef,
+985    mut notify: Pin<&mut Notified<'a>>,
+986    dir: Dir,
+987) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+988    let mut state = conn.state.lock("poll_open");
+989    if let Some(ref e) = state.error {
+990        return Poll::Ready(Err(e.clone()));
+991    } else if let Some(id) = state.inner.streams().open(dir) {
+992        let is_0rtt = state.inner.side().is_client() && state.inner.is_handshaking();
+993        drop(state); // Release the lock so clone can take it
+994        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+995    }
+996    loop {
+997        match notify.as_mut().poll(ctx) {
+998            // `state` lock ensures we didn't race with readiness
+999            Poll::Pending => return Poll::Pending,
+1000            // Spurious wakeup, get a new future
+1001            Poll::Ready(()) => {
+1002                notify.set(conn.shared.stream_budget_available[dir as usize].notified())
+1003            }
+1004        }
+1005    }
+1006}
+1007
+1008pin_project! {
+1009    /// Future produced by [`Connection::accept_uni`]
+1010    pub struct AcceptUni<'a> {
+1011        conn: &'a ConnectionRef,
+1012        #[pin]
+1013        notify: Notified<'a>,
+1014    }
+1015}
+1016
+1017impl Future for AcceptUni<'_> {
+1018    type Output = Result<RecvStream, ConnectionError>;
+1019
+1020    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1021        let this = self.project();
+1022        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Uni))?;
+1023        Poll::Ready(Ok(RecvStream::new(conn, id, is_0rtt)))
+1024    }
+1025}
+1026
+1027pin_project! {
+1028    /// Future produced by [`Connection::accept_bi`]
+1029    pub struct AcceptBi<'a> {
+1030        conn: &'a ConnectionRef,
+1031        #[pin]
+1032        notify: Notified<'a>,
+1033    }
+1034}
+1035
+1036impl Future for AcceptBi<'_> {
+1037    type Output = Result<(SendStream, RecvStream), ConnectionError>;
+1038
+1039    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1040        let this = self.project();
+1041        let (conn, id, is_0rtt) = ready!(poll_accept(ctx, this.conn, this.notify, Dir::Bi))?;
+1042        Poll::Ready(Ok((
+1043            SendStream::new(conn.clone(), id, is_0rtt),
+1044            RecvStream::new(conn, id, is_0rtt),
+1045        )))
+1046    }
+1047}
+1048
+1049fn poll_accept<'a>(
+1050    ctx: &mut Context<'_>,
+1051    conn: &'a ConnectionRef,
+1052    mut notify: Pin<&mut Notified<'a>>,
+1053    dir: Dir,
+1054) -> Poll<Result<(ConnectionRef, StreamId, bool), ConnectionError>> {
+1055    let mut state = conn.state.lock("poll_accept");
+1056    // Check for incoming streams before checking `state.error` so that already-received streams,
+1057    // which are necessarily finite, can be drained from a closed connection.
+1058    if let Some(id) = state.inner.streams().accept(dir) {
+1059        let is_0rtt = state.inner.is_handshaking();
+1060        state.wake(); // To send additional stream ID credit
+1061        drop(state); // Release the lock so clone can take it
+1062        return Poll::Ready(Ok((conn.clone(), id, is_0rtt)));
+1063    } else if let Some(ref e) = state.error {
+1064        return Poll::Ready(Err(e.clone()));
+1065    }
+1066    loop {
+1067        match notify.as_mut().poll(ctx) {
+1068            // `state` lock ensures we didn't race with readiness
+1069            Poll::Pending => return Poll::Pending,
+1070            // Spurious wakeup, get a new future
+1071            Poll::Ready(()) => notify.set(conn.shared.stream_incoming[dir as usize].notified()),
+1072        }
+1073    }
+1074}
+1075
+1076pin_project! {
+1077    /// Future produced by [`Connection::read_datagram`]
+1078    pub struct ReadDatagram<'a> {
+1079        conn: &'a ConnectionRef,
+1080        #[pin]
+1081        notify: Notified<'a>,
+1082    }
+1083}
+1084
+1085impl Future for ReadDatagram<'_> {
+1086    type Output = Result<Bytes, ConnectionError>;
+1087    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1088        let mut this = self.project();
+1089        let mut state = this.conn.state.lock("ReadDatagram::poll");
+1090        // Check for buffered datagrams before checking `state.error` so that already-received
+1091        // datagrams, which are necessarily finite, can be drained from a closed connection.
+1092        if let Some(x) = state.inner.datagrams().recv() {
+1093            return Poll::Ready(Ok(x));
+1094        } else if let Some(ref e) = state.error {
+1095            return Poll::Ready(Err(e.clone()));
+1096        }
+1097        loop {
+1098            match this.notify.as_mut().poll(ctx) {
+1099                // `state` lock ensures we didn't race with readiness
+1100                Poll::Pending => return Poll::Pending,
+1101                // Spurious wakeup, get a new future
+1102                Poll::Ready(()) => this
+1103                    .notify
+1104                    .set(this.conn.shared.datagram_received.notified()),
+1105            }
+1106        }
+1107    }
+1108}
+1109
+1110pin_project! {
+1111    /// Future produced by [`Connection::send_datagram_wait`]
+1112    pub struct SendDatagram<'a> {
+1113        conn: &'a ConnectionRef,
+1114        data: Option<Bytes>,
+1115        #[pin]
+1116        notify: Notified<'a>,
+1117    }
+1118}
+1119
+1120impl Future for SendDatagram<'_> {
+1121    type Output = Result<(), SendDatagramError>;
+1122    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+1123        let mut this = self.project();
+1124        let mut state = this.conn.state.lock("SendDatagram::poll");
+1125        if let Some(ref e) = state.error {
+1126            return Poll::Ready(Err(SendDatagramError::ConnectionLost(e.clone())));
+1127        }
+1128        use proto::SendDatagramError::*;
+1129        match state
+1130            .inner
+1131            .datagrams()
+1132            .send(this.data.take().unwrap(), false)
+1133        {
+1134            Ok(()) => {
+1135                state.wake();
+1136                Poll::Ready(Ok(()))
+1137            }
+1138            Err(e) => Poll::Ready(Err(match e {
+1139                Blocked(data) => {
+1140                    this.data.replace(data);
+1141                    loop {
+1142                        match this.notify.as_mut().poll(ctx) {
+1143                            Poll::Pending => return Poll::Pending,
+1144                            // Spurious wakeup, get a new future
+1145                            Poll::Ready(()) => this
+1146                                .notify
+1147                                .set(this.conn.shared.datagrams_unblocked.notified()),
+1148                        }
+1149                    }
+1150                }
+1151                UnsupportedByPeer => SendDatagramError::UnsupportedByPeer,
+1152                Disabled => SendDatagramError::Disabled,
+1153                TooLarge => SendDatagramError::TooLarge,
+1154            })),
+1155        }
+1156    }
+1157}
+1158
+1159/// Future returned by [`Connection::on_closed`]
+1160///
+1161/// Resolves to a tuple of ([`ConnectionError`], [`ConnectionStats`]).
+1162pub struct OnClosed {
+1163    rx: oneshot::Receiver<(ConnectionError, ConnectionStats)>,
+1164    conn: WeakConnectionHandle,
+1165}
+1166
+1167impl Drop for OnClosed {
+1168    fn drop(&mut self) {
+1169        if self.rx.is_terminated() {
+1170            return;
+1171        };
+1172        if let Some(conn) = self.conn.upgrade() {
+1173            self.rx.close();
+1174            conn.0
+1175                .state
+1176                .lock("OnClosed::drop")
+1177                .on_closed
+1178                .retain(|tx| !tx.is_closed());
+1179        }
+1180    }
+1181}
+1182
+1183impl Future for OnClosed {
+1184    type Output = (ConnectionError, ConnectionStats);
+1185
+1186    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+1187        let this = self.get_mut();
+1188        // The `expect` is safe because `State::drop` ensures that all senders are triggered
+1189        // before being dropped.
+1190        Pin::new(&mut this.rx)
+1191            .poll(cx)
+1192            .map(|x| x.expect("on_close sender is never dropped before sending"))
+1193    }
+1194}
+1195
+1196#[derive(Debug)]
+1197pub(crate) struct ConnectionRef(Arc<ConnectionInner>);
+1198
+1199impl ConnectionRef {
+1200    fn from_arc(inner: Arc<ConnectionInner>) -> Self {
+1201        inner.state.lock("from_arc").ref_count += 1;
+1202        Self(inner)
+1203    }
+1204
+1205    fn stable_id(&self) -> usize {
+1206        &*self.0 as *const _ as usize
+1207    }
+1208}
+1209
+1210impl Clone for ConnectionRef {
+1211    fn clone(&self) -> Self {
+1212        Self::from_arc(Arc::clone(&self.0))
+1213    }
+1214}
+1215
+1216impl Drop for ConnectionRef {
+1217    fn drop(&mut self) {
+1218        let conn = &mut *self.state.lock("drop");
+1219        if let Some(x) = conn.ref_count.checked_sub(1) {
+1220            conn.ref_count = x;
+1221            if x == 0 && !conn.inner.is_closed() {
+1222                // If the driver is alive, it's just it and us, so we'd better shut it down. If it's
+1223                // not, we can't do any harm. If there were any streams being opened, then either
+1224                // the connection will be closed for an unrelated reason or a fresh reference will
+1225                // be constructed for the newly opened stream.
+1226                conn.implicit_close(&self.shared);
+1227            }
+1228        }
+1229    }
+1230}
+1231
+1232impl std::ops::Deref for ConnectionRef {
+1233    type Target = ConnectionInner;
+1234    fn deref(&self) -> &Self::Target {
+1235        &self.0
+1236    }
+1237}
+1238
+1239#[derive(Debug)]
+1240pub(crate) struct ConnectionInner {
+1241    pub(crate) state: Mutex<State>,
+1242    pub(crate) shared: Shared,
+1243}
+1244
+1245/// A handle to some connection internals, use with care.
+1246///
+1247/// This contains a weak reference to the connection so will not itself keep the connection
+1248/// alive.
+1249#[derive(Debug, Clone)]
+1250pub struct WeakConnectionHandle(Weak<ConnectionInner>);
+1251
+1252impl WeakConnectionHandle {
+1253    /// Returns `true` if the [`Connection`] associated with this handle is still alive.
+1254    pub fn is_alive(&self) -> bool {
+1255        self.0.upgrade().is_some()
+1256    }
+1257
+1258    /// Upgrade the handle to a full `Connection`
+1259    pub fn upgrade(&self) -> Option<Connection> {
+1260        self.0
+1261            .upgrade()
+1262            .map(|inner| Connection(ConnectionRef::from_arc(inner)))
+1263    }
+1264}
+1265
+1266#[derive(Debug, Default)]
+1267pub(crate) struct Shared {
+1268    handshake_confirmed: Notify,
+1269    /// Notified when new streams may be locally initiated due to an increase in stream ID flow
+1270    /// control budget
+1271    stream_budget_available: [Notify; 2],
+1272    /// Notified when the peer has initiated a new stream
+1273    stream_incoming: [Notify; 2],
+1274    datagram_received: Notify,
+1275    datagrams_unblocked: Notify,
+1276    closed: Notify,
+1277}
+1278
+1279pub(crate) struct State {
+1280    pub(crate) inner: proto::Connection,
+1281    driver: Option<Waker>,
+1282    handle: ConnectionHandle,
+1283    on_handshake_data: Option<oneshot::Sender<()>>,
+1284    on_connected: Option<oneshot::Sender<bool>>,
+1285    connected: bool,
+1286    handshake_confirmed: bool,
+1287    timer: Option<Pin<Box<dyn AsyncTimer>>>,
+1288    timer_deadline: Option<Instant>,
+1289    conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1290    endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1291    pub(crate) blocked_writers: FxHashMap<StreamId, Waker>,
+1292    pub(crate) blocked_readers: FxHashMap<StreamId, Waker>,
+1293    pub(crate) stopped: FxHashMap<StreamId, Arc<Notify>>,
+1294    /// Always set to Some before the connection becomes drained
+1295    pub(crate) error: Option<ConnectionError>,
+1296    /// Tracks paths being opened
+1297    open_path: FxHashMap<PathId, watch::Sender<Result<(), PathError>>>,
+1298    /// Tracks paths being closed
+1299    pub(crate) close_path: FxHashMap<PathId, oneshot::Sender<VarInt>>,
+1300    pub(crate) path_events: tokio::sync::broadcast::Sender<PathEvent>,
+1301    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+1302    ref_count: usize,
+1303    sender: Pin<Box<dyn UdpSender>>,
+1304    pub(crate) runtime: Arc<dyn Runtime>,
+1305    send_buffer: Vec<u8>,
+1306    /// We buffer a transmit when the underlying I/O would block
+1307    buffered_transmit: Option<proto::Transmit>,
+1308    /// Our last external address reported by the peer. When multipath is enabled, this will be the
+1309    /// last report across all paths.
+1310    pub(crate) observed_external_addr: watch::Sender<Option<SocketAddr>>,
+1311    pub(crate) nat_traversal_updates: tokio::sync::broadcast::Sender<iroh_hp::Event>,
+1312    on_closed: Vec<oneshot::Sender<(ConnectionError, ConnectionStats)>>,
+1313}
+1314
+1315impl State {
+1316    #[allow(clippy::too_many_arguments)]
+1317    fn new(
+1318        inner: proto::Connection,
+1319        handle: ConnectionHandle,
+1320        endpoint_events: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+1321        conn_events: mpsc::UnboundedReceiver<ConnectionEvent>,
+1322        on_handshake_data: oneshot::Sender<()>,
+1323        on_connected: oneshot::Sender<bool>,
+1324        sender: Pin<Box<dyn UdpSender>>,
+1325        runtime: Arc<dyn Runtime>,
+1326    ) -> Self {
+1327        Self {
+1328            inner,
+1329            driver: None,
+1330            handle,
+1331            on_handshake_data: Some(on_handshake_data),
+1332            on_connected: Some(on_connected),
+1333            connected: false,
+1334            handshake_confirmed: false,
+1335            timer: None,
+1336            timer_deadline: None,
+1337            conn_events,
+1338            endpoint_events,
+1339            blocked_writers: FxHashMap::default(),
+1340            blocked_readers: FxHashMap::default(),
+1341            stopped: FxHashMap::default(),
+1342            open_path: FxHashMap::default(),
+1343            close_path: FxHashMap::default(),
+1344            error: None,
+1345            ref_count: 0,
+1346            sender,
+1347            runtime,
+1348            send_buffer: Vec::new(),
+1349            buffered_transmit: None,
+1350            path_events: tokio::sync::broadcast::channel(32).0,
+1351            observed_external_addr: watch::Sender::new(None),
+1352            nat_traversal_updates: tokio::sync::broadcast::channel(32).0,
+1353            on_closed: Vec::new(),
+1354        }
+1355    }
+1356
+1357    fn drive_transmit(&mut self, cx: &mut Context) -> io::Result<bool> {
+1358        let now = self.runtime.now();
+1359        let mut transmits = 0;
+1360
+1361        let max_datagrams = self
+1362            .sender
+1363            .max_transmit_segments()
+1364            .min(MAX_TRANSMIT_SEGMENTS);
+1365
+1366        loop {
+1367            // Retry the last transmit, or get a new one.
+1368            let t = match self.buffered_transmit.take() {
+1369                Some(t) => t,
+1370                None => {
+1371                    self.send_buffer.clear();
+1372                    match self
+1373                        .inner
+1374                        .poll_transmit(now, max_datagrams, &mut self.send_buffer)
+1375                    {
+1376                        Some(t) => {
+1377                            transmits += match t.segment_size {
+1378                                None => 1,
+1379                                Some(s) => t.size.div_ceil(s), // round up
+1380                            };
+1381                            t
+1382                        }
+1383                        None => break,
+1384                    }
+1385                }
+1386            };
+1387
+1388            let len = t.size;
+1389            match self
+1390                .sender
+1391                .as_mut()
+1392                .poll_send(&udp_transmit(&t, &self.send_buffer[..len]), cx)
+1393            {
+1394                Poll::Pending => {
+1395                    self.buffered_transmit = Some(t);
+1396                    return Ok(false);
+1397                }
+1398                Poll::Ready(Err(e)) => return Err(e),
+1399                Poll::Ready(Ok(())) => {}
+1400            }
+1401
+1402            if transmits >= MAX_TRANSMIT_DATAGRAMS {
+1403                // TODO: What isn't ideal here yet is that if we don't poll all
+1404                // datagrams that could be sent we don't go into the `app_limited`
+1405                // state and CWND continues to grow until we get here the next time.
+1406                // See https://github.com/quinn-rs/quinn/issues/1126
+1407                return Ok(true);
+1408            }
+1409        }
+1410
+1411        Ok(false)
+1412    }
+1413
+1414    fn forward_endpoint_events(&mut self) {
+1415        while let Some(event) = self.inner.poll_endpoint_events() {
+1416            // If the endpoint driver is gone, noop.
+1417            let _ = self.endpoint_events.send((self.handle, event));
+1418        }
+1419    }
+1420
+1421    /// If this returns `Err`, the endpoint is dead, so the driver should exit immediately.
+1422    fn process_conn_events(
+1423        &mut self,
+1424        shared: &Shared,
+1425        cx: &mut Context,
+1426    ) -> Result<(), ConnectionError> {
+1427        loop {
+1428            match self.conn_events.poll_recv(cx) {
+1429                Poll::Ready(Some(ConnectionEvent::Rebind(sender))) => {
+1430                    self.sender = sender;
+1431                    self.inner.local_address_changed();
+1432                }
+1433                Poll::Ready(Some(ConnectionEvent::Proto(event))) => {
+1434                    self.inner.handle_event(event);
+1435                }
+1436                Poll::Ready(Some(ConnectionEvent::Close { reason, error_code })) => {
+1437                    self.close(error_code, reason, shared);
+1438                }
+1439                Poll::Ready(None) => {
+1440                    return Err(ConnectionError::TransportError(TransportError::new(
+1441                        TransportErrorCode::INTERNAL_ERROR,
+1442                        "endpoint driver future was dropped".to_string(),
+1443                    )));
+1444                }
+1445                Poll::Pending => {
+1446                    return Ok(());
+1447                }
+1448            }
+1449        }
+1450    }
+1451
+1452    fn forward_app_events(&mut self, shared: &Shared) {
+1453        while let Some(event) = self.inner.poll() {
+1454            use proto::Event::*;
+1455            match event {
+1456                HandshakeDataReady => {
+1457                    if let Some(x) = self.on_handshake_data.take() {
+1458                        let _ = x.send(());
+1459                    }
+1460                }
+1461                Connected => {
+1462                    self.connected = true;
+1463                    if let Some(x) = self.on_connected.take() {
+1464                        // We don't care if the on-connected future was dropped
+1465                        let _ = x.send(self.inner.accepted_0rtt());
+1466                    }
+1467                    if self.inner.side().is_client() && !self.inner.accepted_0rtt() {
+1468                        // Wake up rejected 0-RTT streams so they can fail immediately with
+1469                        // `ZeroRttRejected` errors.
+1470                        wake_all(&mut self.blocked_writers);
+1471                        wake_all(&mut self.blocked_readers);
+1472                        wake_all_notify(&mut self.stopped);
+1473                    }
+1474                }
+1475                HandshakeConfirmed => {
+1476                    self.handshake_confirmed = true;
+1477                    shared.handshake_confirmed.notify_waiters();
+1478                }
+1479                ConnectionLost { reason } => {
+1480                    self.terminate(reason, shared);
+1481                }
+1482                Stream(StreamEvent::Writable { id }) => wake_stream(id, &mut self.blocked_writers),
+1483                Stream(StreamEvent::Opened { dir: Dir::Uni }) => {
+1484                    shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1485                }
+1486                Stream(StreamEvent::Opened { dir: Dir::Bi }) => {
+1487                    shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1488                }
+1489                DatagramReceived => {
+1490                    shared.datagram_received.notify_waiters();
+1491                }
+1492                DatagramsUnblocked => {
+1493                    shared.datagrams_unblocked.notify_waiters();
+1494                }
+1495                Stream(StreamEvent::Readable { id }) => wake_stream(id, &mut self.blocked_readers),
+1496                Stream(StreamEvent::Available { dir }) => {
+1497                    // Might mean any number of streams are ready, so we wake up everyone
+1498                    shared.stream_budget_available[dir as usize].notify_waiters();
+1499                }
+1500                Stream(StreamEvent::Finished { id }) => wake_stream_notify(id, &mut self.stopped),
+1501                Stream(StreamEvent::Stopped { id, .. }) => {
+1502                    wake_stream_notify(id, &mut self.stopped);
+1503                    wake_stream(id, &mut self.blocked_writers);
+1504                }
+1505                Path(ref evt @ PathEvent::ObservedAddr { addr: observed, .. }) => {
+1506                    self.path_events.send(evt.clone()).ok();
+1507                    self.observed_external_addr.send_if_modified(|addr| {
+1508                        let old = addr.replace(observed);
+1509                        old != *addr
+1510                    });
+1511                }
+1512                Path(ref evt @ PathEvent::Opened { id }) => {
+1513                    self.path_events.send(evt.clone()).ok();
+1514                    if let Some(sender) = self.open_path.remove(&id) {
+1515                        sender.send_modify(|value| *value = Ok(()));
+1516                    }
+1517                }
+1518                Path(ref evt @ PathEvent::Closed { id, error_code }) => {
+1519                    self.path_events.send(evt.clone()).ok();
+1520                    if let Some(sender) = self.close_path.remove(&id) {
+1521                        let _ = sender.send(error_code);
+1522                    }
+1523                }
+1524                Path(evt @ PathEvent::Abandoned { .. }) => {
+1525                    self.path_events.send(evt).ok();
+1526                }
+1527                Path(ref evt @ PathEvent::LocallyClosed { id, error }) => {
+1528                    self.path_events.send(evt.clone()).ok();
+1529                    if let Some(sender) = self.open_path.remove(&id) {
+1530                        sender.send_modify(|value| *value = Err(error));
+1531                    }
+1532                    // this will happen also for already opened paths
+1533                }
+1534                Path(evt @ PathEvent::RemoteStatus { .. }) => {
+1535                    self.path_events.send(evt).ok();
+1536                }
+1537                NatTraversal(update) => {
+1538                    self.nat_traversal_updates.send(update).ok();
+1539                }
+1540            }
+1541        }
+1542    }
+1543
+1544    fn drive_timer(&mut self, cx: &mut Context) -> bool {
+1545        // Check whether we need to (re)set the timer. If so, we must poll again to ensure the
+1546        // timer is registered with the runtime (and check whether it's already
+1547        // expired).
+1548        match self.inner.poll_timeout() {
+1549            Some(deadline) => {
+1550                if let Some(delay) = &mut self.timer {
+1551                    // There is no need to reset the tokio timer if the deadline
+1552                    // did not change
+1553                    if self
+1554                        .timer_deadline
+1555                        .map(|current_deadline| current_deadline != deadline)
+1556                        .unwrap_or(true)
+1557                    {
+1558                        delay.as_mut().reset(deadline);
+1559                    }
+1560                } else {
+1561                    self.timer = Some(self.runtime.new_timer(deadline));
+1562                }
+1563                // Store the actual expiration time of the timer
+1564                self.timer_deadline = Some(deadline);
+1565            }
+1566            None => {
+1567                self.timer_deadline = None;
+1568                return false;
+1569            }
+1570        }
+1571
+1572        if self.timer_deadline.is_none() {
+1573            return false;
+1574        }
+1575
+1576        let delay = self
+1577            .timer
+1578            .as_mut()
+1579            .expect("timer must exist in this state")
+1580            .as_mut();
+1581        if delay.poll(cx).is_pending() {
+1582            // Since there wasn't a timeout event, there is nothing new
+1583            // for the connection to do
+1584            return false;
+1585        }
+1586
+1587        // A timer expired, so the caller needs to check for
+1588        // new transmits, which might cause new timers to be set.
+1589        self.inner.handle_timeout(self.runtime.now());
+1590        self.timer_deadline = None;
+1591        true
+1592    }
+1593
+1594    /// Wake up a blocked `Driver` task to process I/O
+1595    pub(crate) fn wake(&mut self) {
+1596        if let Some(x) = self.driver.take() {
+1597            x.wake();
+1598        }
+1599    }
+1600
+1601    /// Used to wake up all blocked futures when the connection becomes closed for any reason
+1602    fn terminate(&mut self, reason: ConnectionError, shared: &Shared) {
+1603        self.error = Some(reason.clone());
+1604        if let Some(x) = self.on_handshake_data.take() {
+1605            let _ = x.send(());
+1606        }
+1607        wake_all(&mut self.blocked_writers);
+1608        wake_all(&mut self.blocked_readers);
+1609        shared.stream_budget_available[Dir::Uni as usize].notify_waiters();
+1610        shared.stream_budget_available[Dir::Bi as usize].notify_waiters();
+1611        shared.stream_incoming[Dir::Uni as usize].notify_waiters();
+1612        shared.stream_incoming[Dir::Bi as usize].notify_waiters();
+1613        shared.datagram_received.notify_waiters();
+1614        shared.datagrams_unblocked.notify_waiters();
+1615        if let Some(x) = self.on_connected.take() {
+1616            let _ = x.send(false);
+1617        }
+1618        shared.handshake_confirmed.notify_waiters();
+1619        wake_all_notify(&mut self.stopped);
+1620        shared.closed.notify_waiters();
+1621
+1622        // Send to the registered on_closed futures.
+1623        let stats = self.inner.stats();
+1624        for tx in self.on_closed.drain(..) {
+1625            tx.send((reason.clone(), stats.clone())).ok();
+1626        }
+1627    }
+1628
+1629    fn close(&mut self, error_code: VarInt, reason: Bytes, shared: &Shared) {
+1630        self.inner.close(self.runtime.now(), error_code, reason);
+1631        self.terminate(ConnectionError::LocallyClosed, shared);
+1632        self.wake();
+1633    }
+1634
+1635    /// Close for a reason other than the application's explicit request
+1636    pub(crate) fn implicit_close(&mut self, shared: &Shared) {
+1637        self.close(0u32.into(), Bytes::new(), shared);
+1638    }
+1639
+1640    pub(crate) fn check_0rtt(&self) -> Result<(), ()> {
+1641        if self.inner.is_handshaking()
+1642            || self.inner.accepted_0rtt()
+1643            || self.inner.side().is_server()
+1644        {
+1645            Ok(())
+1646        } else {
+1647            Err(())
+1648        }
+1649    }
+1650}
+1651
+1652impl Drop for State {
+1653    fn drop(&mut self) {
+1654        if !self.inner.is_drained() {
+1655            // Ensure the endpoint can tidy up
+1656            let _ = self
+1657                .endpoint_events
+1658                .send((self.handle, proto::EndpointEvent::drained()));
+1659        }
+1660
+1661        if !self.on_closed.is_empty() {
+1662            // Ensure that all on_closed oneshot senders are triggered before dropping.
+1663            let reason = self.error.as_ref().expect("closed without error reason");
+1664            let stats = self.inner.stats();
+1665            for tx in self.on_closed.drain(..) {
+1666                tx.send((reason.clone(), stats.clone())).ok();
+1667            }
+1668        }
+1669    }
+1670}
+1671
+1672impl fmt::Debug for State {
+1673    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+1674        f.debug_struct("State").field("inner", &self.inner).finish()
+1675    }
+1676}
+1677
+1678fn wake_stream(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Waker>) {
+1679    if let Some(waker) = wakers.remove(&stream_id) {
+1680        waker.wake();
+1681    }
+1682}
+1683
+1684fn wake_all(wakers: &mut FxHashMap<StreamId, Waker>) {
+1685    wakers.drain().for_each(|(_, waker)| waker.wake())
+1686}
+1687
+1688fn wake_stream_notify(stream_id: StreamId, wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1689    if let Some(notify) = wakers.remove(&stream_id) {
+1690        notify.notify_waiters()
+1691    }
+1692}
+1693
+1694fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
+1695    wakers
+1696        .drain()
+1697        .for_each(|(_, notify)| notify.notify_waiters())
+1698}
+1699
+1700/// Errors that can arise when sending a datagram
+1701#[derive(Debug, Error, Clone, Eq, PartialEq)]
+1702pub enum SendDatagramError {
+1703    /// The peer does not support receiving datagram frames
+1704    #[error("datagrams not supported by peer")]
+1705    UnsupportedByPeer,
+1706    /// Datagram support is disabled locally
+1707    #[error("datagram support disabled")]
+1708    Disabled,
+1709    /// The datagram is larger than the connection can currently accommodate
+1710    ///
+1711    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+1712    /// exceeded.
+1713    #[error("datagram too large")]
+1714    TooLarge,
+1715    /// The connection was lost
+1716    #[error("connection lost")]
+1717    ConnectionLost(#[from] ConnectionError),
+1718}
+1719
+1720/// The maximum amount of datagrams which will be produced in a single `drive_transmit` call
+1721///
+1722/// This limits the amount of CPU resources consumed by datagram generation,
+1723/// and allows other tasks (like receiving ACKs) to run in between.
+1724const MAX_TRANSMIT_DATAGRAMS: usize = 20;
+1725
+1726/// The maximum amount of datagrams that are sent in a single transmit
+1727///
+1728/// This can be lower than the maximum platform capabilities, to avoid excessive
+1729/// memory allocations when calling `poll_transmit()`. Benchmarks have shown
+1730/// that numbers around 10 are a good compromise.
+1731const MAX_TRANSMIT_SEGMENTS: usize = 10;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/endpoint.rs.html b/pr/234/docs/src/iroh_quinn/endpoint.rs.html new file mode 100644 index 000000000..b3bdaad07 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/endpoint.rs.html @@ -0,0 +1,930 @@ +endpoint.rs - source

iroh_quinn/
endpoint.rs

1use std::{
+2    collections::VecDeque,
+3    fmt,
+4    future::Future,
+5    io::{self, IoSliceMut},
+6    mem,
+7    net::{SocketAddr, SocketAddrV6},
+8    pin::Pin,
+9    str,
+10    sync::{Arc, Mutex},
+11    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
+12};
+13
+14#[cfg(all(
+15    not(wasm_browser),
+16    any(feature = "runtime-tokio", feature = "runtime-smol"),
+17    any(feature = "aws-lc-rs", feature = "ring"),
+18))]
+19use crate::runtime::default_runtime;
+20use crate::{
+21    Instant,
+22    runtime::{AsyncUdpSocket, Runtime, UdpSender},
+23    udp_transmit,
+24};
+25use bytes::{Bytes, BytesMut};
+26use pin_project_lite::pin_project;
+27use proto::{
+28    self as proto, ClientConfig, ConnectError, ConnectionError, ConnectionHandle, DatagramEvent,
+29    EndpointEvent, ServerConfig,
+30};
+31use rustc_hash::FxHashMap;
+32#[cfg(all(
+33    not(wasm_browser),
+34    any(feature = "runtime-tokio", feature = "runtime-smol"),
+35    any(feature = "aws-lc-rs", feature = "ring"),
+36))]
+37use socket2::{Domain, Protocol, Socket, Type};
+38use tokio::sync::{Notify, futures::Notified, mpsc};
+39use tracing::{Instrument, Span};
+40use udp::{BATCH_SIZE, RecvMeta};
+41
+42use crate::{
+43    ConnectionEvent, EndpointConfig, IO_LOOP_BOUND, RECV_TIME_BOUND, VarInt,
+44    connection::Connecting, incoming::Incoming, work_limiter::WorkLimiter,
+45};
+46
+47/// A QUIC endpoint.
+48///
+49/// An endpoint corresponds to a single UDP socket, may host many connections, and may act as both
+50/// client and server for different connections.
+51///
+52/// May be cloned to obtain another handle to the same endpoint.
+53#[derive(Debug, Clone)]
+54pub struct Endpoint {
+55    pub(crate) inner: EndpointRef,
+56    pub(crate) default_client_config: Option<ClientConfig>,
+57    runtime: Arc<dyn Runtime>,
+58}
+59
+60impl Endpoint {
+61    /// Helper to construct an endpoint for use with outgoing connections only
+62    ///
+63    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
+64    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
+65    /// IPv6 address respectively from an OS-assigned port.
+66    ///
+67    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
+68    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
+69    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
+70    /// address. For example:
+71    ///
+72    /// ```
+73    /// iroh_quinn::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
+74    /// ```
+75    ///
+76    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
+77    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
+78    #[cfg(all(
+79        not(wasm_browser),
+80        any(feature = "runtime-tokio", feature = "runtime-smol"),
+81        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
+82    ))]
+83    pub fn client(addr: SocketAddr) -> io::Result<Self> {
+84        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
+85        if addr.is_ipv6() {
+86            if let Err(e) = socket.set_only_v6(false) {
+87                tracing::debug!(%e, "unable to make socket dual-stack");
+88            }
+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(
+156                Arc::new(config),
+157                server_config.map(Arc::new),
+158                allow_mtud,
+159                None,
+160            ),
+161            addr.is_ipv6(),
+162            runtime.clone(),
+163        );
+164        let driver = EndpointDriver(rc.clone());
+165        runtime.spawn(Box::pin(
+166            async {
+167                if let Err(e) = driver.await {
+168                    tracing::error!("I/O error: {}", e);
+169                }
+170            }
+171            .instrument(Span::current()),
+172        ));
+173        Ok(Self {
+174            inner: rc,
+175            default_client_config: None,
+176            runtime,
+177        })
+178    }
+179
+180    /// Get the next incoming connection attempt from a client
+181    ///
+182    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
+183    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
+184    /// filter connection attempts or force address validation, or converted into an intermediate
+185    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
+186    pub fn accept(&self) -> Accept<'_> {
+187        Accept {
+188            endpoint: self,
+189            notify: self.inner.shared.incoming.notified(),
+190        }
+191    }
+192
+193    /// Set the client configuration used by `connect`
+194    pub fn set_default_client_config(&mut self, config: ClientConfig) {
+195        self.default_client_config = Some(config);
+196    }
+197
+198    /// Connect to a remote endpoint
+199    ///
+200    /// `server_name` must be covered by the certificate presented by the server. This prevents a
+201    /// connection from being intercepted by an attacker with a valid certificate for some other
+202    /// server.
+203    ///
+204    /// May fail immediately due to configuration errors, or in the future if the connection could
+205    /// not be established.
+206    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
+207        let config = match &self.default_client_config {
+208            Some(config) => config.clone(),
+209            None => return Err(ConnectError::NoDefaultClientConfig),
+210        };
+211
+212        self.connect_with(config, addr, server_name)
+213    }
+214
+215    /// Connect to a remote endpoint using a custom configuration.
+216    ///
+217    /// See [`connect()`] for details.
+218    ///
+219    /// [`connect()`]: Endpoint::connect
+220    pub fn connect_with(
+221        &self,
+222        config: ClientConfig,
+223        addr: SocketAddr,
+224        server_name: &str,
+225    ) -> Result<Connecting, ConnectError> {
+226        let mut endpoint = self.inner.state.lock().unwrap();
+227        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
+228            return Err(ConnectError::EndpointStopping);
+229        }
+230        if addr.is_ipv6() && !endpoint.ipv6 {
+231            return Err(ConnectError::InvalidRemoteAddress(addr));
+232        }
+233        let addr = if endpoint.ipv6 {
+234            SocketAddr::V6(ensure_ipv6(addr))
+235        } else {
+236            addr
+237        };
+238
+239        let (ch, conn) = endpoint
+240            .inner
+241            .connect(self.runtime.now(), config, addr, server_name)?;
+242
+243        let sender = endpoint.socket.create_sender();
+244        endpoint.stats.outgoing_handshakes += 1;
+245        Ok(endpoint
+246            .recv_state
+247            .connections
+248            .insert(ch, conn, sender, self.runtime.clone()))
+249    }
+250
+251    /// Switch to a new UDP socket
+252    ///
+253    /// See [`Endpoint::rebind_abstract()`] for details.
+254    #[cfg(not(wasm_browser))]
+255    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
+256        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
+257    }
+258
+259    /// Switch to a new UDP socket
+260    ///
+261    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
+262    /// connections and connections to servers unreachable from the new address will be lost.
+263    ///
+264    /// On error, the old UDP socket is retained.
+265    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
+266        let addr = socket.local_addr()?;
+267        let mut inner = self.inner.state.lock().unwrap();
+268        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
+269        inner.ipv6 = addr.is_ipv6();
+270
+271        // Update connection socket references
+272        for sender in inner.recv_state.connections.senders.values() {
+273            // Ignoring errors from dropped connections
+274            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
+275        }
+276        if let Some(driver) = inner.driver.take() {
+277            // Ensure the driver can register for wake-ups from the new socket
+278            driver.wake();
+279        }
+280
+281        Ok(())
+282    }
+283
+284    /// Replace the server configuration, affecting new incoming connections only
+285    ///
+286    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
+287    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
+288        self.inner
+289            .state
+290            .lock()
+291            .unwrap()
+292            .inner
+293            .set_server_config(server_config.map(Arc::new))
+294    }
+295
+296    /// Get the local `SocketAddr` the underlying socket is bound to
+297    pub fn local_addr(&self) -> io::Result<SocketAddr> {
+298        self.inner.state.lock().unwrap().socket.local_addr()
+299    }
+300
+301    /// Get the number of connections that are currently open
+302    pub fn open_connections(&self) -> usize {
+303        self.inner.state.lock().unwrap().inner.open_connections()
+304    }
+305
+306    /// Close all of this endpoint's connections immediately and cease accepting new connections.
+307    ///
+308    /// See [`Connection::close()`] for details.
+309    ///
+310    /// [`Connection::close()`]: crate::Connection::close
+311    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
+312        let reason = Bytes::copy_from_slice(reason);
+313        let mut endpoint = self.inner.state.lock().unwrap();
+314        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
+315        for sender in endpoint.recv_state.connections.senders.values() {
+316            // Ignoring errors from dropped connections
+317            let _ = sender.send(ConnectionEvent::Close {
+318                error_code,
+319                reason: reason.clone(),
+320            });
+321        }
+322        self.inner.shared.incoming.notify_waiters();
+323    }
+324
+325    /// Wait for all connections on the endpoint to be cleanly shut down
+326    ///
+327    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
+328    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
+329    /// the idle timeout period.
+330    ///
+331    /// Does not proactively close existing connections or cause incoming connections to be
+332    /// rejected. Consider calling [`close()`] if that is desired.
+333    ///
+334    /// [`close()`]: Endpoint::close
+335    pub async fn wait_idle(&self) {
+336        loop {
+337            {
+338                let endpoint = &mut *self.inner.state.lock().unwrap();
+339                if endpoint.recv_state.connections.is_empty() {
+340                    break;
+341                }
+342                // Construct future while lock is held to avoid race
+343                self.inner.shared.idle.notified()
+344            }
+345            .await;
+346        }
+347    }
+348}
+349
+350/// Statistics on [Endpoint] activity
+351#[non_exhaustive]
+352#[derive(Debug, Default, Copy, Clone)]
+353pub struct EndpointStats {
+354    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
+355    pub accepted_handshakes: u64,
+356    /// Cumulative number of Quic handshakes sent from this [Endpoint]
+357    pub outgoing_handshakes: u64,
+358    /// Cumulative number of Quic handshakes refused on this [Endpoint]
+359    pub refused_handshakes: u64,
+360    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
+361    pub ignored_handshakes: u64,
+362}
+363
+364/// A future that drives IO on an endpoint
+365///
+366/// This task functions as the switch point between the UDP socket object and the
+367/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
+368/// In order to do so, it also facilitates the exchange of different types of events
+369/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
+370/// running this task is necessary to keep the endpoint's connections running.
+371///
+372/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
+373/// an I/O error occurs.
+374#[must_use = "endpoint drivers must be spawned for I/O to occur"]
+375#[derive(Debug)]
+376pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
+377
+378impl Future for EndpointDriver {
+379    type Output = Result<(), io::Error>;
+380
+381    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+382        let mut endpoint = self.0.state.lock().unwrap();
+383        if endpoint.driver.is_none() {
+384            endpoint.driver = Some(cx.waker().clone());
+385        }
+386
+387        let now = endpoint.runtime.now();
+388        let mut keep_going = false;
+389        keep_going |= endpoint.drive_recv(cx, now)?;
+390        keep_going |= endpoint.handle_events(cx, &self.0.shared);
+391
+392        if !endpoint.recv_state.incoming.is_empty() {
+393            self.0.shared.incoming.notify_waiters();
+394        }
+395
+396        if endpoint.ref_count == 0 && endpoint.recv_state.connections.is_empty() {
+397            Poll::Ready(Ok(()))
+398        } else {
+399            drop(endpoint);
+400            // If there is more work to do schedule the endpoint task again.
+401            // `wake_by_ref()` is called outside the lock to minimize
+402            // lock contention on a multithreaded runtime.
+403            if keep_going {
+404                cx.waker().wake_by_ref();
+405            }
+406            Poll::Pending
+407        }
+408    }
+409}
+410
+411impl Drop for EndpointDriver {
+412    fn drop(&mut self) {
+413        let mut endpoint = self.0.state.lock().unwrap();
+414        endpoint.driver_lost = true;
+415        self.0.shared.incoming.notify_waiters();
+416        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
+417        // connections.
+418        endpoint.recv_state.connections.senders.clear();
+419    }
+420}
+421
+422#[derive(Debug)]
+423pub(crate) struct EndpointInner {
+424    pub(crate) state: Mutex<State>,
+425    pub(crate) shared: Shared,
+426}
+427
+428impl EndpointInner {
+429    pub(crate) fn accept(
+430        &self,
+431        incoming: proto::Incoming,
+432        server_config: Option<Arc<ServerConfig>>,
+433    ) -> Result<Connecting, ConnectionError> {
+434        let mut state = self.state.lock().unwrap();
+435        let mut response_buffer = Vec::new();
+436        let now = state.runtime.now();
+437        match state
+438            .inner
+439            .accept(incoming, now, &mut response_buffer, server_config)
+440        {
+441            Ok((handle, conn)) => {
+442                state.stats.accepted_handshakes += 1;
+443                let sender = state.socket.create_sender();
+444                let runtime = state.runtime.clone();
+445                Ok(state
+446                    .recv_state
+447                    .connections
+448                    .insert(handle, conn, sender, runtime))
+449            }
+450            Err(error) => {
+451                if let Some(transmit) = error.response {
+452                    respond(transmit, &response_buffer, &mut state.sender);
+453                }
+454                Err(error.cause)
+455            }
+456        }
+457    }
+458
+459    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
+460        let mut state = self.state.lock().unwrap();
+461        state.stats.refused_handshakes += 1;
+462        let mut response_buffer = Vec::new();
+463        let transmit = state.inner.refuse(incoming, &mut response_buffer);
+464        respond(transmit, &response_buffer, &mut state.sender);
+465    }
+466
+467    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
+468        let mut state = self.state.lock().unwrap();
+469        let mut response_buffer = Vec::new();
+470        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
+471        respond(transmit, &response_buffer, &mut state.sender);
+472        Ok(())
+473    }
+474
+475    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
+476        let mut state = self.state.lock().unwrap();
+477        state.stats.ignored_handshakes += 1;
+478        state.inner.ignore(incoming);
+479    }
+480}
+481
+482#[derive(Debug)]
+483pub(crate) struct State {
+484    socket: Box<dyn AsyncUdpSocket>,
+485    sender: Pin<Box<dyn UdpSender>>,
+486    /// During an active migration, abandoned_socket receives traffic
+487    /// until the first packet arrives on the new socket.
+488    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
+489    inner: proto::Endpoint,
+490    recv_state: RecvState,
+491    driver: Option<Waker>,
+492    ipv6: bool,
+493    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
+494    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
+495    ref_count: usize,
+496    driver_lost: bool,
+497    runtime: Arc<dyn Runtime>,
+498    stats: EndpointStats,
+499}
+500
+501#[derive(Debug)]
+502pub(crate) struct Shared {
+503    incoming: Notify,
+504    idle: Notify,
+505}
+506
+507impl State {
+508    fn drive_recv(&mut self, cx: &mut Context, now: Instant) -> Result<bool, io::Error> {
+509        let get_time = || self.runtime.now();
+510        self.recv_state.recv_limiter.start_cycle(get_time);
+511        if let Some(socket) = &mut self.prev_socket {
+512            // We don't care about the `PollProgress` from old sockets.
+513            let poll_res = self.recv_state.poll_socket(
+514                cx,
+515                &mut self.inner,
+516                &mut **socket,
+517                &mut self.sender,
+518                &*self.runtime,
+519                now,
+520            );
+521            if poll_res.is_err() {
+522                self.prev_socket = None;
+523            }
+524        };
+525        let poll_res = self.recv_state.poll_socket(
+526            cx,
+527            &mut self.inner,
+528            &mut *self.socket,
+529            &mut self.sender,
+530            &*self.runtime,
+531            now,
+532        );
+533        self.recv_state.recv_limiter.finish_cycle(get_time);
+534        let poll_res = poll_res?;
+535        if poll_res.received_connection_packet {
+536            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
+537            // one anymore. TODO: Account for multiple outgoing connections.
+538            self.prev_socket = None;
+539        }
+540        Ok(poll_res.keep_going)
+541    }
+542
+543    fn handle_events(&mut self, cx: &mut Context, shared: &Shared) -> bool {
+544        for _ in 0..IO_LOOP_BOUND {
+545            let (ch, event) = match self.events.poll_recv(cx) {
+546                Poll::Ready(Some(x)) => x,
+547                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
+548                Poll::Pending => {
+549                    return false;
+550                }
+551            };
+552
+553            if event.is_drained() {
+554                self.recv_state.connections.senders.remove(&ch);
+555                if self.recv_state.connections.is_empty() {
+556                    shared.idle.notify_waiters();
+557                }
+558            }
+559            let Some(event) = self.inner.handle_event(ch, event) else {
+560                continue;
+561            };
+562            // Ignoring errors from dropped connections that haven't yet been cleaned up
+563            let _ = self
+564                .recv_state
+565                .connections
+566                .senders
+567                .get_mut(&ch)
+568                .unwrap()
+569                .send(ConnectionEvent::Proto(event));
+570        }
+571
+572        true
+573    }
+574}
+575
+576impl Drop for State {
+577    fn drop(&mut self) {
+578        for incoming in self.recv_state.incoming.drain(..) {
+579            self.inner.ignore(incoming);
+580        }
+581    }
+582}
+583
+584fn respond(
+585    transmit: proto::Transmit,
+586    response_buffer: &[u8],
+587    sender: &mut Pin<Box<dyn UdpSender>>,
+588) {
+589    // Send if there's kernel buffer space; otherwise, drop it
+590    //
+591    // As an endpoint-generated packet, we know this is an
+592    // immediate, stateless response to an unconnected peer,
+593    // one of:
+594    //
+595    // - A version negotiation response due to an unknown version
+596    // - A `CLOSE` due to a malformed or unwanted connection attempt
+597    // - A stateless reset due to an unrecognized connection
+598    // - A `Retry` packet due to a connection attempt when
+599    //   `use_retry` is set
+600    //
+601    // In each case, a well-behaved peer can be trusted to retry a
+602    // few times, which is guaranteed to produce the same response
+603    // from us. Repeated failures might at worst cause a peer's new
+604    // connection attempt to time out, which is acceptable if we're
+605    // under such heavy load that there's never room for this code
+606    // to transmit. This is morally equivalent to the packet getting
+607    // lost due to congestion further along the link, which
+608    // similarly relies on peer retries for recovery.
+609
+610    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
+611    const NOOP: RawWaker = {
+612        const VTABLE: RawWakerVTable = RawWakerVTable::new(
+613            // Cloning just returns a new no-op raw waker
+614            |_| NOOP,
+615            // `wake` does nothing
+616            |_| {},
+617            // `wake_by_ref` does nothing
+618            |_| {},
+619            // Dropping does nothing as we don't allocate anything
+620            |_| {},
+621        );
+622        RawWaker::new(std::ptr::null(), &VTABLE)
+623    };
+624    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
+625    // it doesn't access the data pointer at all.
+626    let waker = unsafe { Waker::from_raw(NOOP) };
+627    let mut cx = Context::from_waker(&waker);
+628    _ = sender.as_mut().poll_send(
+629        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
+630        &mut cx,
+631    );
+632}
+633
+634#[inline]
+635fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
+636    match ecn {
+637        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
+638        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
+639        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
+640    }
+641}
+642
+643#[derive(Debug)]
+644struct ConnectionSet {
+645    /// Senders for communicating with the endpoint's connections
+646    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
+647    /// Stored to give out clones to new ConnectionInners
+648    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+649    /// Set if the endpoint has been manually closed
+650    close: Option<(VarInt, Bytes)>,
+651}
+652
+653impl ConnectionSet {
+654    fn insert(
+655        &mut self,
+656        handle: ConnectionHandle,
+657        conn: proto::Connection,
+658        sender: Pin<Box<dyn UdpSender>>,
+659        runtime: Arc<dyn Runtime>,
+660    ) -> Connecting {
+661        let (send, recv) = mpsc::unbounded_channel();
+662        if let Some((error_code, ref reason)) = self.close {
+663            send.send(ConnectionEvent::Close {
+664                error_code,
+665                reason: reason.clone(),
+666            })
+667            .unwrap();
+668        }
+669        self.senders.insert(handle, send);
+670        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
+671    }
+672
+673    fn is_empty(&self) -> bool {
+674        self.senders.is_empty()
+675    }
+676}
+677
+678pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
+679    match x {
+680        SocketAddr::V6(x) => x,
+681        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
+682    }
+683}
+684
+685pin_project! {
+686    /// Future produced by [`Endpoint::accept`]
+687    pub struct Accept<'a> {
+688        endpoint: &'a Endpoint,
+689        #[pin]
+690        notify: Notified<'a>,
+691    }
+692}
+693
+694impl Future for Accept<'_> {
+695    type Output = Option<Incoming>;
+696    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+697        let mut this = self.project();
+698        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
+699        if endpoint.driver_lost {
+700            return Poll::Ready(None);
+701        }
+702        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
+703            // Release the mutex lock on endpoint so cloning it doesn't deadlock
+704            drop(endpoint);
+705            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
+706            return Poll::Ready(Some(incoming));
+707        }
+708        if endpoint.recv_state.connections.close.is_some() {
+709            return Poll::Ready(None);
+710        }
+711        loop {
+712            match this.notify.as_mut().poll(ctx) {
+713                // `state` lock ensures we didn't race with readiness
+714                Poll::Pending => return Poll::Pending,
+715                // Spurious wakeup, get a new future
+716                Poll::Ready(()) => this
+717                    .notify
+718                    .set(this.endpoint.inner.shared.incoming.notified()),
+719            }
+720        }
+721    }
+722}
+723
+724#[derive(Debug)]
+725pub(crate) struct EndpointRef(Arc<EndpointInner>);
+726
+727impl EndpointRef {
+728    pub(crate) fn new(
+729        socket: Box<dyn AsyncUdpSocket>,
+730        inner: proto::Endpoint,
+731        ipv6: bool,
+732        runtime: Arc<dyn Runtime>,
+733    ) -> Self {
+734        let (sender, events) = mpsc::unbounded_channel();
+735        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
+736        let sender = socket.create_sender();
+737        Self(Arc::new(EndpointInner {
+738            shared: Shared {
+739                incoming: Notify::new(),
+740                idle: Notify::new(),
+741            },
+742            state: Mutex::new(State {
+743                socket,
+744                sender,
+745                prev_socket: None,
+746                inner,
+747                ipv6,
+748                events,
+749                driver: None,
+750                ref_count: 0,
+751                driver_lost: false,
+752                recv_state,
+753                runtime,
+754                stats: EndpointStats::default(),
+755            }),
+756        }))
+757    }
+758}
+759
+760impl Clone for EndpointRef {
+761    fn clone(&self) -> Self {
+762        self.0.state.lock().unwrap().ref_count += 1;
+763        Self(self.0.clone())
+764    }
+765}
+766
+767impl Drop for EndpointRef {
+768    fn drop(&mut self) {
+769        let endpoint = &mut *self.0.state.lock().unwrap();
+770        if let Some(x) = endpoint.ref_count.checked_sub(1) {
+771            endpoint.ref_count = x;
+772            if x == 0 {
+773                // If the driver is about to be on its own, ensure it can shut down if the last
+774                // connection is gone.
+775                if let Some(task) = endpoint.driver.take() {
+776                    task.wake();
+777                }
+778            }
+779        }
+780    }
+781}
+782
+783impl std::ops::Deref for EndpointRef {
+784    type Target = EndpointInner;
+785    fn deref(&self) -> &Self::Target {
+786        &self.0
+787    }
+788}
+789
+790/// State directly involved in handling incoming packets
+791struct RecvState {
+792    incoming: VecDeque<proto::Incoming>,
+793    connections: ConnectionSet,
+794    recv_buf: Box<[u8]>,
+795    recv_limiter: WorkLimiter,
+796}
+797
+798impl RecvState {
+799    fn new(
+800        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
+801        max_receive_segments: usize,
+802        endpoint: &proto::Endpoint,
+803    ) -> Self {
+804        let recv_buf = vec![
+805            0;
+806            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
+807                * max_receive_segments
+808                * BATCH_SIZE
+809        ];
+810        Self {
+811            connections: ConnectionSet {
+812                senders: FxHashMap::default(),
+813                sender,
+814                close: None,
+815            },
+816            incoming: VecDeque::new(),
+817            recv_buf: recv_buf.into(),
+818            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
+819        }
+820    }
+821
+822    fn poll_socket(
+823        &mut self,
+824        cx: &mut Context,
+825        endpoint: &mut proto::Endpoint,
+826        socket: &mut dyn AsyncUdpSocket,
+827        sender: &mut Pin<Box<dyn UdpSender>>,
+828        runtime: &dyn Runtime,
+829        now: Instant,
+830    ) -> Result<PollProgress, io::Error> {
+831        let mut received_connection_packet = false;
+832        let mut metas = [RecvMeta::default(); BATCH_SIZE];
+833        let mut iovs: [IoSliceMut; BATCH_SIZE] = {
+834            let mut bufs = self
+835                .recv_buf
+836                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
+837                .map(IoSliceMut::new);
+838
+839            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
+840            // and iovs will be of size BATCH_SIZE, thus from_fn is called
+841            // exactly BATCH_SIZE times.
+842            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
+843        };
+844        loop {
+845            match socket.poll_recv(cx, &mut iovs, &mut metas) {
+846                Poll::Ready(Ok(msgs)) => {
+847                    self.recv_limiter.record_work(msgs);
+848                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
+849                        let mut data: BytesMut = buf[0..meta.len].into();
+850                        while !data.is_empty() {
+851                            let buf = data.split_to(meta.stride.min(data.len()));
+852                            let mut response_buffer = Vec::new();
+853                            match endpoint.handle(
+854                                now,
+855                                meta.addr,
+856                                meta.dst_ip,
+857                                meta.ecn.map(proto_ecn),
+858                                buf,
+859                                &mut response_buffer,
+860                            ) {
+861                                Some(DatagramEvent::NewConnection(incoming)) => {
+862                                    if self.connections.close.is_none() {
+863                                        self.incoming.push_back(incoming);
+864                                    } else {
+865                                        let transmit =
+866                                            endpoint.refuse(incoming, &mut response_buffer);
+867                                        respond(transmit, &response_buffer, sender);
+868                                    }
+869                                }
+870                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
+871                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
+872                                    received_connection_packet = true;
+873                                    let _ = self
+874                                        .connections
+875                                        .senders
+876                                        .get_mut(&handle)
+877                                        .unwrap()
+878                                        .send(ConnectionEvent::Proto(event));
+879                                }
+880                                Some(DatagramEvent::Response(transmit)) => {
+881                                    respond(transmit, &response_buffer, sender);
+882                                }
+883                                None => {}
+884                            }
+885                        }
+886                    }
+887                }
+888                Poll::Pending => {
+889                    return Ok(PollProgress {
+890                        received_connection_packet,
+891                        keep_going: false,
+892                    });
+893                }
+894                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
+895                // attacker
+896                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
+897                    continue;
+898                }
+899                Poll::Ready(Err(e)) => {
+900                    return Err(e);
+901                }
+902            }
+903            if !self.recv_limiter.allow_work(|| runtime.now()) {
+904                return Ok(PollProgress {
+905                    received_connection_packet,
+906                    keep_going: true,
+907                });
+908            }
+909        }
+910    }
+911}
+912
+913impl fmt::Debug for RecvState {
+914    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+915        f.debug_struct("RecvState")
+916            .field("incoming", &self.incoming)
+917            .field("connections", &self.connections)
+918            // recv_buf too large
+919            .field("recv_limiter", &self.recv_limiter)
+920            .finish_non_exhaustive()
+921    }
+922}
+923
+924#[derive(Default)]
+925struct PollProgress {
+926    /// Whether a datagram was routed to an existing connection
+927    received_connection_packet: bool,
+928    /// Whether datagram handling was interrupted early by the work limiter for fairness
+929    keep_going: bool,
+930}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/incoming.rs.html b/pr/234/docs/src/iroh_quinn/incoming.rs.html new file mode 100644 index 000000000..a9d589515 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/incoming.rs.html @@ -0,0 +1,152 @@ +incoming.rs - source

iroh_quinn/
incoming.rs

1use std::{
+2    future::{Future, IntoFuture},
+3    net::{IpAddr, SocketAddr},
+4    pin::Pin,
+5    sync::Arc,
+6    task::{Context, Poll},
+7};
+8
+9use proto::{ConnectionError, ConnectionId, ServerConfig};
+10use thiserror::Error;
+11
+12use crate::{
+13    connection::{Connecting, Connection},
+14    endpoint::EndpointRef,
+15};
+16
+17/// An incoming connection for which the server has not yet begun its part of the handshake
+18#[derive(Debug)]
+19pub struct Incoming(Option<State>);
+20
+21impl Incoming {
+22    pub(crate) fn new(inner: proto::Incoming, endpoint: EndpointRef) -> Self {
+23        Self(Some(State { inner, endpoint }))
+24    }
+25
+26    /// Attempt to accept this incoming connection (an error may still occur)
+27    pub fn accept(mut self) -> Result<Connecting, ConnectionError> {
+28        let state = self.0.take().unwrap();
+29        state.endpoint.accept(state.inner, None)
+30    }
+31
+32    /// Accept this incoming connection using a custom configuration
+33    ///
+34    /// See [`accept()`][Incoming::accept] for more details.
+35    pub fn accept_with(
+36        mut self,
+37        server_config: Arc<ServerConfig>,
+38    ) -> Result<Connecting, ConnectionError> {
+39        let state = self.0.take().unwrap();
+40        state.endpoint.accept(state.inner, Some(server_config))
+41    }
+42
+43    /// Reject this incoming connection attempt
+44    pub fn refuse(mut self) {
+45        let state = self.0.take().unwrap();
+46        state.endpoint.refuse(state.inner);
+47    }
+48
+49    /// Respond with a retry packet, requiring the client to retry with address validation
+50    ///
+51    /// Errors if `may_retry()` is false.
+52    pub fn retry(mut self) -> Result<(), RetryError> {
+53        let state = self.0.take().unwrap();
+54        state.endpoint.retry(state.inner).map_err(|e| {
+55            RetryError(Box::new(Self(Some(State {
+56                inner: e.into_incoming(),
+57                endpoint: state.endpoint,
+58            }))))
+59        })
+60    }
+61
+62    /// Ignore this incoming connection attempt, not sending any packet in response
+63    pub fn ignore(mut self) {
+64        let state = self.0.take().unwrap();
+65        state.endpoint.ignore(state.inner);
+66    }
+67
+68    /// The local IP address which was used when the peer established the connection
+69    pub fn local_ip(&self) -> Option<IpAddr> {
+70        self.0.as_ref().unwrap().inner.local_ip()
+71    }
+72
+73    /// The peer's UDP address
+74    pub fn remote_address(&self) -> SocketAddr {
+75        self.0.as_ref().unwrap().inner.remote_address()
+76    }
+77
+78    /// Whether the socket address that is initiating this connection has been validated
+79    ///
+80    /// This means that the sender of the initial packet has proved that they can receive traffic
+81    /// sent to `self.remote_address()`.
+82    ///
+83    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+84    /// The inverse is not guaranteed.
+85    pub fn remote_address_validated(&self) -> bool {
+86        self.0.as_ref().unwrap().inner.remote_address_validated()
+87    }
+88
+89    /// Whether it is legal to respond with a retry packet
+90    ///
+91    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+92    /// The inverse is not guaranteed.
+93    pub fn may_retry(&self) -> bool {
+94        self.0.as_ref().unwrap().inner.may_retry()
+95    }
+96
+97    /// The original destination CID when initiating the connection
+98    pub fn orig_dst_cid(&self) -> ConnectionId {
+99        self.0.as_ref().unwrap().inner.orig_dst_cid()
+100    }
+101}
+102
+103impl Drop for Incoming {
+104    fn drop(&mut self) {
+105        // Implicit reject, similar to Connection's implicit close
+106        if let Some(state) = self.0.take() {
+107            state.endpoint.refuse(state.inner);
+108        }
+109    }
+110}
+111
+112#[derive(Debug)]
+113struct State {
+114    inner: proto::Incoming,
+115    endpoint: EndpointRef,
+116}
+117
+118/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+119#[derive(Debug, Error)]
+120#[error("retry() with validated Incoming")]
+121pub struct RetryError(Box<Incoming>);
+122
+123impl RetryError {
+124    /// Get the [`Incoming`]
+125    pub fn into_incoming(self) -> Incoming {
+126        *self.0
+127    }
+128}
+129
+130/// Basic adapter to let [`Incoming`] be `await`-ed like a [`Connecting`]
+131#[derive(Debug)]
+132pub struct IncomingFuture(Result<Connecting, ConnectionError>);
+133
+134impl Future for IncomingFuture {
+135    type Output = Result<Connection, ConnectionError>;
+136
+137    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+138        match &mut self.0 {
+139            Ok(connecting) => Pin::new(connecting).poll(cx),
+140            Err(e) => Poll::Ready(Err(e.clone())),
+141        }
+142    }
+143}
+144
+145impl IntoFuture for Incoming {
+146    type Output = Result<Connection, ConnectionError>;
+147    type IntoFuture = IncomingFuture;
+148
+149    fn into_future(self) -> Self::IntoFuture {
+150        IncomingFuture(self.accept())
+151    }
+152}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/lib.rs.html b/pr/234/docs/src/iroh_quinn/lib.rs.html new file mode 100644 index 000000000..342f67764 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/lib.rs.html @@ -0,0 +1,138 @@ +lib.rs - source

iroh_quinn/
lib.rs

1//! QUIC transport protocol implementation
+2//!
+3//! [QUIC](https://en.wikipedia.org/wiki/QUIC) is a modern transport protocol addressing
+4//! shortcomings of TCP, such as head-of-line blocking, poor security, slow handshakes, and
+5//! inefficient congestion control. This crate provides a portable userspace implementation. It
+6//! builds on top of quinn-proto, which implements protocol logic independent of any particular
+7//! runtime.
+8//!
+9//! The entry point of this crate is the [`Endpoint`].
+10//!
+11//! # About QUIC
+12//!
+13//! A QUIC connection is an association between two endpoints. The endpoint which initiates the
+14//! connection is termed the client, and the endpoint which accepts it is termed the server. A
+15//! single endpoint may function as both client and server for different connections, for example
+16//! in a peer-to-peer application. To communicate application data, each endpoint may open streams
+17//! up to a limit dictated by its peer. Typically, that limit is increased as old streams are
+18//! finished.
+19//!
+20//! Streams may be unidirectional or bidirectional, and are cheap to create and disposable. For
+21//! example, a traditionally datagram-oriented application could use a new stream for every
+22//! message it wants to send, no longer needing to worry about MTUs. Bidirectional streams behave
+23//! much like a traditional TCP connection, and are useful for sending messages that have an
+24//! immediate response, such as an HTTP request. Stream data is delivered reliably, and there is no
+25//! ordering enforced between data on different streams.
+26//!
+27//! By avoiding head-of-line blocking and providing unified congestion control across all streams
+28//! of a connection, QUIC is able to provide higher throughput and lower latency than one or
+29//! multiple TCP connections between the same two hosts, while providing more useful behavior than
+30//! raw UDP sockets.
+31//!
+32//! Quinn also exposes unreliable datagrams, which are a low-level primitive preferred when
+33//! automatic fragmentation and retransmission of certain data is not desired.
+34//!
+35//! QUIC uses encryption and identity verification built directly on TLS 1.3. Just as with a TLS
+36//! server, it is useful for a QUIC server to be identified by a certificate signed by a trusted
+37//! authority. If this is infeasible--for example, if servers are short-lived or not associated
+38//! with a domain name--then as with TLS, self-signed certificates can be used to provide
+39//! encryption alone.
+40#![warn(missing_docs)]
+41#![warn(unreachable_pub)]
+42#![warn(clippy::use_self)]
+43
+44use std::pin::Pin;
+45
+46mod connection;
+47mod endpoint;
+48mod incoming;
+49mod mutex;
+50mod path;
+51mod recv_stream;
+52mod runtime;
+53mod send_stream;
+54mod work_limiter;
+55
+56#[cfg(not(wasm_browser))]
+57pub(crate) use std::time::{Duration, Instant};
+58#[cfg(wasm_browser)]
+59pub(crate) use web_time::{Duration, Instant};
+60
+61#[cfg(feature = "bloom")]
+62pub use proto::BloomTokenLog;
+63pub use proto::{
+64    AckFrequencyConfig, ApplicationClose, Chunk, ClientConfig, ClosedStream, ConfigError,
+65    ConnectError, ConnectionClose, ConnectionError, ConnectionId, ConnectionIdGenerator,
+66    ConnectionStats, Dir, EcnCodepoint, EndpointConfig, FrameStats, FrameType, IdleTimeout,
+67    MtuDiscoveryConfig, NoneTokenLog, NoneTokenStore, PathId, PathStats, ServerConfig, Side,
+68    StdSystemTime, StreamId, TimeSource, TokenLog, TokenMemoryCache, TokenReuseError, TokenStore,
+69    Transmit, TransportConfig, TransportErrorCode, UdpStats, ValidationTokenConfig, VarInt,
+70    VarIntBoundsExceeded, Written, congestion, crypto,
+71};
+72#[cfg(feature = "qlog")]
+73pub use proto::{QlogConfig, QlogFactory, QlogFileFactory};
+74#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+75pub use rustls;
+76pub use udp;
+77
+78pub use crate::connection::{
+79    AcceptBi, AcceptUni, Connecting, Connection, OnClosed, OpenBi, OpenUni, ReadDatagram,
+80    SendDatagram, SendDatagramError, WeakConnectionHandle, ZeroRttAccepted,
+81};
+82pub use crate::endpoint::{Accept, Endpoint, EndpointStats};
+83pub use crate::incoming::{Incoming, IncomingFuture, RetryError};
+84pub use crate::path::{OpenPath, Path};
+85pub use crate::recv_stream::{ReadError, ReadExactError, ReadToEndError, RecvStream, ResetError};
+86#[cfg(feature = "runtime-smol")]
+87pub use crate::runtime::SmolRuntime;
+88#[cfg(feature = "runtime-tokio")]
+89pub use crate::runtime::TokioRuntime;
+90#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+91pub use crate::runtime::default_runtime;
+92pub use crate::runtime::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSender};
+93pub use crate::send_stream::{SendStream, StoppedError, WriteError};
+94
+95#[cfg(test)]
+96mod tests;
+97
+98#[derive(Debug)]
+99enum ConnectionEvent {
+100    Close {
+101        error_code: VarInt,
+102        reason: bytes::Bytes,
+103    },
+104    Proto(proto::ConnectionEvent),
+105    Rebind(Pin<Box<dyn UdpSender>>),
+106}
+107
+108fn udp_transmit<'a>(t: &proto::Transmit, buffer: &'a [u8]) -> udp::Transmit<'a> {
+109    udp::Transmit {
+110        destination: t.destination,
+111        ecn: t.ecn.map(udp_ecn),
+112        contents: buffer,
+113        segment_size: t.segment_size,
+114        src_ip: t.src_ip,
+115    }
+116}
+117
+118fn udp_ecn(ecn: proto::EcnCodepoint) -> udp::EcnCodepoint {
+119    match ecn {
+120        proto::EcnCodepoint::Ect0 => udp::EcnCodepoint::Ect0,
+121        proto::EcnCodepoint::Ect1 => udp::EcnCodepoint::Ect1,
+122        proto::EcnCodepoint::Ce => udp::EcnCodepoint::Ce,
+123    }
+124}
+125
+126/// Maximum number of datagrams processed in send/recv calls to make before moving on to other processing
+127///
+128/// This helps ensure we don't starve anything when the CPU is slower than the link.
+129/// Value is selected by picking a low number which didn't degrade throughput in benchmarks.
+130const IO_LOOP_BOUND: usize = 160;
+131
+132/// The maximum amount of time that should be spent in `recvmsg()` calls per endpoint iteration
+133///
+134/// 50us are chosen so that an endpoint iteration with a 50us sendmsg limit blocks
+135/// the runtime for a maximum of about 100us.
+136/// Going much lower does not yield any noticeable difference, since a single `recvmmsg`
+137/// batch of size 32 was observed to take 30us on some systems.
+138const RECV_TIME_BOUND: Duration = Duration::from_micros(50);
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/mutex.rs.html b/pr/234/docs/src/iroh_quinn/mutex.rs.html new file mode 100644 index 000000000..ad0484c77 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/mutex.rs.html @@ -0,0 +1,163 @@ +mutex.rs - source

iroh_quinn/
mutex.rs

1use std::{
+2    fmt::Debug,
+3    ops::{Deref, DerefMut},
+4};
+5
+6#[cfg(feature = "lock_tracking")]
+7mod tracking {
+8    use super::*;
+9    use crate::{Duration, Instant};
+10    use std::collections::VecDeque;
+11    use tracing::warn;
+12
+13    #[derive(Debug)]
+14    struct Inner<T> {
+15        last_lock_owner: VecDeque<(&'static str, Duration)>,
+16        value: T,
+17    }
+18
+19    /// A Mutex which optionally allows to track the time a lock was held and
+20    /// emit warnings in case of excessive lock times
+21    pub(crate) struct Mutex<T> {
+22        inner: std::sync::Mutex<Inner<T>>,
+23    }
+24
+25    impl<T: Debug> std::fmt::Debug for Mutex<T> {
+26        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+27            std::fmt::Debug::fmt(&self.inner, f)
+28        }
+29    }
+30
+31    impl<T> Mutex<T> {
+32        pub(crate) fn new(value: T) -> Self {
+33            Self {
+34                inner: std::sync::Mutex::new(Inner {
+35                    last_lock_owner: VecDeque::new(),
+36                    value,
+37                }),
+38            }
+39        }
+40
+41        /// Acquires the lock for a certain purpose
+42        ///
+43        /// The purpose will be recorded in the list of last lock owners
+44        pub(crate) fn lock(&self, purpose: &'static str) -> MutexGuard<'_, T> {
+45            // We don't bother dispatching through Runtime::now because they're pure performance
+46            // diagnostics.
+47            let now = Instant::now();
+48            let guard = self.inner.lock().unwrap();
+49
+50            let lock_time = Instant::now();
+51            let elapsed = lock_time.duration_since(now);
+52
+53            if elapsed > Duration::from_millis(1) {
+54                warn!(
+55                    "Locking the connection for {} took {:?}. Last owners: {:?}",
+56                    purpose, elapsed, guard.last_lock_owner
+57                );
+58            }
+59
+60            MutexGuard {
+61                guard,
+62                start_time: lock_time,
+63                purpose,
+64            }
+65        }
+66    }
+67
+68    pub(crate) struct MutexGuard<'a, T> {
+69        guard: std::sync::MutexGuard<'a, Inner<T>>,
+70        start_time: Instant,
+71        purpose: &'static str,
+72    }
+73
+74    impl<T> Drop for MutexGuard<'_, T> {
+75        fn drop(&mut self) {
+76            if self.guard.last_lock_owner.len() == MAX_LOCK_OWNERS {
+77                self.guard.last_lock_owner.pop_back();
+78            }
+79
+80            let duration = self.start_time.elapsed();
+81
+82            if duration > Duration::from_millis(1) {
+83                warn!(
+84                    "Utilizing the connection for {} took {:?}",
+85                    self.purpose, duration
+86                );
+87            }
+88
+89            self.guard
+90                .last_lock_owner
+91                .push_front((self.purpose, duration));
+92        }
+93    }
+94
+95    impl<T> Deref for MutexGuard<'_, T> {
+96        type Target = T;
+97
+98        fn deref(&self) -> &Self::Target {
+99            &self.guard.value
+100        }
+101    }
+102
+103    impl<T> DerefMut for MutexGuard<'_, T> {
+104        fn deref_mut(&mut self) -> &mut Self::Target {
+105            &mut self.guard.value
+106        }
+107    }
+108
+109    const MAX_LOCK_OWNERS: usize = 20;
+110}
+111
+112#[cfg(feature = "lock_tracking")]
+113pub(crate) use tracking::Mutex;
+114
+115#[cfg(not(feature = "lock_tracking"))]
+116mod non_tracking {
+117    use super::*;
+118
+119    /// A Mutex which optionally allows to track the time a lock was held and
+120    /// emit warnings in case of excessive lock times
+121    #[derive(Debug)]
+122    pub(crate) struct Mutex<T> {
+123        inner: std::sync::Mutex<T>,
+124    }
+125
+126    impl<T> Mutex<T> {
+127        pub(crate) fn new(value: T) -> Self {
+128            Self {
+129                inner: std::sync::Mutex::new(value),
+130            }
+131        }
+132
+133        /// Acquires the lock for a certain purpose
+134        ///
+135        /// The purpose will be recorded in the list of last lock owners
+136        pub(crate) fn lock(&self, _purpose: &'static str) -> MutexGuard<'_, T> {
+137            MutexGuard {
+138                guard: self.inner.lock().unwrap(),
+139            }
+140        }
+141    }
+142
+143    pub(crate) struct MutexGuard<'a, T> {
+144        guard: std::sync::MutexGuard<'a, T>,
+145    }
+146
+147    impl<T> Deref for MutexGuard<'_, T> {
+148        type Target = T;
+149
+150        fn deref(&self) -> &Self::Target {
+151            self.guard.deref()
+152        }
+153    }
+154
+155    impl<T> DerefMut for MutexGuard<'_, T> {
+156        fn deref_mut(&mut self) -> &mut Self::Target {
+157            self.guard.deref_mut()
+158        }
+159    }
+160}
+161
+162#[cfg(not(feature = "lock_tracking"))]
+163pub(crate) use non_tracking::Mutex;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/path.rs.html b/pr/234/docs/src/iroh_quinn/path.rs.html new file mode 100644 index 000000000..8a9693d0a --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/path.rs.html @@ -0,0 +1,297 @@ +path.rs - source

iroh_quinn/
path.rs

1use std::future::Future;
+2use std::net::SocketAddr;
+3use std::pin::Pin;
+4use std::sync::Arc;
+5use std::task::{Context, Poll, ready};
+6use std::time::Duration;
+7
+8use proto::{
+9    ClosePathError, ClosedPath, ConnectionError, PathError, PathEvent, PathId, PathStatus,
+10    SetPathStatusError, TransportErrorCode, VarInt,
+11};
+12use tokio::sync::{oneshot, watch};
+13use tokio_stream::{Stream, wrappers::WatchStream};
+14
+15use crate::Runtime;
+16use crate::connection::ConnectionRef;
+17
+18/// Future produced by [`crate::Connection::open_path`]
+19pub struct OpenPath(OpenPathInner);
+20
+21enum OpenPathInner {
+22    /// Opening a path in underway
+23    ///
+24    /// This might fail later on.
+25    Ongoing {
+26        opened: WatchStream<Result<(), PathError>>,
+27        path_id: PathId,
+28        conn: ConnectionRef,
+29    },
+30    /// Opening a path failed immediately
+31    Rejected {
+32        /// The error that occurred
+33        err: PathError,
+34    },
+35    /// The path is already open
+36    Ready {
+37        path_id: PathId,
+38        conn: ConnectionRef,
+39    },
+40}
+41
+42impl OpenPath {
+43    pub(crate) fn new(
+44        path_id: PathId,
+45        opened: watch::Receiver<Result<(), PathError>>,
+46        conn: ConnectionRef,
+47    ) -> Self {
+48        Self(OpenPathInner::Ongoing {
+49            opened: WatchStream::from_changes(opened),
+50            path_id,
+51            conn,
+52        })
+53    }
+54
+55    pub(crate) fn ready(path_id: PathId, conn: ConnectionRef) -> Self {
+56        Self(OpenPathInner::Ready { path_id, conn })
+57    }
+58
+59    pub(crate) fn rejected(err: PathError) -> Self {
+60        Self(OpenPathInner::Rejected { err })
+61    }
+62
+63    /// Returns the path ID of the new path being opened.
+64    ///
+65    /// If an error occurred before a path ID was allocated, `None` is returned.  In this
+66    /// case the future is ready and polling it will immediately yield the error.
+67    ///
+68    /// The returned value remains the same for the entire lifetime of this future.
+69    pub fn path_id(&self) -> Option<PathId> {
+70        match self.0 {
+71            OpenPathInner::Ongoing { path_id, .. } => Some(path_id),
+72            OpenPathInner::Rejected { .. } => None,
+73            OpenPathInner::Ready { path_id, .. } => Some(path_id),
+74        }
+75    }
+76}
+77
+78impl Future for OpenPath {
+79    type Output = Result<Path, PathError>;
+80    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+81        match self.get_mut().0 {
+82            OpenPathInner::Ongoing {
+83                ref mut opened,
+84                path_id,
+85                ref mut conn,
+86            } => match ready!(Pin::new(opened).poll_next(ctx)) {
+87                Some(value) => Poll::Ready(value.map(|_| Path {
+88                    id: path_id,
+89                    conn: conn.clone(),
+90                })),
+91                None => {
+92                    // This only happens if receiving a notification change failed, this means the
+93                    // sender was dropped. This generally should not happen so we use a transient
+94                    // error
+95                    Poll::Ready(Err(PathError::ValidationFailed))
+96                }
+97            },
+98            OpenPathInner::Ready {
+99                path_id,
+100                ref mut conn,
+101            } => Poll::Ready(Ok(Path {
+102                id: path_id,
+103                conn: conn.clone(),
+104            })),
+105            OpenPathInner::Rejected { err } => Poll::Ready(Err(err)),
+106        }
+107    }
+108}
+109
+110/// An open (Multi)Path
+111#[derive(Debug)]
+112pub struct Path {
+113    pub(crate) id: PathId,
+114    pub(crate) conn: ConnectionRef,
+115}
+116
+117impl Path {
+118    /// The [`PathId`] of this path.
+119    pub fn id(&self) -> PathId {
+120        self.id
+121    }
+122
+123    /// The current local [`PathStatus`] of this path.
+124    pub fn status(&self) -> Result<PathStatus, ClosedPath> {
+125        self.conn
+126            .state
+127            .lock("path status")
+128            .inner
+129            .path_status(self.id)
+130    }
+131
+132    /// Sets the [`PathStatus`] of this path.
+133    pub fn set_status(&self, status: PathStatus) -> Result<(), SetPathStatusError> {
+134        self.conn
+135            .state
+136            .lock("set path status")
+137            .inner
+138            .set_path_status(self.id, status)?;
+139        Ok(())
+140    }
+141
+142    /// Closes this path
+143    ///
+144    /// The future will resolve when all the path state is dropped.  This only happens after
+145    /// the remote has confirmed the path as closed **and** after an additional timeout to
+146    /// give any in-flight packets the time to arrive.
+147    pub fn close(&self) -> Result<ClosePath, ClosePathError> {
+148        let (on_path_close_send, on_path_close_recv) = oneshot::channel();
+149        {
+150            let mut state = self.conn.state.lock("close_path");
+151            state.inner.close_path(
+152                crate::Instant::now(),
+153                self.id,
+154                TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+155            )?;
+156            state.close_path.insert(self.id, on_path_close_send);
+157        }
+158
+159        Ok(ClosePath {
+160            closed: on_path_close_recv,
+161        })
+162    }
+163
+164    /// Sets the keep_alive_interval for a specific path
+165    ///
+166    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+167    ///
+168    /// Returns the previous value of the setting.
+169    ///
+170    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+171    pub fn set_max_idle_timeout(
+172        &self,
+173        timeout: Option<Duration>,
+174    ) -> Result<Option<Duration>, ClosedPath> {
+175        let mut state = self.conn.state.lock("path_set_max_idle_timeout");
+176        state.inner.set_path_max_idle_timeout(self.id, timeout)
+177    }
+178
+179    /// Sets the keep_alive_interval for a specific path
+180    ///
+181    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+182    ///
+183    /// Returns the previous value of the setting.
+184    ///
+185    /// [`TransportConfig::default_path_keep_alive_interval`]: crate::TransportConfig::default_path_keep_alive_interval
+186    pub fn set_keep_alive_interval(
+187        &self,
+188        interval: Option<Duration>,
+189    ) -> Result<Option<Duration>, ClosedPath> {
+190        let mut state = self.conn.state.lock("path_set_keep_alive_interval");
+191        state.inner.set_path_keep_alive_interval(self.id, interval)
+192    }
+193
+194    /// Track changes on our external address as reported by the peer.
+195    ///
+196    /// If the address-discovery extension is not negotiated, the stream will never return.
+197    pub fn observed_external_addr(&self) -> Result<AddressDiscovery, ClosedPath> {
+198        let state = self.conn.state.lock("per_path_observed_address");
+199        let path_events = state.path_events.subscribe();
+200        let initial_value = state.inner.path_observed_address(self.id)?;
+201        Ok(AddressDiscovery::new(
+202            self.id,
+203            path_events,
+204            initial_value,
+205            state.runtime.clone(),
+206        ))
+207    }
+208
+209    /// The peer's UDP address for this path.
+210    pub fn remote_address(&self) -> Result<SocketAddr, ClosedPath> {
+211        let state = self.conn.state.lock("per_path_remote_address");
+212        state.inner.path_remote_address(self.id)
+213    }
+214}
+215
+216/// Future produced by [`Path::close`]
+217pub struct ClosePath {
+218    closed: oneshot::Receiver<VarInt>,
+219}
+220
+221impl Future for ClosePath {
+222    type Output = Result<VarInt, ConnectionError>;
+223    fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
+224        // TODO: thread through errors
+225        let res = ready!(Pin::new(&mut self.closed).poll(ctx));
+226        match res {
+227            Ok(code) => Poll::Ready(Ok(code)),
+228            Err(_err) => todo!(), // TODO: appropriate error
+229        }
+230    }
+231}
+232
+233/// Stream produced by [`Path::observed_external_addr`]
+234///
+235/// This will always return the external address most recently reported by the remote over this
+236/// path. If the extension is not negotiated, this stream will never return.
+237// TODO(@divma): provide a way to check if the extension is negotiated.
+238pub struct AddressDiscovery {
+239    watcher: WatchStream<SocketAddr>,
+240}
+241
+242impl AddressDiscovery {
+243    pub(super) fn new(
+244        path_id: PathId,
+245        mut path_events: tokio::sync::broadcast::Receiver<PathEvent>,
+246        initial_value: Option<SocketAddr>,
+247        runtime: Arc<dyn Runtime>,
+248    ) -> Self {
+249        let (tx, rx) = watch::channel(initial_value.unwrap_or_else(||
+250                // if the dummy value is used, it will be ignored
+251                SocketAddr::new([0, 0, 0, 0].into(), 0)));
+252        let filter = async move {
+253            loop {
+254                match path_events.recv().await {
+255                    Ok(PathEvent::ObservedAddr { id, addr: observed }) if id == path_id => {
+256                        tx.send_if_modified(|addr| {
+257                            let old = std::mem::replace(addr, observed);
+258                            old != *addr
+259                        });
+260                    }
+261                    Ok(PathEvent::Abandoned { id, .. }) if id == path_id => {
+262                        // If the path is closed, terminate the stream
+263                        break;
+264                    }
+265                    Ok(_) => {
+266                        // ignore any other event
+267                    }
+268                    Err(_) => {
+269                        // A lagged error should never happen since this (detached) task is
+270                        // constantly reading from the channel. Therefore, if an error does happen,
+271                        // the stream can terminate
+272                        break;
+273                    }
+274                }
+275            }
+276        };
+277
+278        let watcher = if initial_value.is_some() {
+279            WatchStream::new(rx)
+280        } else {
+281            WatchStream::from_changes(rx)
+282        };
+283
+284        runtime.spawn(Box::pin(filter));
+285        // TODO(@divma): check if there's a way to ensure the future ends. AbortHandle is not an
+286        // option
+287        Self { watcher }
+288    }
+289}
+290
+291impl Stream for AddressDiscovery {
+292    type Item = SocketAddr;
+293
+294    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+295        Pin::new(&mut self.watcher).poll_next(cx)
+296    }
+297}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/recv_stream.rs.html b/pr/234/docs/src/iroh_quinn/recv_stream.rs.html new file mode 100644 index 000000000..c8ace4e99 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/recv_stream.rs.html @@ -0,0 +1,699 @@ +recv_stream.rs - source

iroh_quinn/
recv_stream.rs

1use std::{
+2    future::{Future, poll_fn},
+3    io,
+4    pin::Pin,
+5    task::{Context, Poll, ready},
+6};
+7
+8use bytes::Bytes;
+9use proto::{Chunk, Chunks, ClosedStream, ConnectionError, ReadableError, StreamId};
+10use thiserror::Error;
+11use tokio::io::ReadBuf;
+12
+13use crate::{VarInt, connection::ConnectionRef};
+14
+15/// A stream that can only be used to receive data
+16///
+17/// `stop(0)` is implicitly called on drop unless:
+18/// - A variant of [`ReadError`] has been yielded by a read call
+19/// - [`stop()`] was called explicitly
+20///
+21/// # Cancellation
+22///
+23/// A `read` method is said to be *cancel-safe* when dropping its future before the future becomes
+24/// ready cannot lead to loss of stream data. This is true of methods which succeed immediately when
+25/// any progress is made, and is not true of methods which might need to perform multiple reads
+26/// internally before succeeding. Each `read` method documents whether it is cancel-safe.
+27///
+28/// # Common issues
+29///
+30/// ## Data never received on a locally-opened stream
+31///
+32/// Peers are not notified of streams until they or a later-numbered stream are used to send
+33/// data. If a bidirectional stream is locally opened but never used to send, then the peer may
+34/// never see it. Application protocols should always arrange for the endpoint which will first
+35/// transmit on a stream to be the endpoint responsible for opening it.
+36///
+37/// ## Data never received on a remotely-opened stream
+38///
+39/// Verify that the stream you are receiving is the same one that the server is sending on, e.g. by
+40/// logging the [`id`] of each. Streams are always accepted in the same order as they are created,
+41/// i.e. ascending order by [`StreamId`]. For example, even if a sender first transmits on
+42/// bidirectional stream 1, the first stream yielded by [`Connection::accept_bi`] on the receiver
+43/// will be bidirectional stream 0.
+44///
+45/// [`ReadError`]: crate::ReadError
+46/// [`stop()`]: RecvStream::stop
+47/// [`SendStream::finish`]: crate::SendStream::finish
+48/// [`WriteError::Stopped`]: crate::WriteError::Stopped
+49/// [`id`]: RecvStream::id
+50/// [`Connection::accept_bi`]: crate::Connection::accept_bi
+51#[derive(Debug)]
+52pub struct RecvStream {
+53    conn: ConnectionRef,
+54    stream: StreamId,
+55    is_0rtt: bool,
+56    all_data_read: bool,
+57    reset: Option<VarInt>,
+58}
+59
+60impl RecvStream {
+61    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
+62        Self {
+63            conn,
+64            stream,
+65            is_0rtt,
+66            all_data_read: false,
+67            reset: None,
+68        }
+69    }
+70
+71    /// Read data contiguously from the stream.
+72    ///
+73    /// Yields the number of bytes read into `buf` on success, or `None` if the stream was finished.
+74    ///
+75    /// This operation is cancel-safe.
+76    pub async fn read(&mut self, buf: &mut [u8]) -> Result<Option<usize>, ReadError> {
+77        Read {
+78            stream: self,
+79            buf: ReadBuf::new(buf),
+80        }
+81        .await
+82    }
+83
+84    /// Read an exact number of bytes contiguously from the stream.
+85    ///
+86    /// See [`read()`] for details. This operation is *not* cancel-safe.
+87    ///
+88    /// [`read()`]: RecvStream::read
+89    pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), ReadExactError> {
+90        ReadExact {
+91            stream: self,
+92            buf: ReadBuf::new(buf),
+93        }
+94        .await
+95    }
+96
+97    /// Attempts to read from the stream into the provided buffer
+98    ///
+99    /// On success, returns `Poll::Ready(Ok(num_bytes_read))` and places data into `buf`. If this
+100    /// returns zero bytes read (and `buf` has a non-zero length), that indicates that the remote
+101    /// side has [`finish`]ed the stream and the local side has already read all bytes.
+102    ///
+103    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
+104    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
+105    /// closed.
+106    ///
+107    /// [`finish`]: crate::SendStream::finish
+108    pub fn poll_read(
+109        &mut self,
+110        cx: &mut Context,
+111        buf: &mut [u8],
+112    ) -> Poll<Result<usize, ReadError>> {
+113        let mut buf = ReadBuf::new(buf);
+114        ready!(self.poll_read_buf(cx, &mut buf))?;
+115        Poll::Ready(Ok(buf.filled().len()))
+116    }
+117
+118    /// Attempts to read from the stream into the provided buffer, which may be uninitialized
+119    ///
+120    /// On success, returns `Poll::Ready(Ok(()))` and places data into the unfilled portion of
+121    /// `buf`. If this does not write any bytes to `buf` (and `buf.remaining()` is non-zero), that
+122    /// indicates that the remote side has [`finish`]ed the stream and the local side has already
+123    /// read all bytes.
+124    ///
+125    /// If no data is available for reading, this returns `Poll::Pending` and arranges for the
+126    /// current task (via `cx.waker()`) to be notified when the stream becomes readable or is
+127    /// closed.
+128    ///
+129    /// [`finish`]: crate::SendStream::finish
+130    pub fn poll_read_buf(
+131        &mut self,
+132        cx: &mut Context,
+133        buf: &mut ReadBuf<'_>,
+134    ) -> Poll<Result<(), ReadError>> {
+135        if buf.remaining() == 0 {
+136            return Poll::Ready(Ok(()));
+137        }
+138
+139        self.poll_read_generic(cx, true, |chunks| {
+140            let mut read = false;
+141            loop {
+142                if buf.remaining() == 0 {
+143                    // We know `read` is `true` because `buf.remaining()` was not 0 before
+144                    return ReadStatus::Readable(());
+145                }
+146
+147                match chunks.next(buf.remaining()) {
+148                    Ok(Some(chunk)) => {
+149                        buf.put_slice(&chunk.bytes);
+150                        read = true;
+151                    }
+152                    res => return (if read { Some(()) } else { None }, res.err()).into(),
+153                }
+154            }
+155        })
+156        .map(|res| res.map(|_| ()))
+157    }
+158
+159    /// Read the next segment of data
+160    ///
+161    /// Yields `None` if the stream was finished. Otherwise, yields a segment of data and its
+162    /// offset in the stream. If `ordered` is `true`, the chunk's offset will be immediately after
+163    /// the last data yielded by `read()` or `read_chunk()`. If `ordered` is `false`, segments may
+164    /// be received in any order, and the `Chunk`'s `offset` field can be used to determine
+165    /// ordering in the caller. Unordered reads are less prone to head-of-line blocking within a
+166    /// stream, but require the application to manage reassembling the original data.
+167    ///
+168    /// Slightly more efficient than `read` due to not copying. Chunk boundaries do not correspond
+169    /// to peer writes, and hence cannot be used as framing.
+170    ///
+171    /// This operation is cancel-safe.
+172    pub async fn read_chunk(
+173        &mut self,
+174        max_length: usize,
+175        ordered: bool,
+176    ) -> Result<Option<Chunk>, ReadError> {
+177        ReadChunk {
+178            stream: self,
+179            max_length,
+180            ordered,
+181        }
+182        .await
+183    }
+184
+185    /// Attempts to read a chunk from the stream.
+186    ///
+187    /// On success, returns `Poll::Ready(Ok(Some(chunk)))`. If `Poll::Ready(Ok(None))`
+188    /// is returned, it implies that EOF has been reached.
+189    ///
+190    /// If no data is available for reading, the method returns `Poll::Pending`
+191    /// and arranges for the current task (via cx.waker()) to receive a notification
+192    /// when the stream becomes readable or is closed.
+193    fn poll_read_chunk(
+194        &mut self,
+195        cx: &mut Context,
+196        max_length: usize,
+197        ordered: bool,
+198    ) -> Poll<Result<Option<Chunk>, ReadError>> {
+199        self.poll_read_generic(cx, ordered, |chunks| match chunks.next(max_length) {
+200            Ok(Some(chunk)) => ReadStatus::Readable(chunk),
+201            res => (None, res.err()).into(),
+202        })
+203    }
+204
+205    /// Read the next segments of data
+206    ///
+207    /// Fills `bufs` with the segments of data beginning immediately after the
+208    /// last data yielded by `read` or `read_chunk`, or `None` if the stream was
+209    /// finished.
+210    ///
+211    /// Slightly more efficient than `read` due to not copying. Chunk boundaries
+212    /// do not correspond to peer writes, and hence cannot be used as framing.
+213    ///
+214    /// This operation is cancel-safe.
+215    pub async fn read_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Option<usize>, ReadError> {
+216        ReadChunks { stream: self, bufs }.await
+217    }
+218
+219    /// Foundation of [`Self::read_chunks`]
+220    fn poll_read_chunks(
+221        &mut self,
+222        cx: &mut Context,
+223        bufs: &mut [Bytes],
+224    ) -> Poll<Result<Option<usize>, ReadError>> {
+225        if bufs.is_empty() {
+226            return Poll::Ready(Ok(Some(0)));
+227        }
+228
+229        self.poll_read_generic(cx, true, |chunks| {
+230            let mut read = 0;
+231            loop {
+232                if read >= bufs.len() {
+233                    // We know `read > 0` because `bufs` cannot be empty here
+234                    return ReadStatus::Readable(read);
+235                }
+236
+237                match chunks.next(usize::MAX) {
+238                    Ok(Some(chunk)) => {
+239                        bufs[read] = chunk.bytes;
+240                        read += 1;
+241                    }
+242                    res => return (if read == 0 { None } else { Some(read) }, res.err()).into(),
+243                }
+244            }
+245        })
+246    }
+247
+248    /// Convenience method to read all remaining data into a buffer
+249    ///
+250    /// Fails with [`ReadToEndError::TooLong`] on reading more than `size_limit` bytes, discarding
+251    /// all data read. Uses unordered reads to be more efficient than using `AsyncRead` would
+252    /// allow. `size_limit` should be set to limit worst-case memory use.
+253    ///
+254    /// If unordered reads have already been made, the resulting buffer may have gaps containing
+255    /// arbitrary data.
+256    ///
+257    /// This operation is *not* cancel-safe.
+258    ///
+259    /// [`ReadToEndError::TooLong`]: crate::ReadToEndError::TooLong
+260    pub async fn read_to_end(&mut self, size_limit: usize) -> Result<Vec<u8>, ReadToEndError> {
+261        ReadToEnd {
+262            stream: self,
+263            size_limit,
+264            read: Vec::new(),
+265            start: u64::MAX,
+266            end: 0,
+267        }
+268        .await
+269    }
+270
+271    /// Stop accepting data
+272    ///
+273    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+274    /// attempts to operate on a stream will yield `ClosedStream` errors.
+275    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+276        let mut conn = self.conn.state.lock("RecvStream::stop");
+277        if self.is_0rtt && conn.check_0rtt().is_err() {
+278            return Ok(());
+279        }
+280        conn.inner.recv_stream(self.stream).stop(error_code)?;
+281        conn.wake();
+282        self.all_data_read = true;
+283        Ok(())
+284    }
+285
+286    /// Check if this stream has been opened during 0-RTT.
+287    ///
+288    /// In which case any non-idempotent request should be considered dangerous at the application
+289    /// level. Because read data is subject to replay attacks.
+290    pub fn is_0rtt(&self) -> bool {
+291        self.is_0rtt
+292    }
+293
+294    /// Get the identity of this stream
+295    pub fn id(&self) -> StreamId {
+296        self.stream
+297    }
+298
+299    /// Completes when the stream has been reset by the peer or otherwise closed
+300    ///
+301    /// Yields `Some` with the reset error code when the stream is reset by the peer. Yields `None`
+302    /// when the stream was previously [`stop()`](Self::stop)ed, or when the stream was
+303    /// [`finish()`](crate::SendStream::finish)ed by the peer and all data has been received, after
+304    /// which it is no longer meaningful for the stream to be reset.
+305    ///
+306    /// This operation is cancel-safe.
+307    pub async fn received_reset(&mut self) -> Result<Option<VarInt>, ResetError> {
+308        poll_fn(|cx| {
+309            let mut conn = self.conn.state.lock("RecvStream::reset");
+310            if self.is_0rtt && conn.check_0rtt().is_err() {
+311                return Poll::Ready(Err(ResetError::ZeroRttRejected));
+312            }
+313
+314            if let Some(code) = self.reset {
+315                return Poll::Ready(Ok(Some(code)));
+316            }
+317
+318            match conn.inner.recv_stream(self.stream).received_reset() {
+319                Err(_) => Poll::Ready(Ok(None)),
+320                Ok(Some(error_code)) => {
+321                    // Stream state has just now been freed, so the connection may need to issue new
+322                    // stream ID flow control credit
+323                    conn.wake();
+324                    Poll::Ready(Ok(Some(error_code)))
+325                }
+326                Ok(None) => {
+327                    if let Some(e) = &conn.error {
+328                        return Poll::Ready(Err(e.clone().into()));
+329                    }
+330                    // Resets always notify readers, since a reset is an immediate read error. We
+331                    // could introduce a dedicated channel to reduce the risk of spurious wakeups,
+332                    // but that increased complexity is probably not justified, as an application
+333                    // that is expecting a reset is not likely to receive large amounts of data.
+334                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+335                    Poll::Pending
+336                }
+337            }
+338        })
+339        .await
+340    }
+341
+342    /// Handle common logic related to reading out of a receive stream
+343    ///
+344    /// This takes an `FnMut` closure that takes care of the actual reading process, matching
+345    /// the detailed read semantics for the calling function with a particular return type.
+346    /// The closure can read from the passed `&mut Chunks` and has to return the status after
+347    /// reading: the amount of data read, and the status after the final read call.
+348    fn poll_read_generic<T, U>(
+349        &mut self,
+350        cx: &mut Context,
+351        ordered: bool,
+352        mut read_fn: T,
+353    ) -> Poll<Result<Option<U>, ReadError>>
+354    where
+355        T: FnMut(&mut Chunks) -> ReadStatus<U>,
+356    {
+357        use proto::ReadError::*;
+358        if self.all_data_read {
+359            return Poll::Ready(Ok(None));
+360        }
+361
+362        let mut conn = self.conn.state.lock("RecvStream::poll_read");
+363        if self.is_0rtt {
+364            conn.check_0rtt().map_err(|()| ReadError::ZeroRttRejected)?;
+365        }
+366
+367        // If we stored an error during a previous call, return it now. This can happen if a
+368        // `read_fn` both wants to return data and also returns an error in its final stream status.
+369        let status = match self.reset {
+370            Some(code) => ReadStatus::Failed(None, Reset(code)),
+371            None => {
+372                let mut recv = conn.inner.recv_stream(self.stream);
+373                let mut chunks = recv.read(ordered)?;
+374                let status = read_fn(&mut chunks);
+375                if chunks.finalize().should_transmit() {
+376                    conn.wake();
+377                }
+378                status
+379            }
+380        };
+381
+382        match status {
+383            ReadStatus::Readable(read) => Poll::Ready(Ok(Some(read))),
+384            ReadStatus::Finished(read) => {
+385                self.all_data_read = true;
+386                Poll::Ready(Ok(read))
+387            }
+388            ReadStatus::Failed(read, Blocked) => match read {
+389                Some(val) => Poll::Ready(Ok(Some(val))),
+390                None => {
+391                    if let Some(ref x) = conn.error {
+392                        return Poll::Ready(Err(ReadError::ConnectionLost(x.clone())));
+393                    }
+394                    conn.blocked_readers.insert(self.stream, cx.waker().clone());
+395                    Poll::Pending
+396                }
+397            },
+398            ReadStatus::Failed(read, Reset(error_code)) => match read {
+399                None => {
+400                    self.all_data_read = true;
+401                    self.reset = Some(error_code);
+402                    Poll::Ready(Err(ReadError::Reset(error_code)))
+403                }
+404                done => {
+405                    self.reset = Some(error_code);
+406                    Poll::Ready(Ok(done))
+407                }
+408            },
+409        }
+410    }
+411}
+412
+413enum ReadStatus<T> {
+414    Readable(T),
+415    Finished(Option<T>),
+416    Failed(Option<T>, proto::ReadError),
+417}
+418
+419impl<T> From<(Option<T>, Option<proto::ReadError>)> for ReadStatus<T> {
+420    fn from(status: (Option<T>, Option<proto::ReadError>)) -> Self {
+421        match status {
+422            (read, None) => Self::Finished(read),
+423            (read, Some(e)) => Self::Failed(read, e),
+424        }
+425    }
+426}
+427
+428/// Future produced by [`RecvStream::read_to_end()`].
+429///
+430/// [`RecvStream::read_to_end()`]: crate::RecvStream::read_to_end
+431struct ReadToEnd<'a> {
+432    stream: &'a mut RecvStream,
+433    read: Vec<(Bytes, u64)>,
+434    start: u64,
+435    end: u64,
+436    size_limit: usize,
+437}
+438
+439impl Future for ReadToEnd<'_> {
+440    type Output = Result<Vec<u8>, ReadToEndError>;
+441    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+442        loop {
+443            match ready!(self.stream.poll_read_chunk(cx, usize::MAX, false))? {
+444                Some(chunk) => {
+445                    self.start = self.start.min(chunk.offset);
+446                    let end = chunk.bytes.len() as u64 + chunk.offset;
+447                    if (end - self.start) > self.size_limit as u64 {
+448                        return Poll::Ready(Err(ReadToEndError::TooLong));
+449                    }
+450                    self.end = self.end.max(end);
+451                    self.read.push((chunk.bytes, chunk.offset));
+452                }
+453                None => {
+454                    if self.end == 0 {
+455                        // Never received anything
+456                        return Poll::Ready(Ok(Vec::new()));
+457                    }
+458                    let start = self.start;
+459                    let mut buffer = vec![0; (self.end - start) as usize];
+460                    for (data, offset) in self.read.drain(..) {
+461                        let offset = (offset - start) as usize;
+462                        buffer[offset..offset + data.len()].copy_from_slice(&data);
+463                    }
+464                    return Poll::Ready(Ok(buffer));
+465                }
+466            }
+467        }
+468    }
+469}
+470
+471/// Errors from [`RecvStream::read_to_end`]
+472#[derive(Debug, Error, Clone, PartialEq, Eq)]
+473pub enum ReadToEndError {
+474    /// An error occurred during reading
+475    #[error("read error: {0}")]
+476    Read(#[from] ReadError),
+477    /// The stream is larger than the user-supplied limit
+478    #[error("stream too long")]
+479    TooLong,
+480}
+481
+482#[cfg(feature = "futures-io")]
+483impl futures_io::AsyncRead for RecvStream {
+484    fn poll_read(
+485        self: Pin<&mut Self>,
+486        cx: &mut Context,
+487        buf: &mut [u8],
+488    ) -> Poll<io::Result<usize>> {
+489        let mut buf = ReadBuf::new(buf);
+490        ready!(Self::poll_read_buf(self.get_mut(), cx, &mut buf))?;
+491        Poll::Ready(Ok(buf.filled().len()))
+492    }
+493}
+494
+495impl tokio::io::AsyncRead for RecvStream {
+496    fn poll_read(
+497        self: Pin<&mut Self>,
+498        cx: &mut Context<'_>,
+499        buf: &mut ReadBuf<'_>,
+500    ) -> Poll<io::Result<()>> {
+501        ready!(Self::poll_read_buf(self.get_mut(), cx, buf))?;
+502        Poll::Ready(Ok(()))
+503    }
+504}
+505
+506impl Drop for RecvStream {
+507    fn drop(&mut self) {
+508        let mut conn = self.conn.state.lock("RecvStream::drop");
+509
+510        // clean up any previously registered wakers
+511        conn.blocked_readers.remove(&self.stream);
+512
+513        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+514            return;
+515        }
+516        if !self.all_data_read {
+517            // Ignore ClosedStream errors
+518            let _ = conn.inner.recv_stream(self.stream).stop(0u32.into());
+519            conn.wake();
+520        }
+521    }
+522}
+523
+524/// Errors that arise from reading from a stream.
+525#[derive(Debug, Error, Clone, PartialEq, Eq)]
+526pub enum ReadError {
+527    /// The peer abandoned transmitting data on this stream
+528    ///
+529    /// Carries an application-defined error code.
+530    #[error("stream reset by peer: error {0}")]
+531    Reset(VarInt),
+532    /// The connection was lost
+533    #[error("connection lost")]
+534    ConnectionLost(#[from] ConnectionError),
+535    /// The stream has already been stopped, finished, or reset
+536    #[error("closed stream")]
+537    ClosedStream,
+538    /// Attempted an ordered read following an unordered read
+539    ///
+540    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
+541    /// stream which cannot be recovered, making further ordered reads impossible.
+542    #[error("ordered read after unordered read")]
+543    IllegalOrderedRead,
+544    /// This was a 0-RTT stream and the server rejected it
+545    ///
+546    /// Can only occur on clients for 0-RTT streams, which can be opened using
+547    /// [`Connecting::into_0rtt()`].
+548    ///
+549    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+550    #[error("0-RTT rejected")]
+551    ZeroRttRejected,
+552}
+553
+554impl From<ReadableError> for ReadError {
+555    fn from(e: ReadableError) -> Self {
+556        match e {
+557            ReadableError::ClosedStream => Self::ClosedStream,
+558            ReadableError::IllegalOrderedRead => Self::IllegalOrderedRead,
+559        }
+560    }
+561}
+562
+563impl From<ResetError> for ReadError {
+564    fn from(e: ResetError) -> Self {
+565        match e {
+566            ResetError::ConnectionLost(e) => Self::ConnectionLost(e),
+567            ResetError::ZeroRttRejected => Self::ZeroRttRejected,
+568        }
+569    }
+570}
+571
+572impl From<ReadError> for io::Error {
+573    fn from(x: ReadError) -> Self {
+574        use ReadError::*;
+575        let kind = match x {
+576            Reset { .. } | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+577            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+578            IllegalOrderedRead => io::ErrorKind::InvalidInput,
+579        };
+580        Self::new(kind, x)
+581    }
+582}
+583
+584/// Errors that arise while waiting for a stream to be reset
+585#[derive(Debug, Error, Clone, PartialEq, Eq)]
+586pub enum ResetError {
+587    /// The connection was lost
+588    #[error("connection lost")]
+589    ConnectionLost(#[from] ConnectionError),
+590    /// This was a 0-RTT stream and the server rejected it
+591    ///
+592    /// Can only occur on clients for 0-RTT streams, which can be opened using
+593    /// [`Connecting::into_0rtt()`].
+594    ///
+595    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+596    #[error("0-RTT rejected")]
+597    ZeroRttRejected,
+598}
+599
+600impl From<ResetError> for io::Error {
+601    fn from(x: ResetError) -> Self {
+602        use ResetError::*;
+603        let kind = match x {
+604            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+605            ConnectionLost(_) => io::ErrorKind::NotConnected,
+606        };
+607        Self::new(kind, x)
+608    }
+609}
+610
+611/// Future produced by [`RecvStream::read()`].
+612///
+613/// [`RecvStream::read()`]: crate::RecvStream::read
+614struct Read<'a> {
+615    stream: &'a mut RecvStream,
+616    buf: ReadBuf<'a>,
+617}
+618
+619impl Future for Read<'_> {
+620    type Output = Result<Option<usize>, ReadError>;
+621
+622    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+623        let this = self.get_mut();
+624        ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+625        match this.buf.filled().len() {
+626            0 if this.buf.capacity() != 0 => Poll::Ready(Ok(None)),
+627            n => Poll::Ready(Ok(Some(n))),
+628        }
+629    }
+630}
+631
+632/// Future produced by [`RecvStream::read_exact()`].
+633///
+634/// [`RecvStream::read_exact()`]: crate::RecvStream::read_exact
+635struct ReadExact<'a> {
+636    stream: &'a mut RecvStream,
+637    buf: ReadBuf<'a>,
+638}
+639
+640impl Future for ReadExact<'_> {
+641    type Output = Result<(), ReadExactError>;
+642    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+643        let this = self.get_mut();
+644        let mut remaining = this.buf.remaining();
+645        while remaining > 0 {
+646            ready!(this.stream.poll_read_buf(cx, &mut this.buf))?;
+647            let new = this.buf.remaining();
+648            if new == remaining {
+649                return Poll::Ready(Err(ReadExactError::FinishedEarly(this.buf.filled().len())));
+650            }
+651            remaining = new;
+652        }
+653        Poll::Ready(Ok(()))
+654    }
+655}
+656
+657/// Errors that arise from reading from a stream.
+658#[derive(Debug, Error, Clone, PartialEq, Eq)]
+659pub enum ReadExactError {
+660    /// The stream finished before all bytes were read
+661    #[error("stream finished early ({0} bytes read)")]
+662    FinishedEarly(usize),
+663    /// A read error occurred
+664    #[error(transparent)]
+665    ReadError(#[from] ReadError),
+666}
+667
+668/// Future produced by [`RecvStream::read_chunk()`].
+669///
+670/// [`RecvStream::read_chunk()`]: crate::RecvStream::read_chunk
+671struct ReadChunk<'a> {
+672    stream: &'a mut RecvStream,
+673    max_length: usize,
+674    ordered: bool,
+675}
+676
+677impl Future for ReadChunk<'_> {
+678    type Output = Result<Option<Chunk>, ReadError>;
+679    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+680        let (max_length, ordered) = (self.max_length, self.ordered);
+681        self.stream.poll_read_chunk(cx, max_length, ordered)
+682    }
+683}
+684
+685/// Future produced by [`RecvStream::read_chunks()`].
+686///
+687/// [`RecvStream::read_chunks()`]: crate::RecvStream::read_chunks
+688struct ReadChunks<'a> {
+689    stream: &'a mut RecvStream,
+690    bufs: &'a mut [Bytes],
+691}
+692
+693impl Future for ReadChunks<'_> {
+694    type Output = Result<Option<usize>, ReadError>;
+695    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+696        let this = self.get_mut();
+697        this.stream.poll_read_chunks(cx, this.bufs)
+698    }
+699}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/runtime/mod.rs.html b/pr/234/docs/src/iroh_quinn/runtime/mod.rs.html new file mode 100644 index 000000000..54b401924 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/runtime/mod.rs.html @@ -0,0 +1,250 @@ +mod.rs - source

iroh_quinn/runtime/
mod.rs

1#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+2use std::sync::Arc;
+3use std::{
+4    fmt::{self, Debug},
+5    future::Future,
+6    io::{self, IoSliceMut},
+7    net::SocketAddr,
+8    pin::Pin,
+9    task::{Context, Poll},
+10};
+11
+12use udp::{RecvMeta, Transmit};
+13
+14use crate::Instant;
+15
+16/// Abstracts I/O and timer operations for runtime independence
+17pub trait Runtime: Send + Sync + Debug + 'static {
+18    /// Construct a timer that will expire at `i`
+19    fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>;
+20    /// Drive `future` to completion in the background
+21    #[track_caller]
+22    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
+23    /// Convert `t` into the socket type used by this runtime
+24    #[cfg(not(wasm_browser))]
+25    fn wrap_udp_socket(&self, t: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>>;
+26    /// Look up the current time
+27    ///
+28    /// Allows simulating the flow of time for testing.
+29    fn now(&self) -> Instant {
+30        Instant::now()
+31    }
+32}
+33
+34/// Abstract implementation of an async timer for runtime independence
+35pub trait AsyncTimer: Send + Debug + 'static {
+36    /// Update the timer to expire at `i`
+37    fn reset(self: Pin<&mut Self>, i: Instant);
+38    /// Check whether the timer has expired, and register to be woken if not
+39    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()>;
+40}
+41
+42/// Abstract implementation of a UDP socket for runtime independence
+43pub trait AsyncUdpSocket: Send + Sync + Debug + 'static {
+44    /// Create a [`UdpSender`] that can register a single task for write-readiness notifications
+45    /// and send a transmit, if ready.
+46    ///
+47    /// A `poll_send` method on a single object can usually store only one [`Waker`] at a time,
+48    /// i.e. allow at most one caller to wait for an event. This method allows any number of
+49    /// interested tasks to construct their own [`UdpSender`] object. They can all then wait for the
+50    /// same event and be notified concurrently, because each [`UdpSender`] can store a separate
+51    /// [`Waker`].
+52    ///
+53    /// [`Waker`]: std::task::Waker
+54    fn create_sender(&self) -> Pin<Box<dyn UdpSender>>;
+55
+56    /// Receive UDP datagrams, or register to be woken if receiving may succeed in the future
+57    fn poll_recv(
+58        &mut self,
+59        cx: &mut Context,
+60        bufs: &mut [IoSliceMut<'_>],
+61        meta: &mut [RecvMeta],
+62    ) -> Poll<io::Result<usize>>;
+63
+64    /// Look up the local IP address and port used by this socket
+65    fn local_addr(&self) -> io::Result<SocketAddr>;
+66
+67    /// Maximum number of datagrams that might be described by a single [`RecvMeta`]
+68    fn max_receive_segments(&self) -> usize {
+69        1
+70    }
+71
+72    /// Whether datagrams might get fragmented into multiple parts
+73    ///
+74    /// Sockets should prevent this for best performance. See e.g. the `IPV6_DONTFRAG` socket
+75    /// option.
+76    fn may_fragment(&self) -> bool {
+77        true
+78    }
+79}
+80
+81/// An object for asynchronously writing to an associated [`AsyncUdpSocket`].
+82///
+83/// Any number of [`UdpSender`]s may exist for a single [`AsyncUdpSocket`]. Each [`UdpSender`] is
+84/// responsible for notifying at most one task for send readiness.
+85pub trait UdpSender: Send + Sync + Debug + 'static {
+86    /// Send a UDP datagram, or register to be woken if sending may succeed in the future.
+87    ///
+88    /// Usually implementations of this will poll the socket for writability before trying to
+89    /// write to them, and retry both if writing fails.
+90    ///
+91    /// Quinn will create multiple [`UdpSender`]s, one for each task it's using it from. Thus it's
+92    /// important to poll the underlying socket in a way that doesn't overwrite wakers.
+93    ///
+94    /// A single [`UdpSender`] will be reused, even if `poll_send` returns `Poll::Ready` once,
+95    /// unlike [`Future::poll`], so calling it again after readiness should not panic.
+96    fn poll_send(
+97        self: Pin<&mut Self>,
+98        transmit: &Transmit,
+99        cx: &mut Context,
+100    ) -> Poll<io::Result<()>>;
+101
+102    /// Maximum number of datagrams that a [`Transmit`] may encode.
+103    fn max_transmit_segments(&self) -> usize {
+104        1
+105    }
+106}
+107
+108pin_project_lite::pin_project! {
+109    /// A helper for constructing [`UdpSender`]s from an underlying `Socket` type.
+110    ///
+111    /// This struct implements [`UdpSender`] if `MakeWritableFn` produces a `WritableFut`.
+112    ///
+113    /// Also serves as a trick, since `WritableFut` doesn't need to be a named future,
+114    /// it can be an anonymous async block, as long as `MakeWritableFn` produces that
+115    /// anonymous async block type.
+116    ///
+117    /// The `UdpSenderHelper` generic type parameters don't need to named, as it will be
+118    /// used in its dyn-compatible form as a `Pin<Box<dyn UdpSender>>`.
+119    struct UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut> {
+120        socket: Socket,
+121        make_writable_fut_fn: MakeWritableFutFn,
+122        #[pin]
+123        writable_fut: Option<WritableFut>,
+124    }
+125}
+126
+127impl<Socket, MakeWritableFutFn, WritableFut> Debug
+128    for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
+129{
+130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+131        f.write_str("UdpSender")
+132    }
+133}
+134
+135impl<Socket, MakeWritableFutFn, WriteableFut>
+136    UdpSenderHelper<Socket, MakeWritableFutFn, WriteableFut>
+137{
+138    /// Create helper that implements [`UdpSender`] from a socket.
+139    ///
+140    /// Additionally you need to provide what is essentially an async function
+141    /// that resolves once the socket is write-ready.
+142    ///
+143    /// See also the bounds on this struct's [`UdpSender`] implementation.
+144    #[cfg(any(feature = "runtime-smol", feature = "runtime-tokio",))]
+145    fn new(inner: Socket, make_fut: MakeWritableFutFn) -> Self {
+146        Self {
+147            socket: inner,
+148            make_writable_fut_fn: make_fut,
+149            writable_fut: None,
+150        }
+151    }
+152}
+153
+154impl<Socket, MakeWritableFutFn, WritableFut> super::UdpSender
+155    for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
+156where
+157    Socket: UdpSenderHelperSocket,
+158    MakeWritableFutFn: Fn(&Socket) -> WritableFut + Send + Sync + 'static,
+159    WritableFut: Future<Output = io::Result<()>> + Send + Sync + 'static,
+160{
+161    fn poll_send(
+162        self: Pin<&mut Self>,
+163        transmit: &udp::Transmit,
+164        cx: &mut Context,
+165    ) -> Poll<io::Result<()>> {
+166        let mut this = self.project();
+167        loop {
+168            if this.writable_fut.is_none() {
+169                this.writable_fut
+170                    .set(Some((this.make_writable_fut_fn)(this.socket)));
+171            }
+172            // We're forced to `unwrap` here because `Fut` may be `!Unpin`, which means we can't safely
+173            // obtain an `&mut WritableFut` after storing it in `self.writable_fut` when `self` is already behind `Pin`,
+174            // and if we didn't store it then we wouldn't be able to keep it alive between
+175            // `poll_send` calls.
+176            let result =
+177                std::task::ready!(this.writable_fut.as_mut().as_pin_mut().unwrap().poll(cx));
+178
+179            // Polling an arbitrary `Future` after it becomes ready is a logic error, so arrange for
+180            // a new `Future` to be created on the next call.
+181            this.writable_fut.set(None);
+182
+183            // If .writable() fails, propagate the error
+184            result?;
+185
+186            match this.socket.try_send(transmit) {
+187                // We thought the socket was writable, but it wasn't, then retry so that either another
+188                // `writable().await` call determines that the socket is indeed not writable and
+189                // registers us for a wakeup, or the send succeeds if this really was just a
+190                // transient failure.
+191                Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
+192                // In all other cases, either propagate the error or we're Ok
+193                result => return Poll::Ready(result),
+194            }
+195        }
+196    }
+197
+198    fn max_transmit_segments(&self) -> usize {
+199        self.socket.max_transmit_segments()
+200    }
+201}
+202
+203/// Parts of the [`UdpSender`] trait that aren't asynchronous or require storing wakers.
+204///
+205/// This trait is used by [`UdpSenderHelper`] to help construct [`UdpSender`]s.
+206trait UdpSenderHelperSocket: Send + Sync + 'static {
+207    /// Try to send a transmit, if the socket happens to be write-ready.
+208    ///
+209    /// If not write-ready, this is allowed to return [`std::io::ErrorKind::WouldBlock`].
+210    ///
+211    /// The [`UdpSenderHelper`] will use this to implement [`UdpSender::poll_send`].
+212    fn try_send(&self, transmit: &udp::Transmit) -> io::Result<()>;
+213
+214    /// See [`UdpSender::max_transmit_segments`].
+215    fn max_transmit_segments(&self) -> usize;
+216}
+217
+218/// Automatically select an appropriate runtime from those enabled at compile time
+219///
+220/// If `runtime-tokio` is enabled and this function is called from within a Tokio runtime context,
+221/// then `TokioRuntime` is returned. Otherwise, if `runtime-smol` is enabled, `SmolRuntime` is
+222/// returned. Otherwise, `None` is returned.
+223#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
+224#[allow(clippy::needless_return)] // Be sure we return the right thing
+225pub fn default_runtime() -> Option<Arc<dyn Runtime>> {
+226    #[cfg(feature = "runtime-tokio")]
+227    {
+228        if ::tokio::runtime::Handle::try_current().is_ok() {
+229            return Some(Arc::new(TokioRuntime));
+230        }
+231    }
+232
+233    #[cfg(feature = "runtime-smol")]
+234    {
+235        return Some(Arc::new(SmolRuntime));
+236    }
+237
+238    #[cfg(not(feature = "runtime-smol"))]
+239    None
+240}
+241
+242#[cfg(feature = "runtime-tokio")]
+243mod tokio;
+244#[cfg(feature = "runtime-tokio")]
+245pub use tokio::TokioRuntime;
+246
+247#[cfg(feature = "runtime-smol")]
+248mod smol;
+249#[cfg(feature = "runtime-smol")]
+250pub use smol::*;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/runtime/smol.rs.html b/pr/234/docs/src/iroh_quinn/runtime/smol.rs.html new file mode 100644 index 000000000..257a6b251 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/runtime/smol.rs.html @@ -0,0 +1,101 @@ +smol.rs - source

iroh_quinn/runtime/
smol.rs

1use std::{
+2    future::Future,
+3    pin::Pin,
+4    task::{Context, Poll},
+5    time::Instant,
+6};
+7use std::{io, sync::Arc, task::ready};
+8
+9use async_io::Async;
+10use async_io::Timer;
+11
+12use super::AsyncTimer;
+13use super::{AsyncUdpSocket, Runtime, UdpSender, UdpSenderHelper, UdpSenderHelperSocket};
+14
+15/// A Quinn runtime for smol
+16#[derive(Debug)]
+17pub struct SmolRuntime;
+18
+19impl Runtime for SmolRuntime {
+20    fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>> {
+21        Box::pin(Timer::at(t))
+22    }
+23
+24    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
+25        ::smol::spawn(future).detach();
+26    }
+27
+28    fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>> {
+29        Ok(Box::new(UdpSocket::new(sock)?))
+30    }
+31}
+32
+33impl AsyncTimer for Timer {
+34    fn reset(mut self: Pin<&mut Self>, t: Instant) {
+35        self.set_at(t)
+36    }
+37
+38    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
+39        Future::poll(self, cx).map(|_| ())
+40    }
+41}
+42
+43#[derive(Debug, Clone)]
+44struct UdpSocket {
+45    io: Arc<Async<std::net::UdpSocket>>,
+46    inner: Arc<udp::UdpSocketState>,
+47}
+48
+49impl UdpSocket {
+50    fn new(sock: std::net::UdpSocket) -> io::Result<Self> {
+51        Ok(Self {
+52            inner: Arc::new(udp::UdpSocketState::new((&sock).into())?),
+53            io: Arc::new(Async::new_nonblocking(sock)?),
+54        })
+55    }
+56}
+57
+58impl UdpSenderHelperSocket for UdpSocket {
+59    fn max_transmit_segments(&self) -> usize {
+60        self.inner.max_gso_segments()
+61    }
+62
+63    fn try_send(&self, transmit: &udp::Transmit) -> io::Result<()> {
+64        self.inner.send((&self.io).into(), transmit)
+65    }
+66}
+67
+68impl AsyncUdpSocket for UdpSocket {
+69    fn create_sender(&self) -> Pin<Box<dyn UdpSender>> {
+70        Box::pin(UdpSenderHelper::new(self.clone(), |socket: &Self| {
+71            let socket = socket.clone();
+72            async move { socket.io.writable().await }
+73        }))
+74    }
+75
+76    fn poll_recv(
+77        &mut self,
+78        cx: &mut Context,
+79        bufs: &mut [io::IoSliceMut<'_>],
+80        meta: &mut [udp::RecvMeta],
+81    ) -> Poll<io::Result<usize>> {
+82        loop {
+83            ready!(self.io.poll_readable(cx))?;
+84            if let Ok(res) = self.inner.recv((&self.io).into(), bufs, meta) {
+85                return Poll::Ready(Ok(res));
+86            }
+87        }
+88    }
+89
+90    fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
+91        self.io.as_ref().as_ref().local_addr()
+92    }
+93
+94    fn may_fragment(&self) -> bool {
+95        self.inner.may_fragment()
+96    }
+97
+98    fn max_receive_segments(&self) -> usize {
+99        self.inner.gro_segments()
+100    }
+101}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/runtime/tokio.rs.html b/pr/234/docs/src/iroh_quinn/runtime/tokio.rs.html new file mode 100644 index 000000000..150f0a314 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/runtime/tokio.rs.html @@ -0,0 +1,105 @@ +tokio.rs - source

iroh_quinn/runtime/
tokio.rs

1use std::{
+2    fmt::Debug,
+3    future::Future,
+4    io,
+5    pin::Pin,
+6    sync::Arc,
+7    task::{Context, Poll, ready},
+8    time::Instant,
+9};
+10
+11use tokio::{
+12    io::Interest,
+13    time::{Sleep, sleep_until},
+14};
+15
+16use super::{AsyncTimer, AsyncUdpSocket, Runtime, UdpSenderHelper, UdpSenderHelperSocket};
+17
+18/// A Quinn runtime for Tokio
+19#[derive(Debug)]
+20pub struct TokioRuntime;
+21
+22impl Runtime for TokioRuntime {
+23    fn new_timer(&self, t: Instant) -> Pin<Box<dyn AsyncTimer>> {
+24        Box::pin(sleep_until(t.into()))
+25    }
+26
+27    fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) {
+28        tokio::spawn(future);
+29    }
+30
+31    fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>> {
+32        Ok(Box::new(UdpSocket {
+33            inner: Arc::new(udp::UdpSocketState::new((&sock).into())?),
+34            io: Arc::new(tokio::net::UdpSocket::from_std(sock)?),
+35        }))
+36    }
+37
+38    fn now(&self) -> Instant {
+39        tokio::time::Instant::now().into_std()
+40    }
+41}
+42
+43impl AsyncTimer for Sleep {
+44    fn reset(self: Pin<&mut Self>, t: Instant) {
+45        Self::reset(self, t.into())
+46    }
+47    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
+48        Future::poll(self, cx)
+49    }
+50}
+51
+52#[derive(Debug, Clone)]
+53struct UdpSocket {
+54    io: Arc<tokio::net::UdpSocket>,
+55    inner: Arc<udp::UdpSocketState>,
+56}
+57
+58impl UdpSenderHelperSocket for UdpSocket {
+59    fn max_transmit_segments(&self) -> usize {
+60        self.inner.max_gso_segments()
+61    }
+62
+63    fn try_send(&self, transmit: &udp::Transmit) -> io::Result<()> {
+64        self.io.try_io(Interest::WRITABLE, || {
+65            self.inner.send((&self.io).into(), transmit)
+66        })
+67    }
+68}
+69
+70impl AsyncUdpSocket for UdpSocket {
+71    fn create_sender(&self) -> Pin<Box<dyn super::UdpSender>> {
+72        Box::pin(UdpSenderHelper::new(self.clone(), |socket: &Self| {
+73            let socket = socket.clone();
+74            async move { socket.io.writable().await }
+75        }))
+76    }
+77
+78    fn poll_recv(
+79        &mut self,
+80        cx: &mut Context,
+81        bufs: &mut [std::io::IoSliceMut<'_>],
+82        meta: &mut [udp::RecvMeta],
+83    ) -> Poll<io::Result<usize>> {
+84        loop {
+85            ready!(self.io.poll_recv_ready(cx))?;
+86            if let Ok(res) = self.io.try_io(Interest::READABLE, || {
+87                self.inner.recv((&self.io).into(), bufs, meta)
+88            }) {
+89                return Poll::Ready(Ok(res));
+90            }
+91        }
+92    }
+93
+94    fn local_addr(&self) -> io::Result<std::net::SocketAddr> {
+95        self.io.local_addr()
+96    }
+97
+98    fn may_fragment(&self) -> bool {
+99        self.inner.may_fragment()
+100    }
+101
+102    fn max_receive_segments(&self) -> usize {
+103        self.inner.gro_segments()
+104    }
+105}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/send_stream.rs.html b/pr/234/docs/src/iroh_quinn/send_stream.rs.html new file mode 100644 index 000000000..b21a2cdd1 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/send_stream.rs.html @@ -0,0 +1,443 @@ +send_stream.rs - source

iroh_quinn/
send_stream.rs

1use std::{
+2    future::{Future, poll_fn},
+3    io,
+4    pin::{Pin, pin},
+5    task::{Context, Poll},
+6};
+7
+8use bytes::Bytes;
+9use proto::{ClosedStream, ConnectionError, FinishError, StreamId, Written};
+10use thiserror::Error;
+11
+12use crate::{
+13    VarInt,
+14    connection::{ConnectionRef, State},
+15};
+16
+17/// A stream that can only be used to send data
+18///
+19/// If dropped, streams that haven't been explicitly [`reset()`] will be implicitly [`finish()`]ed,
+20/// continuing to (re)transmit previously written data until it has been fully acknowledged or the
+21/// connection is closed.
+22///
+23/// # Cancellation
+24///
+25/// A `write` method is said to be *cancel-safe* when dropping its future before the future becomes
+26/// ready will always result in no data being written to the stream. This is true of methods which
+27/// succeed immediately when any progress is made, and is not true of methods which might need to
+28/// perform multiple writes internally before succeeding. Each `write` method documents whether it is
+29/// cancel-safe.
+30///
+31/// [`reset()`]: SendStream::reset
+32/// [`finish()`]: SendStream::finish
+33#[derive(Debug)]
+34pub struct SendStream {
+35    conn: ConnectionRef,
+36    stream: StreamId,
+37    is_0rtt: bool,
+38}
+39
+40impl SendStream {
+41    pub(crate) fn new(conn: ConnectionRef, stream: StreamId, is_0rtt: bool) -> Self {
+42        Self {
+43            conn,
+44            stream,
+45            is_0rtt,
+46        }
+47    }
+48
+49    /// Write a buffer into this stream, returning how many bytes were written
+50    ///
+51    /// Unless this method errors, it waits until some amount of `buf` can be written into this
+52    /// stream, and then writes as much as it can without waiting again. Due to congestion and flow
+53    /// control, this may be shorter than `buf.len()`. On success this yields the length of the
+54    /// prefix that was written.
+55    ///
+56    /// # Cancel safety
+57    ///
+58    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+59    pub async fn write(&mut self, buf: &[u8]) -> Result<usize, WriteError> {
+60        poll_fn(|cx| self.execute_poll(cx, |s| s.write(buf))).await
+61    }
+62
+63    /// Write a buffer into this stream in its entirety
+64    ///
+65    /// This method repeatedly calls [`write`](Self::write) until all bytes are written, or an
+66    /// error occurs.
+67    ///
+68    /// # Cancel safety
+69    ///
+70    /// This method is *not* cancellation safe. Even if this does not resolve, some prefix of `buf`
+71    /// may have been written when previously polled.
+72    pub async fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteError> {
+73        while !buf.is_empty() {
+74            let written = self.write(buf).await?;
+75            buf = &buf[written..];
+76        }
+77        Ok(())
+78    }
+79
+80    /// Write a slice of [`Bytes`] into this stream, returning how much was written
+81    ///
+82    /// Bytes to try to write are provided to this method as an array of cheaply cloneable chunks.
+83    /// Unless this method errors, it waits until some amount of those bytes can be written into
+84    /// this stream, and then writes as much as it can without waiting again. Due to congestion and
+85    /// flow control, this may be less than the total number of bytes.
+86    ///
+87    /// On success, this method both mutates `bufs` and yields an informative [`Written`] struct
+88    /// indicating how much was written:
+89    ///
+90    /// - [`Bytes`] chunks that were fully written are mutated to be [empty](Bytes::is_empty).
+91    /// - If a [`Bytes`] chunk was partially written, it is [split to](Bytes::split_to) contain
+92    ///   only the suffix of bytes that were not written.
+93    /// - The yielded [`Written`] struct indicates how many chunks were fully written as well as
+94    ///   how many bytes were written.
+95    ///
+96    /// # Cancel safety
+97    ///
+98    /// This method is cancellation safe. If this does not resolve, no bytes were written.
+99    pub async fn write_chunks(&mut self, bufs: &mut [Bytes]) -> Result<Written, WriteError> {
+100        poll_fn(|cx| self.execute_poll(cx, |s| s.write_chunks(bufs))).await
+101    }
+102
+103    /// Write a single [`Bytes`] into this stream in its entirety
+104    ///
+105    /// Bytes to write are provided to this method as an single cheaply cloneable chunk. This
+106    /// method repeatedly calls [`write_chunks`](Self::write_chunks) until all bytes are written,
+107    /// or an error occurs.
+108    ///
+109    /// # Cancel safety
+110    ///
+111    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+112    /// been written when previously polled.
+113    pub async fn write_chunk(&mut self, buf: Bytes) -> Result<(), WriteError> {
+114        self.write_all_chunks(&mut [buf]).await?;
+115        Ok(())
+116    }
+117
+118    /// Write a slice of [`Bytes`] into this stream in its entirety
+119    ///
+120    /// Bytes to write are provided to this method as an array of cheaply cloneable chunks. This
+121    /// method repeatedly calls [`write_chunks`](Self::write_chunks) until all bytes are written,
+122    /// or an error occurs. This method mutates `bufs` by mutating all chunks to be
+123    /// [empty](Bytes::is_empty).
+124    ///
+125    /// # Cancel safety
+126    ///
+127    /// This method is *not* cancellation safe. Even if this does not resolve, some bytes may have
+128    /// been written when previously polled.
+129    pub async fn write_all_chunks(&mut self, mut bufs: &mut [Bytes]) -> Result<(), WriteError> {
+130        while !bufs.is_empty() {
+131            let written = self.write_chunks(bufs).await?;
+132            bufs = &mut bufs[written.chunks..];
+133        }
+134        Ok(())
+135    }
+136
+137    fn execute_poll<F, R>(&mut self, cx: &mut Context, write_fn: F) -> Poll<Result<R, WriteError>>
+138    where
+139        F: FnOnce(&mut proto::SendStream) -> Result<R, proto::WriteError>,
+140    {
+141        use proto::WriteError::*;
+142        let mut conn = self.conn.state.lock("SendStream::poll_write");
+143        if self.is_0rtt {
+144            conn.check_0rtt()
+145                .map_err(|()| WriteError::ZeroRttRejected)?;
+146        }
+147        if let Some(ref x) = conn.error {
+148            return Poll::Ready(Err(WriteError::ConnectionLost(x.clone())));
+149        }
+150
+151        let result = match write_fn(&mut conn.inner.send_stream(self.stream)) {
+152            Ok(result) => result,
+153            Err(Blocked) => {
+154                conn.blocked_writers.insert(self.stream, cx.waker().clone());
+155                return Poll::Pending;
+156            }
+157            Err(Stopped(error_code)) => {
+158                return Poll::Ready(Err(WriteError::Stopped(error_code)));
+159            }
+160            Err(ClosedStream) => {
+161                return Poll::Ready(Err(WriteError::ClosedStream));
+162            }
+163        };
+164
+165        conn.wake();
+166        Poll::Ready(Ok(result))
+167    }
+168
+169    /// Notify the peer that no more data will ever be written to this stream
+170    ///
+171    /// It is an error to write to a [`SendStream`] after `finish()`ing it. [`reset()`](Self::reset)
+172    /// may still be called after `finish` to abandon transmission of any stream data that might
+173    /// still be buffered.
+174    ///
+175    /// To wait for the peer to receive all buffered stream data, see [`stopped()`](Self::stopped).
+176    ///
+177    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+178    /// called. This error is harmless and serves only to indicate that the caller may have
+179    /// incorrect assumptions about the stream's state.
+180    pub fn finish(&mut self) -> Result<(), ClosedStream> {
+181        let mut conn = self.conn.state.lock("finish");
+182        match conn.inner.send_stream(self.stream).finish() {
+183            Ok(()) => {
+184                conn.wake();
+185                Ok(())
+186            }
+187            Err(FinishError::ClosedStream) => Err(ClosedStream::default()),
+188            // Harmless. If the application needs to know about stopped streams at this point, it
+189            // should call `stopped`.
+190            Err(FinishError::Stopped(_)) => Ok(()),
+191        }
+192    }
+193
+194    /// Close the send stream immediately.
+195    ///
+196    /// No new data can be written after calling this method. Locally buffered data is dropped, and
+197    /// previously transmitted data will no longer be retransmitted if lost. If an attempt has
+198    /// already been made to finish the stream, the peer may still receive all written data.
+199    ///
+200    /// May fail if [`finish()`](Self::finish) or [`reset()`](Self::reset) was previously
+201    /// called. This error is harmless and serves only to indicate that the caller may have
+202    /// incorrect assumptions about the stream's state.
+203    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+204        let mut conn = self.conn.state.lock("SendStream::reset");
+205        if self.is_0rtt && conn.check_0rtt().is_err() {
+206            return Ok(());
+207        }
+208        conn.inner.send_stream(self.stream).reset(error_code)?;
+209        conn.wake();
+210        Ok(())
+211    }
+212
+213    /// Set the priority of the send stream
+214    ///
+215    /// Every send stream has an initial priority of 0. Locally buffered data from streams with
+216    /// higher priority will be transmitted before data from streams with lower priority. Changing
+217    /// the priority of a stream with pending data may only take effect after that data has been
+218    /// transmitted. Using many different priority levels per connection may have a negative
+219    /// impact on performance.
+220    pub fn set_priority(&self, priority: i32) -> Result<(), ClosedStream> {
+221        let mut conn = self.conn.state.lock("SendStream::set_priority");
+222        conn.inner.send_stream(self.stream).set_priority(priority)?;
+223        Ok(())
+224    }
+225
+226    /// Get the priority of the send stream
+227    pub fn priority(&self) -> Result<i32, ClosedStream> {
+228        let mut conn = self.conn.state.lock("SendStream::priority");
+229        conn.inner.send_stream(self.stream).priority()
+230    }
+231
+232    /// Completes when the peer stops the stream or reads the stream to completion
+233    ///
+234    /// Yields `Some` with the stop error code if the peer stops the stream. Yields `None` if the
+235    /// local side [`finish()`](Self::finish)es the stream and then the peer acknowledges receipt
+236    /// of all stream data (although not necessarily the processing of it), after which the peer
+237    /// closing the stream is no longer meaningful.
+238    ///
+239    /// For a variety of reasons, the peer may not send acknowledgements immediately upon receiving
+240    /// data. As such, relying on `stopped` to know when the peer has read a stream to completion
+241    /// may introduce more latency than using an application-level response of some sort.
+242    pub fn stopped(
+243        &self,
+244    ) -> impl Future<Output = Result<Option<VarInt>, StoppedError>> + Send + Sync + 'static {
+245        let conn = self.conn.clone();
+246        let stream = self.stream;
+247        let is_0rtt = self.is_0rtt;
+248        async move {
+249            loop {
+250                // The `Notify::notified` future needs to be created while the lock is being held,
+251                // otherwise a wakeup could be missed if triggered in between releasing the lock
+252                // and creating the future.
+253                // The lock may only be held in a block without `await`s, otherwise the future
+254                // becomes `!Send`. `Notify::notified` is lifetime-bound to `Notify`, therefore
+255                // we need to declare `notify` outside of the block, and initialize it inside.
+256                let notify;
+257                {
+258                    let mut conn = conn.state.lock("SendStream::stopped");
+259                    if let Some(output) = send_stream_stopped(&mut conn, stream, is_0rtt) {
+260                        return output;
+261                    }
+262
+263                    notify = conn.stopped.entry(stream).or_default().clone();
+264                    notify.notified()
+265                }
+266                .await
+267            }
+268        }
+269    }
+270
+271    /// Get the identity of this stream
+272    pub fn id(&self) -> StreamId {
+273        self.stream
+274    }
+275
+276    /// Attempt to write bytes from buf into the stream.
+277    ///
+278    /// On success, returns Poll::Ready(Ok(num_bytes_written)).
+279    ///
+280    /// If the stream is not ready for writing, the method returns Poll::Pending and arranges
+281    /// for the current task (via cx.waker().wake_by_ref()) to receive a notification when the
+282    /// stream becomes writable or is closed.
+283    pub fn poll_write(
+284        self: Pin<&mut Self>,
+285        cx: &mut Context,
+286        buf: &[u8],
+287    ) -> Poll<Result<usize, WriteError>> {
+288        pin!(self.get_mut().write(buf)).as_mut().poll(cx)
+289    }
+290}
+291
+292/// Check if a send stream is stopped.
+293///
+294/// Returns `Some` if the stream is stopped or the connection is closed.
+295/// Returns `None` if the stream is not stopped.
+296fn send_stream_stopped(
+297    conn: &mut State,
+298    stream: StreamId,
+299    is_0rtt: bool,
+300) -> Option<Result<Option<VarInt>, StoppedError>> {
+301    if is_0rtt && conn.check_0rtt().is_err() {
+302        return Some(Err(StoppedError::ZeroRttRejected));
+303    }
+304    match conn.inner.send_stream(stream).stopped() {
+305        Err(ClosedStream { .. }) => Some(Ok(None)),
+306        Ok(Some(error_code)) => Some(Ok(Some(error_code))),
+307        Ok(None) => conn.error.clone().map(|error| Err(error.into())),
+308    }
+309}
+310
+311#[cfg(feature = "futures-io")]
+312impl futures_io::AsyncWrite for SendStream {
+313    fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
+314        self.poll_write(cx, buf).map_err(Into::into)
+315    }
+316
+317    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+318        Poll::Ready(Ok(()))
+319    }
+320
+321    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+322        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+323    }
+324}
+325
+326impl tokio::io::AsyncWrite for SendStream {
+327    fn poll_write(
+328        self: Pin<&mut Self>,
+329        cx: &mut Context<'_>,
+330        buf: &[u8],
+331    ) -> Poll<io::Result<usize>> {
+332        self.poll_write(cx, buf).map_err(Into::into)
+333    }
+334
+335    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+336        Poll::Ready(Ok(()))
+337    }
+338
+339    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<io::Result<()>> {
+340        Poll::Ready(self.get_mut().finish().map_err(Into::into))
+341    }
+342}
+343
+344impl Drop for SendStream {
+345    fn drop(&mut self) {
+346        let mut conn = self.conn.state.lock("SendStream::drop");
+347
+348        // clean up any previously registered wakers
+349        conn.blocked_writers.remove(&self.stream);
+350
+351        if conn.error.is_some() || (self.is_0rtt && conn.check_0rtt().is_err()) {
+352            return;
+353        }
+354        match conn.inner.send_stream(self.stream).finish() {
+355            Ok(()) => conn.wake(),
+356            Err(FinishError::Stopped(reason)) => {
+357                if conn.inner.send_stream(self.stream).reset(reason).is_ok() {
+358                    conn.wake();
+359                }
+360            }
+361            // Already finished or reset, which is fine.
+362            Err(FinishError::ClosedStream) => {}
+363        }
+364    }
+365}
+366
+367/// Errors that arise from writing to a stream
+368#[derive(Debug, Error, Clone, PartialEq, Eq)]
+369pub enum WriteError {
+370    /// The peer is no longer accepting data on this stream
+371    ///
+372    /// Carries an application-defined error code.
+373    #[error("sending stopped by peer: error {0}")]
+374    Stopped(VarInt),
+375    /// The connection was lost
+376    #[error("connection lost")]
+377    ConnectionLost(#[from] ConnectionError),
+378    /// The stream has already been finished or reset
+379    #[error("closed stream")]
+380    ClosedStream,
+381    /// This was a 0-RTT stream and the server rejected it
+382    ///
+383    /// Can only occur on clients for 0-RTT streams, which can be opened using
+384    /// [`Connecting::into_0rtt()`].
+385    ///
+386    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+387    #[error("0-RTT rejected")]
+388    ZeroRttRejected,
+389}
+390
+391impl From<ClosedStream> for WriteError {
+392    #[inline]
+393    fn from(_: ClosedStream) -> Self {
+394        Self::ClosedStream
+395    }
+396}
+397
+398impl From<StoppedError> for WriteError {
+399    fn from(x: StoppedError) -> Self {
+400        match x {
+401            StoppedError::ConnectionLost(e) => Self::ConnectionLost(e),
+402            StoppedError::ZeroRttRejected => Self::ZeroRttRejected,
+403        }
+404    }
+405}
+406
+407impl From<WriteError> for io::Error {
+408    fn from(x: WriteError) -> Self {
+409        use WriteError::*;
+410        let kind = match x {
+411            Stopped(_) | ZeroRttRejected => io::ErrorKind::ConnectionReset,
+412            ConnectionLost(_) | ClosedStream => io::ErrorKind::NotConnected,
+413        };
+414        Self::new(kind, x)
+415    }
+416}
+417
+418/// Errors that arise while monitoring for a send stream stop from the peer
+419#[derive(Debug, Error, Clone, PartialEq, Eq)]
+420pub enum StoppedError {
+421    /// The connection was lost
+422    #[error("connection lost")]
+423    ConnectionLost(#[from] ConnectionError),
+424    /// This was a 0-RTT stream and the server rejected it
+425    ///
+426    /// Can only occur on clients for 0-RTT streams, which can be opened using
+427    /// [`Connecting::into_0rtt()`].
+428    ///
+429    /// [`Connecting::into_0rtt()`]: crate::Connecting::into_0rtt()
+430    #[error("0-RTT rejected")]
+431    ZeroRttRejected,
+432}
+433
+434impl From<StoppedError> for io::Error {
+435    fn from(x: StoppedError) -> Self {
+436        use StoppedError::*;
+437        let kind = match x {
+438            ZeroRttRejected => io::ErrorKind::ConnectionReset,
+439            ConnectionLost(_) => io::ErrorKind::NotConnected,
+440        };
+441        Self::new(kind, x)
+442    }
+443}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn/work_limiter.rs.html b/pr/234/docs/src/iroh_quinn/work_limiter.rs.html new file mode 100644 index 000000000..ad6c82eea --- /dev/null +++ b/pr/234/docs/src/iroh_quinn/work_limiter.rs.html @@ -0,0 +1,224 @@ +work_limiter.rs - source

iroh_quinn/
work_limiter.rs

1use crate::{Duration, Instant};
+2
+3/// Limits the amount of time spent on a certain type of work in a cycle
+4///
+5/// The limiter works dynamically: For a sampled subset of cycles it measures
+6/// the time that is approximately required for fulfilling 1 work item, and
+7/// calculates the amount of allowed work items per cycle.
+8/// The estimates are smoothed over all cycles where the exact duration is measured.
+9///
+10/// In cycles where no measurement is performed the previously determined work limit
+11/// is used.
+12///
+13/// For the limiter the exact definition of a work item does not matter.
+14/// It could for example track the amount of transmitted bytes per cycle,
+15/// or the amount of transmitted datagrams per cycle.
+16/// It will however work best if the required time to complete a work item is
+17/// constant.
+18#[derive(Debug)]
+19pub(crate) struct WorkLimiter {
+20    /// Whether to measure the required work time, or to use the previous estimates
+21    mode: Mode,
+22    /// The current cycle number
+23    cycle: u16,
+24    /// The time the cycle started - only used in measurement mode
+25    start_time: Option<Instant>,
+26    /// How many work items have been completed in the cycle
+27    completed: usize,
+28    /// The amount of work items which are allowed for a cycle
+29    allowed: usize,
+30    /// The desired cycle time
+31    desired_cycle_time: Duration,
+32    /// The estimated and smoothed time per work item in nanoseconds
+33    smoothed_time_per_work_item_nanos: f64,
+34}
+35
+36impl WorkLimiter {
+37    pub(crate) fn new(desired_cycle_time: Duration) -> Self {
+38        Self {
+39            mode: Mode::Measure,
+40            cycle: 0,
+41            start_time: None,
+42            completed: 0,
+43            allowed: 0,
+44            desired_cycle_time,
+45            smoothed_time_per_work_item_nanos: 0.0,
+46        }
+47    }
+48
+49    /// Starts one work cycle
+50    pub(crate) fn start_cycle(&mut self, now: impl Fn() -> Instant) {
+51        self.completed = 0;
+52        if let Mode::Measure = self.mode {
+53            self.start_time = Some(now());
+54        }
+55    }
+56
+57    /// Returns whether more work can be performed inside the `desired_cycle_time`
+58    ///
+59    /// Requires that previous work was tracked using `record_work`.
+60    pub(crate) fn allow_work(&mut self, now: impl Fn() -> Instant) -> bool {
+61        match self.mode {
+62            Mode::Measure => (now() - self.start_time.unwrap()) < self.desired_cycle_time,
+63            Mode::HistoricData => self.completed < self.allowed,
+64        }
+65    }
+66
+67    /// Records that `work` additional work items have been completed inside the cycle
+68    ///
+69    /// Must be called between `start_cycle` and `finish_cycle`.
+70    pub(crate) fn record_work(&mut self, work: usize) {
+71        self.completed += work;
+72    }
+73
+74    /// Finishes one work cycle
+75    ///
+76    /// For cycles where the exact duration is measured this will update the estimates
+77    /// for the time per work item and the limit of allowed work items per cycle.
+78    /// The estimate is updated using the same exponential averaging (smoothing)
+79    /// mechanism which is used for determining QUIC path rtts: The last value is
+80    /// weighted by 1/8, and the previous average by 7/8.
+81    pub(crate) fn finish_cycle(&mut self, now: impl Fn() -> Instant) {
+82        // If no work was done in the cycle drop the measurement, it won't be useful
+83        if self.completed == 0 {
+84            return;
+85        }
+86
+87        if let Mode::Measure = self.mode {
+88            let elapsed = now() - self.start_time.unwrap();
+89
+90            let time_per_work_item_nanos = (elapsed.as_nanos()) as f64 / self.completed as f64;
+91
+92            // Calculate the time per work item. We set this to at least 1ns to avoid
+93            // dividing by 0 when calculating the allowed amount of work items.
+94            self.smoothed_time_per_work_item_nanos = if self.allowed == 0 {
+95                // Initial estimate
+96                time_per_work_item_nanos
+97            } else {
+98                // Smoothed estimate
+99                (7.0 * self.smoothed_time_per_work_item_nanos + time_per_work_item_nanos) / 8.0
+100            }
+101            .max(1.0);
+102
+103            // Allow at least 1 work item in order to make progress
+104            self.allowed = (((self.desired_cycle_time.as_nanos()) as f64
+105                / self.smoothed_time_per_work_item_nanos) as usize)
+106                .max(1);
+107            self.start_time = None;
+108        }
+109
+110        self.cycle = self.cycle.wrapping_add(1);
+111        self.mode = match self.cycle % SAMPLING_INTERVAL {
+112            0 => Mode::Measure,
+113            _ => Mode::HistoricData,
+114        };
+115    }
+116}
+117
+118/// We take a measurement sample once every `SAMPLING_INTERVAL` cycles
+119const SAMPLING_INTERVAL: u16 = 256;
+120
+121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+122enum Mode {
+123    Measure,
+124    HistoricData,
+125}
+126
+127#[cfg(test)]
+128mod tests {
+129    use super::*;
+130    use std::cell::RefCell;
+131
+132    #[test]
+133    fn limit_work() {
+134        const CYCLE_TIME: Duration = Duration::from_millis(500);
+135        const BATCH_WORK_ITEMS: usize = 12;
+136        const BATCH_TIME: Duration = Duration::from_millis(100);
+137
+138        const EXPECTED_INITIAL_BATCHES: usize =
+139            (CYCLE_TIME.as_nanos() / BATCH_TIME.as_nanos()) as usize;
+140        const EXPECTED_ALLOWED_WORK_ITEMS: usize = EXPECTED_INITIAL_BATCHES * BATCH_WORK_ITEMS;
+141
+142        let mut limiter = WorkLimiter::new(CYCLE_TIME);
+143        reset_time();
+144
+145        // The initial cycle is measuring
+146        limiter.start_cycle(get_time);
+147        let mut initial_batches = 0;
+148        while limiter.allow_work(get_time) {
+149            limiter.record_work(BATCH_WORK_ITEMS);
+150            advance_time(BATCH_TIME);
+151            initial_batches += 1;
+152        }
+153        limiter.finish_cycle(get_time);
+154
+155        assert_eq!(initial_batches, EXPECTED_INITIAL_BATCHES);
+156        assert_eq!(limiter.allowed, EXPECTED_ALLOWED_WORK_ITEMS);
+157        let initial_time_per_work_item = limiter.smoothed_time_per_work_item_nanos;
+158
+159        // The next cycles are using historic data
+160        const BATCH_SIZES: [usize; 4] = [1, 2, 3, 5];
+161        for &batch_size in &BATCH_SIZES {
+162            limiter.start_cycle(get_time);
+163            let mut allowed_work = 0;
+164            while limiter.allow_work(get_time) {
+165                limiter.record_work(batch_size);
+166                allowed_work += batch_size;
+167            }
+168            limiter.finish_cycle(get_time);
+169
+170            assert_eq!(allowed_work, EXPECTED_ALLOWED_WORK_ITEMS);
+171        }
+172
+173        // After `SAMPLING_INTERVAL`, we get into measurement mode again
+174        for _ in 0..(SAMPLING_INTERVAL as usize - BATCH_SIZES.len() - 1) {
+175            limiter.start_cycle(get_time);
+176            limiter.record_work(1);
+177            limiter.finish_cycle(get_time);
+178        }
+179
+180        // We now do more work per cycle, and expect the estimate of allowed
+181        // work items to go up
+182        const BATCH_WORK_ITEMS_2: usize = 96;
+183        const TIME_PER_WORK_ITEMS_2_NANOS: f64 =
+184            CYCLE_TIME.as_nanos() as f64 / (EXPECTED_INITIAL_BATCHES * BATCH_WORK_ITEMS_2) as f64;
+185
+186        let expected_updated_time_per_work_item =
+187            (initial_time_per_work_item * 7.0 + TIME_PER_WORK_ITEMS_2_NANOS) / 8.0;
+188        let expected_updated_allowed_work_items =
+189            (CYCLE_TIME.as_nanos() as f64 / expected_updated_time_per_work_item) as usize;
+190
+191        limiter.start_cycle(get_time);
+192        let mut initial_batches = 0;
+193        while limiter.allow_work(get_time) {
+194            limiter.record_work(BATCH_WORK_ITEMS_2);
+195            advance_time(BATCH_TIME);
+196            initial_batches += 1;
+197        }
+198        limiter.finish_cycle(get_time);
+199
+200        assert_eq!(initial_batches, EXPECTED_INITIAL_BATCHES);
+201        assert_eq!(limiter.allowed, expected_updated_allowed_work_items);
+202    }
+203
+204    thread_local! {
+205        /// Mocked time
+206        pub static TIME: RefCell<Instant> = RefCell::new(Instant::now());
+207    }
+208
+209    fn reset_time() {
+210        TIME.with(|t| {
+211            *t.borrow_mut() = Instant::now();
+212        })
+213    }
+214
+215    fn get_time() -> Instant {
+216        TIME.with(|t| *t.borrow())
+217    }
+218
+219    fn advance_time(duration: Duration) {
+220        TIME.with(|t| {
+221            *t.borrow_mut() += duration;
+222        })
+223    }
+224}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/address_discovery.rs.html b/pr/234/docs/src/iroh_quinn_proto/address_discovery.rs.html new file mode 100644 index 000000000..d65b64801 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/address_discovery.rs.html @@ -0,0 +1,126 @@ +address_discovery.rs - source

iroh_quinn_proto/
address_discovery.rs

1//! Address discovery types from
+2//! <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+3
+4use crate::VarInt;
+5
+6/// The role of each participant.
+7///
+8/// When enabled, this is reported as a transport parameter.
+9#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
+10pub(crate) enum Role {
+11    /// Is able to report observer addresses to other peers, but it's not interested in receiving
+12    /// reports about its own address.
+13    SendOnly,
+14    /// Is interested on reports about its own observed address, but will not report back to other
+15    /// peers.
+16    ReceiveOnly,
+17    /// Will both report and receive reports of observed addresses.
+18    Both,
+19    /// Address discovery is disabled.
+20    #[default]
+21    Disabled,
+22}
+23
+24impl TryFrom<VarInt> for Role {
+25    type Error = crate::transport_parameters::Error;
+26
+27    fn try_from(value: VarInt) -> Result<Self, Self::Error> {
+28        match value.0 {
+29            0 => Ok(Self::SendOnly),
+30            1 => Ok(Self::ReceiveOnly),
+31            2 => Ok(Self::Both),
+32            _ => Err(crate::transport_parameters::Error::IllegalValue),
+33        }
+34    }
+35}
+36
+37impl Role {
+38    /// Whether address discovery is disabled.
+39    pub(crate) fn is_disabled(&self) -> bool {
+40        matches!(self, Self::Disabled)
+41    }
+42
+43    /// Whether this peer's role allows for address reporting to other peers.
+44    fn is_reporter(&self) -> bool {
+45        matches!(self, Self::SendOnly | Self::Both)
+46    }
+47
+48    /// Whether this peer's role accepts observed address reports.
+49    fn receives_reports(&self) -> bool {
+50        matches!(self, Self::ReceiveOnly | Self::Both)
+51    }
+52
+53    /// Whether this peer should report observed addresses to the other peer.
+54    pub(crate) fn should_report(&self, other: &Self) -> bool {
+55        self.is_reporter() && other.receives_reports()
+56    }
+57
+58    /// Sets whether this peer should provide observed addresses to other peers.
+59    pub(crate) fn send_reports_to_peers(&mut self, provide: bool) {
+60        if provide {
+61            self.enable_sending_reports_to_peers()
+62        } else {
+63            self.disable_sending_reports_to_peers()
+64        }
+65    }
+66
+67    /// Enables sending reports of observed addresses to other peers.
+68    fn enable_sending_reports_to_peers(&mut self) {
+69        match self {
+70            Self::SendOnly => {} // already enabled
+71            Self::ReceiveOnly => *self = Self::Both,
+72            Self::Both => {} // already enabled
+73            Self::Disabled => *self = Self::SendOnly,
+74        }
+75    }
+76
+77    /// Disables sending reports of observed addresses to other peers.
+78    fn disable_sending_reports_to_peers(&mut self) {
+79        match self {
+80            Self::SendOnly => *self = Self::Disabled,
+81            Self::ReceiveOnly => {} // already disabled
+82            Self::Both => *self = Self::ReceiveOnly,
+83            Self::Disabled => {} // already disabled
+84        }
+85    }
+86
+87    /// Sets whether this peer should accept received reports of observed addresses from other
+88    /// peers.
+89    pub(crate) fn receive_reports_from_peers(&mut self, receive: bool) {
+90        if receive {
+91            self.enable_receiving_reports_from_peers()
+92        } else {
+93            self.disable_receiving_reports_from_peers()
+94        }
+95    }
+96
+97    /// Enables receiving reports of observed addresses from other peers.
+98    fn enable_receiving_reports_from_peers(&mut self) {
+99        match self {
+100            Self::SendOnly => *self = Self::Both,
+101            Self::ReceiveOnly => {} // already enabled
+102            Self::Both => {}        // already enabled
+103            Self::Disabled => *self = Self::ReceiveOnly,
+104        }
+105    }
+106
+107    /// Disables receiving reports of observed addresses from other peers.
+108    fn disable_receiving_reports_from_peers(&mut self) {
+109        match self {
+110            Self::SendOnly => {} // already disabled
+111            Self::ReceiveOnly => *self = Self::Disabled,
+112            Self::Both => *self = Self::SendOnly,
+113            Self::Disabled => {} // already disabled
+114        }
+115    }
+116
+117    /// Gives the [`VarInt`] representing this [`Role`] as a transport parameter.
+118    pub(crate) fn as_transport_parameter(&self) -> Option<VarInt> {
+119        match self {
+120            Self::SendOnly => Some(VarInt(0)),
+121            Self::ReceiveOnly => Some(VarInt(1)),
+122            Self::Both => Some(VarInt(2)),
+123            Self::Disabled => None,
+124        }
+125    }
+126}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/bloom_token_log.rs.html b/pr/234/docs/src/iroh_quinn_proto/bloom_token_log.rs.html new file mode 100644 index 000000000..a2c6b8721 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/bloom_token_log.rs.html @@ -0,0 +1,368 @@ +bloom_token_log.rs - source

iroh_quinn_proto/
bloom_token_log.rs

1use std::{
+2    collections::HashSet,
+3    f64::consts::LN_2,
+4    hash::{BuildHasher, Hasher},
+5    mem::{size_of, take},
+6    sync::Mutex,
+7};
+8
+9use fastbloom::BloomFilter;
+10use rustc_hash::FxBuildHasher;
+11use tracing::{trace, warn};
+12
+13use crate::{Duration, SystemTime, TokenLog, TokenReuseError, UNIX_EPOCH};
+14
+15/// Bloom filter-based [`TokenLog`]
+16///
+17/// Parameterizable over an approximate maximum number of bytes to allocate. Starts out by storing
+18/// used tokens in a hash set. Once the hash set becomes too large, converts it to a bloom filter.
+19/// This achieves a memory profile of linear growth with an upper bound.
+20///
+21/// Divides time into periods based on `lifetime` and stores two filters at any given moment, for
+22/// each of the two periods currently non-expired tokens could expire in. As such, turns over
+23/// filters as time goes on to avoid bloom filter false positive rate increasing infinitely over
+24/// time.
+25pub struct BloomTokenLog(Mutex<State>);
+26
+27impl BloomTokenLog {
+28    /// Construct with an approximate maximum memory usage and expected number of validation token
+29    /// usages per expiration period
+30    ///
+31    /// Calculates the optimal bloom filter k number automatically.
+32    pub fn new_expected_items(max_bytes: usize, expected_hits: u64) -> Self {
+33        Self::new(max_bytes, optimal_k_num(max_bytes, expected_hits))
+34    }
+35
+36    /// Construct with an approximate maximum memory usage and a [bloom filter k number][bloom]
+37    ///
+38    /// [bloom]: https://en.wikipedia.org/wiki/Bloom_filter
+39    ///
+40    /// If choosing a custom k number, note that `BloomTokenLog` always maintains two filters
+41    /// between them and divides the allocation budget of `max_bytes` evenly between them. As such,
+42    /// each bloom filter will contain `max_bytes * 4` bits.
+43    pub fn new(max_bytes: usize, k_num: u32) -> Self {
+44        Self(Mutex::new(State {
+45            config: FilterConfig {
+46                filter_max_bytes: max_bytes / 2,
+47                k_num,
+48            },
+49            period_1_start: UNIX_EPOCH,
+50            filter_1: Filter::default(),
+51            filter_2: Filter::default(),
+52        }))
+53    }
+54}
+55
+56impl TokenLog for BloomTokenLog {
+57    fn check_and_insert(
+58        &self,
+59        nonce: u128,
+60        issued: SystemTime,
+61        lifetime: Duration,
+62    ) -> Result<(), TokenReuseError> {
+63        trace!(%nonce, "check_and_insert");
+64
+65        if lifetime.is_zero() {
+66            // avoid divide-by-zero if lifetime is zero
+67            return Err(TokenReuseError);
+68        }
+69
+70        let mut guard = self.0.lock().unwrap();
+71        let state = &mut *guard;
+72
+73        // calculate how many periods past period 1 the token expires
+74        let expires_at = issued + lifetime;
+75        let Ok(periods_forward) = expires_at
+76            .duration_since(state.period_1_start)
+77            .map(|duration| duration.as_nanos() / lifetime.as_nanos())
+78        else {
+79            // shouldn't happen unless time travels backwards or lifetime changes or the current
+80            // system time is before the Unix epoch
+81            warn!("BloomTokenLog presented with token too far in past");
+82            return Err(TokenReuseError);
+83        };
+84
+85        // get relevant filter
+86        let filter = match periods_forward {
+87            0 => &mut state.filter_1,
+88            1 => &mut state.filter_2,
+89            2 => {
+90                // turn over filter 1
+91                state.filter_1 = take(&mut state.filter_2);
+92                state.period_1_start += lifetime;
+93                &mut state.filter_2
+94            }
+95            _ => {
+96                // turn over both filters
+97                state.filter_1 = Filter::default();
+98                state.filter_2 = Filter::default();
+99                state.period_1_start = expires_at;
+100                &mut state.filter_1
+101            }
+102        };
+103
+104        // insert into the filter
+105        //
+106        // the token's nonce needs to guarantee uniqueness because of the role it plays in the
+107        // encryption of the tokens, so it is 128 bits. but since the token log can tolerate false
+108        // positives, we trim it down to 64 bits, which would still only have a small collision
+109        // rate even at significant amounts of usage, while allowing us to store twice as many in
+110        // the hash set variant.
+111        //
+112        // token nonce values are uniformly randomly generated server-side and cryptographically
+113        // integrity-checked, so we don't need to employ secure hashing to trim it down to 64 bits,
+114        // we can simply truncate.
+115        //
+116        // per the Rust reference, we can truncate by simply casting:
+117        // https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#numeric-cast
+118        filter.check_and_insert(nonce as u64, &state.config)
+119    }
+120}
+121
+122/// Default to 20 MiB max memory consumption and expected one million hits
+123///
+124/// With the default validation token lifetime of 2 weeks, this corresponds to one token usage per
+125/// 1.21 seconds.
+126impl Default for BloomTokenLog {
+127    fn default() -> Self {
+128        Self::new_expected_items(DEFAULT_MAX_BYTES, DEFAULT_EXPECTED_HITS)
+129    }
+130}
+131
+132/// Lockable state of [`BloomTokenLog`]
+133struct State {
+134    config: FilterConfig,
+135    // filter_1 covers tokens that expire in the period starting at period_1_start and extending
+136    // lifetime after. filter_2 covers tokens for the next lifetime after that.
+137    period_1_start: SystemTime,
+138    filter_1: Filter,
+139    filter_2: Filter,
+140}
+141
+142/// Unchanging parameters governing [`Filter`] behavior
+143struct FilterConfig {
+144    filter_max_bytes: usize,
+145    k_num: u32,
+146}
+147
+148/// Period filter within [`State`]
+149enum Filter {
+150    Set(HashSet<u64, IdentityBuildHasher>),
+151    Bloom(BloomFilter<FxBuildHasher>),
+152}
+153
+154impl Filter {
+155    fn check_and_insert(
+156        &mut self,
+157        fingerprint: u64,
+158        config: &FilterConfig,
+159    ) -> Result<(), TokenReuseError> {
+160        match self {
+161            Self::Set(hset) => {
+162                if !hset.insert(fingerprint) {
+163                    return Err(TokenReuseError);
+164                }
+165
+166                if hset.capacity() * size_of::<u64>() <= config.filter_max_bytes {
+167                    return Ok(());
+168                }
+169
+170                // convert to bloom
+171                // avoid panicking if user passed in filter_max_bytes of 0. we document that this
+172                // limit is approximate, so just fudge it up to 1.
+173                let mut bloom = BloomFilter::with_num_bits((config.filter_max_bytes * 8).max(1))
+174                    .hasher(FxBuildHasher)
+175                    .hashes(config.k_num);
+176                for item in &*hset {
+177                    bloom.insert(item);
+178                }
+179                *self = Self::Bloom(bloom);
+180            }
+181            Self::Bloom(bloom) => {
+182                if bloom.insert(&fingerprint) {
+183                    return Err(TokenReuseError);
+184                }
+185            }
+186        }
+187        Ok(())
+188    }
+189}
+190
+191impl Default for Filter {
+192    fn default() -> Self {
+193        Self::Set(HashSet::default())
+194    }
+195}
+196
+197/// `BuildHasher` of `IdentityHasher`
+198#[derive(Default)]
+199struct IdentityBuildHasher;
+200
+201impl BuildHasher for IdentityBuildHasher {
+202    type Hasher = IdentityHasher;
+203
+204    fn build_hasher(&self) -> Self::Hasher {
+205        IdentityHasher::default()
+206    }
+207}
+208
+209/// Hasher that is the identity operation--it assumes that exactly 8 bytes will be hashed, and the
+210/// resultant hash is those bytes as a `u64`
+211#[derive(Default)]
+212struct IdentityHasher {
+213    data: [u8; 8],
+214    #[cfg(debug_assertions)]
+215    wrote_8_byte_slice: bool,
+216}
+217
+218impl Hasher for IdentityHasher {
+219    fn write(&mut self, bytes: &[u8]) {
+220        #[cfg(debug_assertions)]
+221        {
+222            assert!(!self.wrote_8_byte_slice);
+223            assert_eq!(bytes.len(), 8);
+224            self.wrote_8_byte_slice = true;
+225        }
+226        self.data.copy_from_slice(bytes);
+227    }
+228
+229    fn finish(&self) -> u64 {
+230        #[cfg(debug_assertions)]
+231        assert!(self.wrote_8_byte_slice);
+232        u64::from_ne_bytes(self.data)
+233    }
+234}
+235
+236fn optimal_k_num(num_bytes: usize, expected_hits: u64) -> u32 {
+237    // be more forgiving rather than panickey here. excessively high num_bits may occur if the user
+238    // wishes it to be unbounded, so just saturate. expected_hits of 0 would cause divide-by-zero,
+239    // so just fudge it up to 1 in that case.
+240    let num_bits = (num_bytes as u64).saturating_mul(8);
+241    let expected_hits = expected_hits.max(1);
+242    // reference for this formula: https://programming.guide/bloom-filter-calculator.html
+243    // optimal k = (m ln 2) / n
+244    // wherein m is the number of bits, and n is the number of elements in the set.
+245    //
+246    // we also impose a minimum return value of 1, to avoid making the bloom filter entirely
+247    // useless in the case that the user provided an absurdly high ratio of hits / bytes.
+248    (((num_bits as f64 / expected_hits as f64) * LN_2).round() as u32).max(1)
+249}
+250
+251// remember to change the doc comment for `impl Default for BloomTokenLog` if these ever change
+252const DEFAULT_MAX_BYTES: usize = 10 << 20;
+253const DEFAULT_EXPECTED_HITS: u64 = 1_000_000;
+254
+255#[cfg(test)]
+256mod test {
+257    use super::*;
+258    use rand::prelude::*;
+259    use rand_pcg::Pcg32;
+260
+261    fn new_rng() -> impl Rng {
+262        Pcg32::from_seed(0xdeadbeefdeadbeefdeadbeefdeadbeef_u128.to_le_bytes())
+263    }
+264
+265    #[test]
+266    fn identity_hash_test() {
+267        let mut rng = new_rng();
+268        let builder = IdentityBuildHasher;
+269        for _ in 0..100 {
+270            let n = rng.random::<u64>();
+271            let hash = builder.hash_one(n);
+272            assert_eq!(hash, n);
+273        }
+274    }
+275
+276    #[test]
+277    fn optimal_k_num_test() {
+278        assert_eq!(optimal_k_num(10 << 20, 1_000_000), 58);
+279        assert_eq!(optimal_k_num(10 << 20, 1_000_000_000_000_000), 1);
+280        // assert that these don't panic:
+281        optimal_k_num(10 << 20, 0);
+282        optimal_k_num(usize::MAX, 1_000_000);
+283    }
+284
+285    #[test]
+286    fn bloom_token_log_conversion() {
+287        let mut rng = new_rng();
+288        let mut log = BloomTokenLog::new_expected_items(800, 200);
+289
+290        let issued = SystemTime::now();
+291        let lifetime = Duration::from_secs(1_000_000);
+292
+293        for i in 0..200 {
+294            let token = rng.random::<u128>();
+295            let result = log.check_and_insert(token, issued, lifetime);
+296            {
+297                let filter = &log.0.lock().unwrap().filter_1;
+298                if let Filter::Set(ref hset) = *filter {
+299                    assert!(hset.capacity() * size_of::<u64>() <= 800);
+300                    assert_eq!(hset.len(), i + 1);
+301                    assert!(result.is_ok());
+302                } else {
+303                    assert!(i > 10, "definitely bloomed too early");
+304                }
+305            }
+306            assert!(log.check_and_insert(token, issued, lifetime).is_err());
+307        }
+308
+309        assert!(
+310            matches!(log.0.get_mut().unwrap().filter_1, Filter::Bloom { .. }),
+311            "didn't bloom"
+312        );
+313    }
+314
+315    #[test]
+316    fn turn_over() {
+317        let mut rng = new_rng();
+318        let log = BloomTokenLog::new_expected_items(800, 200);
+319        let lifetime = Duration::from_secs(1_000);
+320        let mut old = Vec::default();
+321        let mut accepted = 0;
+322
+323        for i in 0..200 {
+324            let token = rng.random::<u128>();
+325            let now = UNIX_EPOCH + lifetime * 10 + lifetime * i / 10;
+326            let issued = now - lifetime.mul_f32(rng.random_range(0.0..3.0));
+327            let result = log.check_and_insert(token, issued, lifetime);
+328            if result.is_ok() {
+329                accepted += 1;
+330            }
+331            old.push((token, issued));
+332            let old_idx = rng.random_range(0..old.len());
+333            let (old_token, old_issued) = old[old_idx];
+334            assert!(
+335                log.check_and_insert(old_token, old_issued, lifetime)
+336                    .is_err()
+337            );
+338        }
+339        assert!(accepted > 0);
+340    }
+341
+342    fn test_doesnt_panic(log: BloomTokenLog) {
+343        let mut rng = new_rng();
+344
+345        let issued = SystemTime::now();
+346        let lifetime = Duration::from_secs(1_000_000);
+347
+348        for _ in 0..200 {
+349            let _ = log.check_and_insert(rng.random::<u128>(), issued, lifetime);
+350        }
+351    }
+352
+353    #[test]
+354    fn max_bytes_zero() {
+355        // "max bytes" is documented to be approximate. but make sure it doesn't panic.
+356        test_doesnt_panic(BloomTokenLog::new_expected_items(0, 200));
+357    }
+358
+359    #[test]
+360    fn expected_hits_zero() {
+361        test_doesnt_panic(BloomTokenLog::new_expected_items(100, 0));
+362    }
+363
+364    #[test]
+365    fn k_num_zero() {
+366        test_doesnt_panic(BloomTokenLog::new(100, 0));
+367    }
+368}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/cid_generator.rs.html b/pr/234/docs/src/iroh_quinn_proto/cid_generator.rs.html new file mode 100644 index 000000000..111ef0b80 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/cid_generator.rs.html @@ -0,0 +1,180 @@ +cid_generator.rs - source

iroh_quinn_proto/
cid_generator.rs

1use std::hash::Hasher;
+2
+3use rand::{Rng, RngCore};
+4
+5use crate::Duration;
+6use crate::MAX_CID_SIZE;
+7use crate::shared::ConnectionId;
+8
+9/// Generates connection IDs for incoming connections
+10pub trait ConnectionIdGenerator: Send + Sync {
+11    /// Generates a new CID
+12    ///
+13    /// Connection IDs MUST NOT contain any information that can be used by
+14    /// an external observer (that is, one that does not cooperate with the
+15    /// issuer) to correlate them with other connection IDs for the same
+16    /// connection. They MUST have high entropy, e.g. due to encrypted data
+17    /// or cryptographic-grade random data.
+18    fn generate_cid(&mut self) -> ConnectionId;
+19
+20    /// Quickly determine whether `cid` could have been generated by this generator
+21    ///
+22    /// False positives are permitted, but increase the cost of handling invalid packets.
+23    fn validate(&self, _cid: ConnectionId) -> Result<(), InvalidCid> {
+24        Ok(())
+25    }
+26
+27    /// Returns the length of a CID for connections created by this generator
+28    fn cid_len(&self) -> usize;
+29    /// Returns the lifetime of generated Connection IDs
+30    ///
+31    /// Connection IDs will be retired after the returned `Duration`, if any. Assumed to be constant.
+32    fn cid_lifetime(&self) -> Option<Duration>;
+33}
+34
+35/// The connection ID was not recognized by the [`ConnectionIdGenerator`]
+36#[derive(Debug, Copy, Clone)]
+37pub struct InvalidCid;
+38
+39/// Generates purely random connection IDs of a specified length
+40///
+41/// Random CIDs can be smaller than those produced by [`HashedConnectionIdGenerator`], but cannot be
+42/// usefully [`validate`](ConnectionIdGenerator::validate)d.
+43#[derive(Debug, Clone, Copy)]
+44pub struct RandomConnectionIdGenerator {
+45    cid_len: usize,
+46    lifetime: Option<Duration>,
+47}
+48
+49impl Default for RandomConnectionIdGenerator {
+50    fn default() -> Self {
+51        Self {
+52            cid_len: 8,
+53            lifetime: None,
+54        }
+55    }
+56}
+57
+58impl RandomConnectionIdGenerator {
+59    /// Initialize Random CID generator with a fixed CID length
+60    ///
+61    /// The given length must be less than or equal to MAX_CID_SIZE.
+62    pub fn new(cid_len: usize) -> Self {
+63        debug_assert!(cid_len <= MAX_CID_SIZE);
+64        Self {
+65            cid_len,
+66            ..Self::default()
+67        }
+68    }
+69
+70    /// Set the lifetime of CIDs created by this generator
+71    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+72        self.lifetime = Some(d);
+73        self
+74    }
+75}
+76
+77impl ConnectionIdGenerator for RandomConnectionIdGenerator {
+78    fn generate_cid(&mut self) -> ConnectionId {
+79        let mut bytes_arr = [0; MAX_CID_SIZE];
+80        rand::rng().fill_bytes(&mut bytes_arr[..self.cid_len]);
+81
+82        ConnectionId::new(&bytes_arr[..self.cid_len])
+83    }
+84
+85    /// Provide the length of dst_cid in short header packet
+86    fn cid_len(&self) -> usize {
+87        self.cid_len
+88    }
+89
+90    fn cid_lifetime(&self) -> Option<Duration> {
+91        self.lifetime
+92    }
+93}
+94
+95/// Generates 8-byte connection IDs that can be efficiently
+96/// [`validate`](ConnectionIdGenerator::validate)d
+97///
+98/// This generator uses a non-cryptographic hash and can therefore still be spoofed, but nonetheless
+99/// helps prevents Quinn from responding to non-QUIC packets at very low cost.
+100pub struct HashedConnectionIdGenerator {
+101    key: u64,
+102    lifetime: Option<Duration>,
+103}
+104
+105impl HashedConnectionIdGenerator {
+106    /// Create a generator with a random key
+107    pub fn new() -> Self {
+108        Self::from_key(rand::rng().random())
+109    }
+110
+111    /// Create a generator with a specific key
+112    ///
+113    /// Allows [`validate`](ConnectionIdGenerator::validate) to recognize a consistent set of
+114    /// connection IDs across restarts
+115    pub fn from_key(key: u64) -> Self {
+116        Self {
+117            key,
+118            lifetime: None,
+119        }
+120    }
+121
+122    /// Set the lifetime of CIDs created by this generator
+123    pub fn set_lifetime(&mut self, d: Duration) -> &mut Self {
+124        self.lifetime = Some(d);
+125        self
+126    }
+127}
+128
+129impl Default for HashedConnectionIdGenerator {
+130    fn default() -> Self {
+131        Self::new()
+132    }
+133}
+134
+135impl ConnectionIdGenerator for HashedConnectionIdGenerator {
+136    fn generate_cid(&mut self) -> ConnectionId {
+137        let mut bytes_arr = [0; NONCE_LEN + SIGNATURE_LEN];
+138        rand::rng().fill_bytes(&mut bytes_arr[..NONCE_LEN]);
+139        let mut hasher = rustc_hash::FxHasher::default();
+140        hasher.write_u64(self.key);
+141        hasher.write(&bytes_arr[..NONCE_LEN]);
+142        bytes_arr[NONCE_LEN..].copy_from_slice(&hasher.finish().to_le_bytes()[..SIGNATURE_LEN]);
+143        ConnectionId::new(&bytes_arr)
+144    }
+145
+146    fn validate(&self, cid: ConnectionId) -> Result<(), InvalidCid> {
+147        let (nonce, signature) = cid.split_at(NONCE_LEN);
+148        let mut hasher = rustc_hash::FxHasher::default();
+149        hasher.write_u64(self.key);
+150        hasher.write(nonce);
+151        let expected = hasher.finish().to_le_bytes();
+152        match expected[..SIGNATURE_LEN] == signature[..] {
+153            true => Ok(()),
+154            false => Err(InvalidCid),
+155        }
+156    }
+157
+158    fn cid_len(&self) -> usize {
+159        NONCE_LEN + SIGNATURE_LEN
+160    }
+161
+162    fn cid_lifetime(&self) -> Option<Duration> {
+163        self.lifetime
+164    }
+165}
+166
+167const NONCE_LEN: usize = 3; // Good for more than 16 million connections
+168const SIGNATURE_LEN: usize = 8 - NONCE_LEN; // 8-byte total CID length
+169
+170#[cfg(test)]
+171mod tests {
+172    use super::*;
+173
+174    #[test]
+175    fn validate_keyed_cid() {
+176        let mut generator = HashedConnectionIdGenerator::new();
+177        let cid = generator.generate_cid();
+178        generator.validate(cid).unwrap();
+179    }
+180}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/cid_queue.rs.html b/pr/234/docs/src/iroh_quinn_proto/cid_queue.rs.html new file mode 100644 index 000000000..3b769964d --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/cid_queue.rs.html @@ -0,0 +1,486 @@ +cid_queue.rs - source

iroh_quinn_proto/
cid_queue.rs

1use std::{fmt::Debug, ops::Range};
+2
+3use crate::{ConnectionId, ResetToken, frame::NewConnectionId};
+4
+5/// DataType stored in CidQueue buffer
+6#[derive(Debug, Clone, Copy)]
+7struct CidData(ConnectionId, Option<ResetToken>);
+8
+9/// Sliding window of active Connection IDs.
+10///
+11/// This represents a circular buffer that can contain gaps due to packet loss or reordering.
+12/// The buffer has three regions:
+13/// - Exactly one active CID at `self.buffer[self.cursor]`.
+14/// - Zero to `Self::LEN - 1` reserved CIDs from `self.cursor` up to `self.cursor_reserved`.
+15/// - More "available"/"ready" CIDs after `self.cursor_reserved`.
+16///
+17/// The range of reserved CIDs is grown by calling [`CidQueue::next_reserved`], which takes one of
+18/// the available ones and returns the CID that was reserved.
+19///
+20/// New available/ready CIDs are added by calling [`CidQueue::insert`].
+21///
+22/// May contain gaps due to packet loss or reordering.
+23#[derive(Debug)]
+24pub(crate) struct CidQueue {
+25    /// Ring buffer indexed by `self.cursor`
+26    buffer: [Option<CidData>; Self::LEN],
+27    /// Index at which circular buffer addressing is based
+28    cursor: usize,
+29    /// Sequence number of `self.buffer[cursor]`
+30    ///
+31    /// The sequence number of the active CID; must be the smallest among CIDs in `buffer`.
+32    offset: u64,
+33    /// Circular index for the last reserved CID, i.e. a CID that is not the active CID, but was
+34    /// used for probing packets on a different remote address.
+35    ///
+36    /// When [`Self::cursor_reserved`] and [`Self::cursor`] are equal, no CID is considered
+37    /// reserved.
+38    cursor_reserved: usize,
+39}
+40
+41impl CidQueue {
+42    pub(crate) fn new(cid: ConnectionId) -> Self {
+43        let mut buffer = [None; Self::LEN];
+44        buffer[0] = Some(CidData(cid, None));
+45        Self {
+46            buffer,
+47            cursor: 0,
+48            offset: 0,
+49            cursor_reserved: 0,
+50        }
+51    }
+52
+53    /// Handle a `NEW_CONNECTION_ID` frame
+54    ///
+55    /// Returns a non-empty range of retired sequence numbers and the reset token of the new active
+56    /// CID iff any CIDs were retired.
+57    pub(crate) fn insert(
+58        &mut self,
+59        cid: NewConnectionId,
+60    ) -> Result<Option<(Range<u64>, ResetToken)>, InsertError> {
+61        // Position of new CID wrt. the current active CID
+62        let Some(index) = cid.sequence.checked_sub(self.offset) else {
+63            return Err(InsertError::Retired);
+64        };
+65
+66        let retired_count = cid.retire_prior_to.saturating_sub(self.offset);
+67        if index >= Self::LEN as u64 + retired_count {
+68            return Err(InsertError::ExceedsLimit);
+69        }
+70
+71        // Discard retired CIDs, if any
+72        for i in 0..(retired_count.min(Self::LEN as u64) as usize) {
+73            self.buffer[(self.cursor + i) % Self::LEN] = None;
+74        }
+75
+76        // Record the new CID
+77        let index = ((self.cursor as u64 + index) % Self::LEN as u64) as usize;
+78        self.buffer[index] = Some(CidData(cid.id, Some(cid.reset_token)));
+79
+80        if retired_count == 0 {
+81            return Ok(None);
+82        }
+83
+84        // The active CID was retired. Find the first known CID with sequence number of at least
+85        // retire_prior_to, and inform the caller that all prior CIDs have been retired, and of
+86        // the new CID's reset token.
+87        self.cursor = ((self.cursor as u64 + retired_count) % Self::LEN as u64) as usize;
+88        let (i, CidData(_, token)) = self
+89            .iter_from_active()
+90            .next()
+91            .expect("it is impossible to retire a CID without supplying a new one");
+92        self.cursor = (self.cursor + i) % Self::LEN;
+93        self.cursor_reserved = self.cursor;
+94        let orig_offset = self.offset;
+95        self.offset = cid.retire_prior_to + i as u64;
+96        // We don't immediately retire CIDs in the range (orig_offset +
+97        // Self::LEN)..self.offset. These are CIDs that we haven't yet received from a
+98        // NEW_CONNECTION_ID frame, since having previously received them would violate the
+99        // connection ID limit we specified based on Self::LEN. If we do receive a such a frame
+100        // in the future, e.g. due to reordering, we'll retire it then. This ensures we can't be
+101        // made to buffer an arbitrarily large number of RETIRE_CONNECTION_ID frames.
+102        Ok(Some((
+103            orig_offset..self.offset.min(orig_offset + Self::LEN as u64),
+104            token.expect("non-initial CID missing reset token"),
+105        )))
+106    }
+107
+108    /// Switch to next active CID if possible, return
+109    /// 1) the corresponding ResetToken and 2) a non-empty range preceding it to retire
+110    pub(crate) fn next(&mut self) -> Option<(ResetToken, Range<u64>)> {
+111        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+112        let reserved = self.reserved_len();
+113        for j in 0..=reserved {
+114            self.buffer[self.cursor + j] = None;
+115        }
+116        let orig_offset = self.offset;
+117        self.offset += (i + reserved) as u64;
+118        self.cursor = (self.cursor_reserved + i) % Self::LEN;
+119        self.cursor_reserved = self.cursor;
+120
+121        Some((cid_data.1.unwrap(), orig_offset..self.offset))
+122    }
+123
+124    /// Returns a CID from the available ones and marks it as reserved.
+125    ///
+126    /// If there's no more CIDs in the ready set, this will return None.
+127    /// CIDs marked as reserved will be skipped when the active one advances.
+128    // TODO(@divma): remove
+129    #[cfg(test)]
+130    pub(crate) fn next_reserved(&mut self) -> Option<ConnectionId> {
+131        let (i, cid_data) = self.iter_from_reserved().nth(1)?;
+132
+133        self.cursor_reserved = (self.cursor_reserved + i) % Self::LEN;
+134        Some(cid_data.0)
+135    }
+136
+137    /// Iterate CIDs in CidQueue that are not `None`, including the active CID
+138    fn iter_from_active(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+139        (0..Self::LEN).filter_map(move |step| {
+140            let index = (self.cursor + step) % Self::LEN;
+141            self.buffer[index].map(|cid_data| (step, cid_data))
+142        })
+143    }
+144
+145    /// Iterate CIDs in CidQueue that are not `None`, including the active CID.
+146    ///
+147    /// Along with the CID, it returns the offset counted from [`Self::cursor_reserved`] where the CID is stored.
+148    fn iter_from_reserved(&self) -> impl Iterator<Item = (usize, CidData)> + '_ {
+149        (0..(Self::LEN - self.reserved_len())).filter_map(move |step| {
+150            let index = (self.cursor_reserved + step) % Self::LEN;
+151            self.buffer[index].map(|cid_data| (step, cid_data))
+152        })
+153    }
+154
+155    /// The length of the internal buffer's section of CIDs that are marked as reserved.
+156    fn reserved_len(&self) -> usize {
+157        if self.cursor_reserved >= self.cursor {
+158            self.cursor_reserved - self.cursor
+159        } else {
+160            self.cursor_reserved + Self::LEN - self.cursor
+161        }
+162    }
+163
+164    /// Replace the initial CID
+165    pub(crate) fn update_initial_cid(&mut self, cid: ConnectionId) {
+166        debug_assert_eq!(self.offset, 0);
+167        self.buffer[self.cursor] = Some(CidData(cid, None));
+168    }
+169
+170    /// Return active remote CID itself
+171    pub(crate) fn active(&self) -> ConnectionId {
+172        self.buffer[self.cursor].unwrap().0
+173    }
+174
+175    /// Return the sequence number of active remote CID
+176    pub(crate) fn active_seq(&self) -> u64 {
+177        self.offset
+178    }
+179
+180    pub(crate) const LEN: usize = 5;
+181}
+182
+183#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+184pub(crate) enum InsertError {
+185    /// CID was already retired
+186    Retired,
+187    /// Sequence number violates the leading edge of the window
+188    ExceedsLimit,
+189}
+190
+191#[cfg(test)]
+192mod tests {
+193    use super::*;
+194
+195    fn cid(sequence: u64, retire_prior_to: u64) -> NewConnectionId {
+196        NewConnectionId {
+197            path_id: None,
+198            sequence,
+199            id: ConnectionId::new(&sequence.to_be_bytes()),
+200            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+201            retire_prior_to,
+202        }
+203    }
+204
+205    fn initial_cid() -> ConnectionId {
+206        ConnectionId::new(&[0xFF; 8])
+207    }
+208
+209    #[test]
+210    fn next_dense() {
+211        let mut q = CidQueue::new(initial_cid());
+212        assert!(q.next().is_none());
+213        assert!(q.next().is_none());
+214
+215        for i in 1..CidQueue::LEN as u64 {
+216            q.insert(cid(i, 0)).unwrap();
+217        }
+218        for i in 1..CidQueue::LEN as u64 {
+219            let (_, retire) = q.next().unwrap();
+220            assert_eq!(q.active_seq(), i);
+221            assert_eq!(retire.end - retire.start, 1);
+222        }
+223        assert!(q.next().is_none());
+224    }
+225
+226    #[test]
+227    fn next_sparse() {
+228        let mut q = CidQueue::new(initial_cid());
+229        let seqs = (1..CidQueue::LEN as u64).filter(|x| x % 2 == 0);
+230        for i in seqs.clone() {
+231            q.insert(cid(i, 0)).unwrap();
+232        }
+233        for i in seqs {
+234            let (_, retire) = q.next().unwrap();
+235            dbg!(&retire);
+236            assert_eq!(q.active_seq(), i);
+237            assert_eq!(retire, (q.active_seq().saturating_sub(2))..q.active_seq());
+238        }
+239        assert!(q.next().is_none());
+240    }
+241
+242    #[test]
+243    fn wrap() {
+244        let mut q = CidQueue::new(initial_cid());
+245
+246        for i in 1..CidQueue::LEN as u64 {
+247            q.insert(cid(i, 0)).unwrap();
+248        }
+249        for _ in 1..(CidQueue::LEN as u64 - 1) {
+250            q.next().unwrap();
+251        }
+252        for i in CidQueue::LEN as u64..(CidQueue::LEN as u64 + 3) {
+253            q.insert(cid(i, 0)).unwrap();
+254        }
+255        for i in (CidQueue::LEN as u64 - 1)..(CidQueue::LEN as u64 + 3) {
+256            q.next().unwrap();
+257            assert_eq!(q.active_seq(), i);
+258        }
+259        assert!(q.next().is_none());
+260    }
+261
+262    #[test]
+263    fn retire_dense() {
+264        let mut q = CidQueue::new(initial_cid());
+265
+266        for i in 1..CidQueue::LEN as u64 {
+267            q.insert(cid(i, 0)).unwrap();
+268        }
+269        assert_eq!(q.active_seq(), 0);
+270
+271        assert_eq!(q.insert(cid(4, 2)).unwrap().unwrap().0, 0..2);
+272        assert_eq!(q.active_seq(), 2);
+273        assert_eq!(q.insert(cid(4, 2)), Ok(None));
+274
+275        for i in 2..(CidQueue::LEN as u64 - 1) {
+276            let _ = q.next().unwrap();
+277            assert_eq!(q.active_seq(), i + 1);
+278            assert_eq!(q.insert(cid(i + 1, i + 1)), Ok(None));
+279        }
+280
+281        assert!(q.next().is_none());
+282    }
+283
+284    #[test]
+285    fn retire_sparse() {
+286        // Retiring CID 0 when CID 1 is not known should retire CID 1 as we move to CID 2
+287        let mut q = CidQueue::new(initial_cid());
+288        q.insert(cid(2, 0)).unwrap();
+289        assert_eq!(q.insert(cid(3, 1)).unwrap().unwrap().0, 0..2,);
+290        assert_eq!(q.active_seq(), 2);
+291    }
+292
+293    #[test]
+294    fn retire_many() {
+295        let mut q = CidQueue::new(initial_cid());
+296        q.insert(cid(2, 0)).unwrap();
+297        assert_eq!(
+298            q.insert(cid(1_000_000, 1_000_000)).unwrap().unwrap().0,
+299            0..CidQueue::LEN as u64,
+300        );
+301        assert_eq!(q.active_seq(), 1_000_000);
+302    }
+303
+304    #[test]
+305    fn insert_limit() {
+306        let mut q = CidQueue::new(initial_cid());
+307        assert_eq!(q.insert(cid(CidQueue::LEN as u64 - 1, 0)), Ok(None));
+308        assert_eq!(
+309            q.insert(cid(CidQueue::LEN as u64, 0)),
+310            Err(InsertError::ExceedsLimit)
+311        );
+312    }
+313
+314    #[test]
+315    fn insert_duplicate() {
+316        let mut q = CidQueue::new(initial_cid());
+317        q.insert(cid(0, 0)).unwrap();
+318        q.insert(cid(0, 0)).unwrap();
+319    }
+320
+321    #[test]
+322    fn insert_retired() {
+323        let mut q = CidQueue::new(initial_cid());
+324        assert_eq!(
+325            q.insert(cid(0, 0)),
+326            Ok(None),
+327            "reinserting active CID succeeds"
+328        );
+329        assert!(q.next().is_none(), "active CID isn't requeued");
+330        q.insert(cid(1, 0)).unwrap();
+331        q.next().unwrap();
+332        assert_eq!(
+333            q.insert(cid(0, 0)),
+334            Err(InsertError::Retired),
+335            "previous active CID is already retired"
+336        );
+337    }
+338
+339    #[test]
+340    fn retire_then_insert_next() {
+341        let mut q = CidQueue::new(initial_cid());
+342        for i in 1..CidQueue::LEN as u64 {
+343            q.insert(cid(i, 0)).unwrap();
+344        }
+345        q.next().unwrap();
+346        q.insert(cid(CidQueue::LEN as u64, 0)).unwrap();
+347        assert_eq!(
+348            q.insert(cid(CidQueue::LEN as u64 + 1, 0)),
+349            Err(InsertError::ExceedsLimit)
+350        );
+351    }
+352
+353    #[test]
+354    fn always_valid() {
+355        let mut q = CidQueue::new(initial_cid());
+356        assert!(q.next().is_none());
+357        assert_eq!(q.active(), initial_cid());
+358        assert_eq!(q.active_seq(), 0);
+359    }
+360
+361    #[test]
+362    fn reserved_smoke() {
+363        let mut q = CidQueue::new(initial_cid());
+364        assert_eq!(q.next_reserved(), None);
+365
+366        let one = cid(1, 0);
+367        q.insert(one).unwrap();
+368        assert_eq!(q.next_reserved(), Some(one.id));
+369
+370        let two = cid(2, 2);
+371        let (retired_range, reset_token) = q.insert(two).unwrap().unwrap();
+372        assert_eq!(reset_token, two.reset_token);
+373        assert_eq!(retired_range, 0..2);
+374
+375        assert_eq!(q.next_reserved(), None);
+376
+377        let four = cid(4, 2);
+378        q.insert(four).unwrap();
+379        println!("{q:?}");
+380        assert_eq!(q.next_reserved(), Some(four.id));
+381        assert_eq!(q.active(), two.id);
+382
+383        assert_eq!(q.next(), None);
+384    }
+385
+386    #[test]
+387    fn reserve_multiple() {
+388        let mut q = CidQueue::new(initial_cid());
+389        let one = cid(1, 0);
+390        let two = cid(2, 0);
+391        q.insert(one).unwrap();
+392        q.insert(two).unwrap();
+393        assert_eq!(q.next_reserved(), Some(one.id));
+394        assert_eq!(q.next_reserved(), Some(two.id));
+395        assert_eq!(q.next_reserved(), None);
+396    }
+397
+398    #[test]
+399    fn reserve_multiple_sparse() {
+400        let mut q = CidQueue::new(initial_cid());
+401        let two = cid(2, 0);
+402        let four = cid(4, 0);
+403        q.insert(two).unwrap();
+404        q.insert(four).unwrap();
+405        assert_eq!(q.next_reserved(), Some(two.id));
+406        assert_eq!(q.next_reserved(), Some(four.id));
+407        assert_eq!(q.next_reserved(), None);
+408    }
+409
+410    #[test]
+411    fn reserve_many_next_clears() {
+412        let mut q = CidQueue::new(initial_cid());
+413        for i in 1..CidQueue::LEN {
+414            q.insert(cid(i as u64, 0)).unwrap();
+415        }
+416
+417        for _ in 0..CidQueue::LEN - 2 {
+418            assert!(q.next_reserved().is_some());
+419        }
+420
+421        assert!(q.next().is_some());
+422        assert_eq!(q.next(), None);
+423    }
+424
+425    #[test]
+426    fn reserve_many_next_reserved_none() {
+427        let mut q = CidQueue::new(initial_cid());
+428        for i in 1..CidQueue::LEN {
+429            q.insert(cid(i as u64, 0)).unwrap();
+430        }
+431
+432        for _ in 0..CidQueue::LEN - 1 {
+433            assert!(q.next_reserved().is_some());
+434        }
+435
+436        assert_eq!(q.next_reserved(), None);
+437    }
+438
+439    #[test]
+440    fn one_active_all_else_reserved_next_none() {
+441        let mut q = CidQueue::new(initial_cid());
+442        for i in 1..CidQueue::LEN {
+443            q.insert(cid(i as u64, 0)).unwrap();
+444        }
+445
+446        for _ in 0..CidQueue::LEN - 1 {
+447            assert!(q.next_reserved().is_some());
+448        }
+449
+450        assert_eq!(q.next(), None);
+451    }
+452
+453    #[test]
+454    fn insert_reserve_advance() {
+455        let mut q = CidQueue::new(initial_cid());
+456
+457        let first = cid(1, 0);
+458        let second = cid(2, 0);
+459        let third = cid(3, 0);
+460
+461        q.insert(first).unwrap();
+462        q.insert(second).unwrap();
+463
+464        assert_eq!(q.next_reserved(), Some(first.id));
+465        q.insert(third).unwrap();
+466        q.next();
+467        assert_eq!(q.active(), second.id);
+468    }
+469
+470    #[test]
+471    fn sparse_insert_reserve_insert_advance() {
+472        let mut q = CidQueue::new(initial_cid());
+473
+474        let one = cid(1, 0);
+475        let two = cid(2, 0);
+476        let three = cid(3, 0);
+477
+478        q.insert(two).unwrap();
+479        q.insert(three).unwrap();
+480        assert_eq!(q.next_reserved(), Some(two.id));
+481        q.insert(one).unwrap();
+482        q.next();
+483        assert_eq!(q.active(), three.id);
+484        assert_eq!(q.next_reserved(), None);
+485    }
+486}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/coding.rs.html b/pr/234/docs/src/iroh_quinn_proto/coding.rs.html new file mode 100644 index 000000000..c25eab370 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/coding.rs.html @@ -0,0 +1,130 @@ +coding.rs - source

iroh_quinn_proto/
coding.rs

1//! Coding related traits.
+2
+3use std::net::{Ipv4Addr, Ipv6Addr};
+4
+5use bytes::{Buf, BufMut};
+6use thiserror::Error;
+7
+8use crate::VarInt;
+9
+10/// Error indicating that the provided buffer was too small
+11#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
+12#[error("unexpected end of buffer")]
+13pub struct UnexpectedEnd;
+14
+15/// Coding result type
+16pub type Result<T> = ::std::result::Result<T, UnexpectedEnd>;
+17
+18/// Infallible encoding and decoding of QUIC primitives
+19pub trait Codec: Sized {
+20    /// Decode a `Self` from the provided buffer, if the buffer is large enough
+21    fn decode<B: Buf>(buf: &mut B) -> Result<Self>;
+22    /// Append the encoding of `self` to the provided buffer
+23    fn encode<B: BufMut>(&self, buf: &mut B);
+24}
+25
+26impl Codec for u8 {
+27    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+28        if buf.remaining() < 1 {
+29            return Err(UnexpectedEnd);
+30        }
+31        Ok(buf.get_u8())
+32    }
+33    fn encode<B: BufMut>(&self, buf: &mut B) {
+34        buf.put_u8(*self);
+35    }
+36}
+37
+38impl Codec for u16 {
+39    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+40        if buf.remaining() < 2 {
+41            return Err(UnexpectedEnd);
+42        }
+43        Ok(buf.get_u16())
+44    }
+45    fn encode<B: BufMut>(&self, buf: &mut B) {
+46        buf.put_u16(*self);
+47    }
+48}
+49
+50impl Codec for u32 {
+51    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+52        if buf.remaining() < 4 {
+53            return Err(UnexpectedEnd);
+54        }
+55        Ok(buf.get_u32())
+56    }
+57    fn encode<B: BufMut>(&self, buf: &mut B) {
+58        buf.put_u32(*self);
+59    }
+60}
+61
+62impl Codec for u64 {
+63    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+64        if buf.remaining() < 8 {
+65            return Err(UnexpectedEnd);
+66        }
+67        Ok(buf.get_u64())
+68    }
+69    fn encode<B: BufMut>(&self, buf: &mut B) {
+70        buf.put_u64(*self);
+71    }
+72}
+73
+74impl Codec for Ipv4Addr {
+75    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+76        if buf.remaining() < 4 {
+77            return Err(UnexpectedEnd);
+78        }
+79        let mut octets = [0; 4];
+80        buf.copy_to_slice(&mut octets);
+81        Ok(octets.into())
+82    }
+83    fn encode<B: BufMut>(&self, buf: &mut B) {
+84        buf.put_slice(&self.octets());
+85    }
+86}
+87
+88impl Codec for Ipv6Addr {
+89    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
+90        if buf.remaining() < 16 {
+91            return Err(UnexpectedEnd);
+92        }
+93        let mut octets = [0; 16];
+94        buf.copy_to_slice(&mut octets);
+95        Ok(octets.into())
+96    }
+97    fn encode<B: BufMut>(&self, buf: &mut B) {
+98        buf.put_slice(&self.octets());
+99    }
+100}
+101
+102pub(crate) trait BufExt {
+103    fn get<T: Codec>(&mut self) -> Result<T>;
+104    fn get_var(&mut self) -> Result<u64>;
+105}
+106
+107impl<T: Buf> BufExt for T {
+108    fn get<U: Codec>(&mut self) -> Result<U> {
+109        U::decode(self)
+110    }
+111
+112    fn get_var(&mut self) -> Result<u64> {
+113        Ok(VarInt::decode(self)?.into_inner())
+114    }
+115}
+116
+117pub(crate) trait BufMutExt {
+118    fn write<T: Codec>(&mut self, x: T);
+119    fn write_var(&mut self, x: u64);
+120}
+121
+122impl<T: BufMut> BufMutExt for T {
+123    fn write<U: Codec>(&mut self, x: U) {
+124        x.encode(self);
+125    }
+126
+127    fn write_var(&mut self, x: u64) {
+128        VarInt::from_u64(x).unwrap().encode(self);
+129    }
+130}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/config/mod.rs.html b/pr/234/docs/src/iroh_quinn_proto/config/mod.rs.html new file mode 100644 index 000000000..f0a2971c4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/config/mod.rs.html @@ -0,0 +1,703 @@ +mod.rs - source

iroh_quinn_proto/config/
mod.rs

1use std::{
+2    fmt,
+3    net::{SocketAddrV4, SocketAddrV6},
+4    num::TryFromIntError,
+5    sync::Arc,
+6};
+7
+8#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+9use rustls::client::WebPkiServerVerifier;
+10#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+11use rustls::pki_types::{CertificateDer, PrivateKeyDer};
+12use thiserror::Error;
+13
+14#[cfg(feature = "bloom")]
+15use crate::BloomTokenLog;
+16#[cfg(not(feature = "bloom"))]
+17use crate::NoneTokenLog;
+18#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+19use crate::crypto::rustls::{QuicServerConfig, configured_provider};
+20use crate::{
+21    DEFAULT_SUPPORTED_VERSIONS, Duration, MAX_CID_SIZE, RandomConnectionIdGenerator, SystemTime,
+22    TokenLog, TokenMemoryCache, TokenStore, VarInt, VarIntBoundsExceeded,
+23    cid_generator::{ConnectionIdGenerator, HashedConnectionIdGenerator},
+24    crypto::{self, HandshakeTokenKey, HmacKey},
+25    shared::ConnectionId,
+26};
+27
+28#[cfg(feature = "qlog")]
+29mod qlog;
+30mod transport;
+31
+32#[cfg(feature = "qlog")]
+33pub use qlog::{QlogConfig, QlogFactory, QlogFileFactory};
+34pub use transport::{AckFrequencyConfig, IdleTimeout, MtuDiscoveryConfig, TransportConfig};
+35
+36#[cfg(doc)]
+37pub use transport::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+38
+39/// Global configuration for the endpoint, affecting all connections
+40///
+41/// Default values should be suitable for most internet applications.
+42#[derive(Clone)]
+43pub struct EndpointConfig {
+44    pub(crate) reset_key: Arc<dyn HmacKey>,
+45    pub(crate) max_udp_payload_size: VarInt,
+46    /// CID generator factory
+47    ///
+48    /// Create a cid generator for local cid in Endpoint struct
+49    pub(crate) connection_id_generator_factory:
+50        Arc<dyn Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync>,
+51    pub(crate) supported_versions: Vec<u32>,
+52    pub(crate) grease_quic_bit: bool,
+53    /// Minimum interval between outgoing stateless reset packets
+54    pub(crate) min_reset_interval: Duration,
+55    /// Optional seed to be used internally for random number generation
+56    pub(crate) rng_seed: Option<[u8; 32]>,
+57}
+58
+59impl EndpointConfig {
+60    /// Create a default config with a particular `reset_key`
+61    pub fn new(reset_key: Arc<dyn HmacKey>) -> Self {
+62        let cid_factory =
+63            || -> Box<dyn ConnectionIdGenerator> { Box::<HashedConnectionIdGenerator>::default() };
+64        Self {
+65            reset_key,
+66            max_udp_payload_size: (1500u32 - 28).into(), // Ethernet MTU minus IP + UDP headers
+67            connection_id_generator_factory: Arc::new(cid_factory),
+68            supported_versions: DEFAULT_SUPPORTED_VERSIONS.to_vec(),
+69            grease_quic_bit: true,
+70            min_reset_interval: Duration::from_millis(20),
+71            rng_seed: None,
+72        }
+73    }
+74
+75    /// Supply a custom connection ID generator factory
+76    ///
+77    /// Called once by each `Endpoint` constructed from this configuration to obtain the CID
+78    /// generator which will be used to generate the CIDs used for incoming packets on all
+79    /// connections involving that  `Endpoint`. A custom CID generator allows applications to embed
+80    /// information in local connection IDs, e.g. to support stateless packet-level load balancers.
+81    ///
+82    /// Defaults to [`HashedConnectionIdGenerator`].
+83    pub fn cid_generator<F: Fn() -> Box<dyn ConnectionIdGenerator> + Send + Sync + 'static>(
+84        &mut self,
+85        factory: F,
+86    ) -> &mut Self {
+87        self.connection_id_generator_factory = Arc::new(factory);
+88        self
+89    }
+90
+91    /// Private key used to send authenticated connection resets to peers who were
+92    /// communicating with a previous instance of this endpoint.
+93    pub fn reset_key(&mut self, key: Arc<dyn HmacKey>) -> &mut Self {
+94        self.reset_key = key;
+95        self
+96    }
+97
+98    /// Maximum UDP payload size accepted from peers (excluding UDP and IP overhead).
+99    ///
+100    /// Must be greater or equal than 1200.
+101    ///
+102    /// Defaults to 1472, which is the largest UDP payload that can be transmitted in the typical
+103    /// 1500 byte Ethernet MTU. Deployments on links with larger MTUs (e.g. loopback or Ethernet
+104    /// with jumbo frames) can raise this to improve performance at the cost of a linear increase in
+105    /// datagram receive buffer size.
+106    pub fn max_udp_payload_size(&mut self, value: u16) -> Result<&mut Self, ConfigError> {
+107        if !(1200..=65_527).contains(&value) {
+108            return Err(ConfigError::OutOfBounds);
+109        }
+110
+111        self.max_udp_payload_size = value.into();
+112        Ok(self)
+113    }
+114
+115    /// Get the current value of [`max_udp_payload_size`](Self::max_udp_payload_size)
+116    //
+117    // While most parameters don't need to be readable, this must be exposed to allow higher-level
+118    // layers, e.g. the `quinn` crate, to determine how large a receive buffer to allocate to
+119    // support an externally-defined `EndpointConfig`.
+120    //
+121    // While `get_` accessors are typically unidiomatic in Rust, we favor concision for setters,
+122    // which will be used far more heavily.
+123    pub fn get_max_udp_payload_size(&self) -> u64 {
+124        self.max_udp_payload_size.into()
+125    }
+126
+127    /// Override supported QUIC versions
+128    pub fn supported_versions(&mut self, supported_versions: Vec<u32>) -> &mut Self {
+129        self.supported_versions = supported_versions;
+130        self
+131    }
+132
+133    /// Whether to accept QUIC packets containing any value for the fixed bit
+134    ///
+135    /// Enabled by default. Helps protect against protocol ossification and makes traffic less
+136    /// identifiable to observers. Disable if helping observers identify this traffic as QUIC is
+137    /// desired.
+138    pub fn grease_quic_bit(&mut self, value: bool) -> &mut Self {
+139        self.grease_quic_bit = value;
+140        self
+141    }
+142
+143    /// Minimum interval between outgoing stateless reset packets
+144    ///
+145    /// Defaults to 20ms. Limits the impact of attacks which flood an endpoint with garbage packets,
+146    /// e.g. [ISAKMP/IKE amplification]. Larger values provide a stronger defense, but may delay
+147    /// detection of some error conditions by clients. Using a [`ConnectionIdGenerator`] with a low
+148    /// rate of false positives in [`validate`](ConnectionIdGenerator::validate) reduces the risk
+149    /// incurred by a small minimum reset interval.
+150    ///
+151    /// [ISAKMP/IKE
+152    /// amplification]: https://bughunters.google.com/blog/5960150648750080/preventing-cross-service-udp-loops-in-quic#isakmp-ike-amplification-vs-quic
+153    pub fn min_reset_interval(&mut self, value: Duration) -> &mut Self {
+154        self.min_reset_interval = value;
+155        self
+156    }
+157
+158    /// Optional seed to be used internally for random number generation
+159    ///
+160    /// By default, quinn will initialize an endpoint's rng using a platform entropy source.
+161    /// However, you can seed the rng yourself through this method (e.g. if you need to run quinn
+162    /// deterministically or if you are using quinn in an environment that doesn't have a source of
+163    /// entropy available).
+164    pub fn rng_seed(&mut self, seed: Option<[u8; 32]>) -> &mut Self {
+165        self.rng_seed = seed;
+166        self
+167    }
+168}
+169
+170impl fmt::Debug for EndpointConfig {
+171    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+172        fmt.debug_struct("EndpointConfig")
+173            // reset_key not debug
+174            .field("max_udp_payload_size", &self.max_udp_payload_size)
+175            // cid_generator_factory not debug
+176            .field("supported_versions", &self.supported_versions)
+177            .field("grease_quic_bit", &self.grease_quic_bit)
+178            .field("rng_seed", &self.rng_seed)
+179            .finish_non_exhaustive()
+180    }
+181}
+182
+183#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+184impl Default for EndpointConfig {
+185    fn default() -> Self {
+186        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+187        use aws_lc_rs::hmac;
+188        use rand::RngCore;
+189        #[cfg(feature = "ring")]
+190        use ring::hmac;
+191
+192        let mut reset_key = [0; 64];
+193        rand::rng().fill_bytes(&mut reset_key);
+194
+195        Self::new(Arc::new(hmac::Key::new(hmac::HMAC_SHA256, &reset_key)))
+196    }
+197}
+198
+199/// Parameters governing incoming connections
+200///
+201/// Default values should be suitable for most internet applications.
+202#[derive(Clone)]
+203pub struct ServerConfig {
+204    /// Transport configuration to use for incoming connections
+205    pub transport: Arc<TransportConfig>,
+206
+207    /// TLS configuration used for incoming connections
+208    ///
+209    /// Must be set to use TLS 1.3 only.
+210    pub crypto: Arc<dyn crypto::ServerConfig>,
+211
+212    /// Configuration for sending and handling validation tokens
+213    pub validation_token: ValidationTokenConfig,
+214
+215    /// Used to generate one-time AEAD keys to protect handshake tokens
+216    pub(crate) token_key: Arc<dyn HandshakeTokenKey>,
+217
+218    /// Duration after a retry token was issued for which it's considered valid
+219    pub(crate) retry_token_lifetime: Duration,
+220
+221    /// Whether to allow clients to migrate to new addresses
+222    ///
+223    /// Improves behavior for clients that move between different internet connections or suffer NAT
+224    /// rebinding. Enabled by default.
+225    pub(crate) migration: bool,
+226
+227    pub(crate) preferred_address_v4: Option<SocketAddrV4>,
+228    pub(crate) preferred_address_v6: Option<SocketAddrV6>,
+229
+230    pub(crate) max_incoming: usize,
+231    pub(crate) incoming_buffer_size: u64,
+232    pub(crate) incoming_buffer_size_total: u64,
+233
+234    pub(crate) time_source: Arc<dyn TimeSource>,
+235}
+236
+237impl ServerConfig {
+238    /// Create a default config with a particular handshake token key
+239    pub fn new(
+240        crypto: Arc<dyn crypto::ServerConfig>,
+241        token_key: Arc<dyn HandshakeTokenKey>,
+242    ) -> Self {
+243        Self {
+244            transport: Arc::new(TransportConfig::default()),
+245            crypto,
+246
+247            token_key,
+248            retry_token_lifetime: Duration::from_secs(15),
+249
+250            migration: true,
+251
+252            validation_token: ValidationTokenConfig::default(),
+253
+254            preferred_address_v4: None,
+255            preferred_address_v6: None,
+256
+257            max_incoming: 1 << 16,
+258            incoming_buffer_size: 10 << 20,
+259            incoming_buffer_size_total: 100 << 20,
+260
+261            time_source: Arc::new(StdSystemTime),
+262        }
+263    }
+264
+265    /// Set a custom [`TransportConfig`]
+266    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+267        self.transport = transport;
+268        self
+269    }
+270
+271    /// Set a custom [`ValidationTokenConfig`]
+272    pub fn validation_token_config(
+273        &mut self,
+274        validation_token: ValidationTokenConfig,
+275    ) -> &mut Self {
+276        self.validation_token = validation_token;
+277        self
+278    }
+279
+280    /// Private key used to authenticate data included in handshake tokens
+281    pub fn token_key(&mut self, value: Arc<dyn HandshakeTokenKey>) -> &mut Self {
+282        self.token_key = value;
+283        self
+284    }
+285
+286    /// Duration after a retry token was issued for which it's considered valid
+287    ///
+288    /// Defaults to 15 seconds.
+289    pub fn retry_token_lifetime(&mut self, value: Duration) -> &mut Self {
+290        self.retry_token_lifetime = value;
+291        self
+292    }
+293
+294    /// Whether to allow clients to migrate to new addresses
+295    ///
+296    /// Improves behavior for clients that move between different internet connections or suffer NAT
+297    /// rebinding. Enabled by default.
+298    pub fn migration(&mut self, value: bool) -> &mut Self {
+299        self.migration = value;
+300        self
+301    }
+302
+303    /// The preferred IPv4 address that will be communicated to clients during handshaking
+304    ///
+305    /// If the client is able to reach this address, it will switch to it.
+306    pub fn preferred_address_v4(&mut self, address: Option<SocketAddrV4>) -> &mut Self {
+307        self.preferred_address_v4 = address;
+308        self
+309    }
+310
+311    /// The preferred IPv6 address that will be communicated to clients during handshaking
+312    ///
+313    /// If the client is able to reach this address, it will switch to it.
+314    pub fn preferred_address_v6(&mut self, address: Option<SocketAddrV6>) -> &mut Self {
+315        self.preferred_address_v6 = address;
+316        self
+317    }
+318
+319    /// Maximum number of [`Incoming`][crate::Incoming] to allow to exist at a time
+320    ///
+321    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+322    /// is received and stops existing when the application either accepts it or otherwise disposes
+323    /// of it. While this limit is reached, new incoming connection attempts are immediately
+324    /// refused. Larger values have greater worst-case memory consumption, but accommodate greater
+325    /// application latency in handling incoming connection attempts.
+326    ///
+327    /// The default value is set to 65536. With a typical Ethernet MTU of 1500 bytes, this limits
+328    /// memory consumption from this to under 100 MiB--a generous amount that still prevents memory
+329    /// exhaustion in most contexts.
+330    pub fn max_incoming(&mut self, max_incoming: usize) -> &mut Self {
+331        self.max_incoming = max_incoming;
+332        self
+333    }
+334
+335    /// Maximum number of received bytes to buffer for each [`Incoming`][crate::Incoming]
+336    ///
+337    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+338    /// is received and stops existing when the application either accepts it or otherwise disposes
+339    /// of it. This limit governs only packets received within that period, and does not include
+340    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+341    /// 0-RTT or handshake data to have to be retransmitted.
+342    ///
+343    /// The default value is set to 10 MiB--an amount such that in most situations a client would
+344    /// not transmit that much 0-RTT data faster than the server handles the corresponding
+345    /// [`Incoming`][crate::Incoming].
+346    pub fn incoming_buffer_size(&mut self, incoming_buffer_size: u64) -> &mut Self {
+347        self.incoming_buffer_size = incoming_buffer_size;
+348        self
+349    }
+350
+351    /// Maximum number of received bytes to buffer for all [`Incoming`][crate::Incoming]
+352    /// collectively
+353    ///
+354    /// An [`Incoming`][crate::Incoming] comes into existence when an incoming connection attempt
+355    /// is received and stops existing when the application either accepts it or otherwise disposes
+356    /// of it. This limit governs only packets received within that period, and does not include
+357    /// the first packet. Packets received in excess of this limit are dropped, which may cause
+358    /// 0-RTT or handshake data to have to be retransmitted.
+359    ///
+360    /// The default value is set to 100 MiB--a generous amount that still prevents memory
+361    /// exhaustion in most contexts.
+362    pub fn incoming_buffer_size_total(&mut self, incoming_buffer_size_total: u64) -> &mut Self {
+363        self.incoming_buffer_size_total = incoming_buffer_size_total;
+364        self
+365    }
+366
+367    /// Object to get current [`SystemTime`]
+368    ///
+369    /// This exists to allow system time to be mocked in tests, or wherever else desired.
+370    ///
+371    /// Defaults to [`StdSystemTime`], which simply calls [`SystemTime::now()`](SystemTime::now).
+372    pub fn time_source(&mut self, time_source: Arc<dyn TimeSource>) -> &mut Self {
+373        self.time_source = time_source;
+374        self
+375    }
+376
+377    pub(crate) fn has_preferred_address(&self) -> bool {
+378        self.preferred_address_v4.is_some() || self.preferred_address_v6.is_some()
+379    }
+380}
+381
+382#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+383impl ServerConfig {
+384    /// Create a server config with the given certificate chain to be presented to clients
+385    ///
+386    /// Uses a randomized handshake token key.
+387    pub fn with_single_cert(
+388        cert_chain: Vec<CertificateDer<'static>>,
+389        key: PrivateKeyDer<'static>,
+390    ) -> Result<Self, rustls::Error> {
+391        Ok(Self::with_crypto(Arc::new(QuicServerConfig::new(
+392            cert_chain, key,
+393        )?)))
+394    }
+395}
+396
+397#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+398impl ServerConfig {
+399    /// Create a server config with the given [`crypto::ServerConfig`]
+400    ///
+401    /// Uses a randomized handshake token key.
+402    pub fn with_crypto(crypto: Arc<dyn crypto::ServerConfig>) -> Self {
+403        #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+404        use aws_lc_rs::hkdf;
+405        use rand::RngCore;
+406        #[cfg(feature = "ring")]
+407        use ring::hkdf;
+408
+409        let rng = &mut rand::rng();
+410        let mut master_key = [0u8; 64];
+411        rng.fill_bytes(&mut master_key);
+412        let master_key = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+413
+414        Self::new(crypto, Arc::new(master_key))
+415    }
+416}
+417
+418impl fmt::Debug for ServerConfig {
+419    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+420        fmt.debug_struct("ServerConfig")
+421            .field("transport", &self.transport)
+422            // crypto not debug
+423            // token not debug
+424            .field("retry_token_lifetime", &self.retry_token_lifetime)
+425            .field("validation_token", &self.validation_token)
+426            .field("migration", &self.migration)
+427            .field("preferred_address_v4", &self.preferred_address_v4)
+428            .field("preferred_address_v6", &self.preferred_address_v6)
+429            .field("max_incoming", &self.max_incoming)
+430            .field("incoming_buffer_size", &self.incoming_buffer_size)
+431            .field(
+432                "incoming_buffer_size_total",
+433                &self.incoming_buffer_size_total,
+434            )
+435            // system_time_clock not debug
+436            .finish_non_exhaustive()
+437    }
+438}
+439
+440/// Configuration for sending and handling validation tokens in incoming connections
+441///
+442/// Default values should be suitable for most internet applications.
+443///
+444/// ## QUIC Tokens
+445///
+446/// The QUIC protocol defines a concept of "[address validation][1]". Essentially, one side of a
+447/// QUIC connection may appear to be receiving QUIC packets from a particular remote UDP address,
+448/// but it will only consider that remote address "validated" once it has convincing evidence that
+449/// the address is not being [spoofed][2].
+450///
+451/// Validation is important primarily because of QUIC's "anti-amplification limit." This limit
+452/// prevents a QUIC server from sending a client more than three times the number of bytes it has
+453/// received from the client on a given address until that address is validated. This is designed
+454/// to mitigate the ability of attackers to use QUIC-based servers as reflectors in [amplification
+455/// attacks][3].
+456///
+457/// A path may become validated in several ways. The server is always considered validated by the
+458/// client. The client usually begins in an unvalidated state upon first connecting or migrating,
+459/// but then becomes validated through various mechanisms that usually take one network round trip.
+460/// However, in some cases, a client which has previously attempted to connect to a server may have
+461/// been given a one-time use cryptographically secured "token" that it can send in a subsequent
+462/// connection attempt to be validated immediately.
+463///
+464/// There are two ways these tokens can originate:
+465///
+466/// - If the server responds to an incoming connection with `retry`, a "retry token" is minted and
+467///   sent to the client, which the client immediately uses to attempt to connect again. Retry
+468///   tokens operate on short timescales, such as 15 seconds.
+469/// - If a client's path within an active connection is validated, the server may send the client
+470///   one or more "validation tokens," which the client may store for use in later connections to
+471///   the same server. Validation tokens may be valid for much longer lifetimes than retry token.
+472///
+473/// The usage of validation tokens is most impactful in situations where 0-RTT data is also being
+474/// used--in particular, in situations where the server sends the client more than three times more
+475/// 0.5-RTT data than it has received 0-RTT data. Since the successful completion of a connection
+476/// handshake implicitly causes the client's address to be validated, transmission of 0.5-RTT data
+477/// is the main situation where a server might be sending application data to an address that could
+478/// be validated by token usage earlier than it would become validated without token usage.
+479///
+480/// [1]: https://www.rfc-editor.org/rfc/rfc9000.html#section-8
+481/// [2]: https://en.wikipedia.org/wiki/IP_address_spoofing
+482/// [3]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+483///
+484/// These tokens should not be confused with "stateless reset tokens," which are similarly named
+485/// but entirely unrelated.
+486#[derive(Clone)]
+487pub struct ValidationTokenConfig {
+488    pub(crate) lifetime: Duration,
+489    pub(crate) log: Arc<dyn TokenLog>,
+490    pub(crate) sent: u32,
+491}
+492
+493impl ValidationTokenConfig {
+494    /// Duration after an address validation token was issued for which it's considered valid
+495    ///
+496    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+497    ///
+498    /// Defaults to 2 weeks.
+499    pub fn lifetime(&mut self, value: Duration) -> &mut Self {
+500        self.lifetime = value;
+501        self
+502    }
+503
+504    #[allow(rustdoc::redundant_explicit_links)] // which links are redundant depends on features
+505    /// Set a custom [`TokenLog`]
+506    ///
+507    /// If the `bloom` feature is enabled (which it is by default), defaults to a default
+508    /// [`BloomTokenLog`][crate::BloomTokenLog], which is suitable for most internet applications.
+509    ///
+510    /// If the `bloom` feature is disabled, defaults to [`NoneTokenLog`][crate::NoneTokenLog],
+511    /// which makes the server ignore all address validation tokens (that is, tokens originating
+512    /// from NEW_TOKEN frames--retry tokens are not affected).
+513    pub fn log(&mut self, log: Arc<dyn TokenLog>) -> &mut Self {
+514        self.log = log;
+515        self
+516    }
+517
+518    /// Number of address validation tokens sent to a client when its path is validated
+519    ///
+520    /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens.
+521    ///
+522    /// If the `bloom` feature is enabled (which it is by default), defaults to 2. Otherwise,
+523    /// defaults to 0.
+524    pub fn sent(&mut self, value: u32) -> &mut Self {
+525        self.sent = value;
+526        self
+527    }
+528}
+529
+530impl Default for ValidationTokenConfig {
+531    fn default() -> Self {
+532        #[cfg(feature = "bloom")]
+533        let log = Arc::new(BloomTokenLog::default());
+534        #[cfg(not(feature = "bloom"))]
+535        let log = Arc::new(NoneTokenLog);
+536        Self {
+537            lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60),
+538            log,
+539            sent: if cfg!(feature = "bloom") { 2 } else { 0 },
+540        }
+541    }
+542}
+543
+544impl fmt::Debug for ValidationTokenConfig {
+545    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+546        fmt.debug_struct("ServerValidationTokenConfig")
+547            .field("lifetime", &self.lifetime)
+548            // log not debug
+549            .field("sent", &self.sent)
+550            .finish_non_exhaustive()
+551    }
+552}
+553
+554/// Configuration for outgoing connections
+555///
+556/// Default values should be suitable for most internet applications.
+557#[derive(Clone)]
+558#[non_exhaustive]
+559pub struct ClientConfig {
+560    /// Transport configuration to use
+561    pub(crate) transport: Arc<TransportConfig>,
+562
+563    /// Cryptographic configuration to use
+564    pub(crate) crypto: Arc<dyn crypto::ClientConfig>,
+565
+566    /// Validation token store to use
+567    pub(crate) token_store: Arc<dyn TokenStore>,
+568
+569    /// Provider that populates the destination connection ID of Initial Packets
+570    pub(crate) initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+571
+572    /// QUIC protocol version to use
+573    pub(crate) version: u32,
+574}
+575
+576impl ClientConfig {
+577    /// Create a default config with a particular cryptographic config
+578    pub fn new(crypto: Arc<dyn crypto::ClientConfig>) -> Self {
+579        Self {
+580            transport: Default::default(),
+581            crypto,
+582            token_store: Arc::new(TokenMemoryCache::default()),
+583            initial_dst_cid_provider: Arc::new(|| {
+584                RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid()
+585            }),
+586            version: 1,
+587        }
+588    }
+589
+590    /// Configure how to populate the destination CID of the initial packet when attempting to
+591    /// establish a new connection
+592    ///
+593    /// By default, it's populated with random bytes with reasonable length, so unless you have
+594    /// a good reason, you do not need to change it.
+595    ///
+596    /// When prefer to override the default, please note that the generated connection ID MUST be
+597    /// at least 8 bytes long and unpredictable, as per section 7.2 of RFC 9000.
+598    pub fn initial_dst_cid_provider(
+599        &mut self,
+600        initial_dst_cid_provider: Arc<dyn Fn() -> ConnectionId + Send + Sync>,
+601    ) -> &mut Self {
+602        self.initial_dst_cid_provider = initial_dst_cid_provider;
+603        self
+604    }
+605
+606    /// Set a custom [`TransportConfig`]
+607    pub fn transport_config(&mut self, transport: Arc<TransportConfig>) -> &mut Self {
+608        self.transport = transport;
+609        self
+610    }
+611
+612    /// Set a custom [`TokenStore`]
+613    ///
+614    /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications.
+615    pub fn token_store(&mut self, store: Arc<dyn TokenStore>) -> &mut Self {
+616        self.token_store = store;
+617        self
+618    }
+619
+620    /// Set the QUIC version to use
+621    pub fn version(&mut self, version: u32) -> &mut Self {
+622        self.version = version;
+623        self
+624    }
+625}
+626
+627#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+628impl ClientConfig {
+629    /// Create a client configuration that trusts the platform's native roots
+630    #[deprecated(since = "0.11.13", note = "use `try_with_platform_verifier()` instead")]
+631    #[cfg(feature = "platform-verifier")]
+632    pub fn with_platform_verifier() -> Self {
+633        Self::try_with_platform_verifier().expect("use try_with_platform_verifier() instead")
+634    }
+635
+636    /// Create a client configuration that trusts the platform's native roots
+637    #[cfg(feature = "platform-verifier")]
+638    pub fn try_with_platform_verifier() -> Result<Self, rustls::Error> {
+639        Ok(Self::new(Arc::new(
+640            crypto::rustls::QuicClientConfig::with_platform_verifier()?,
+641        )))
+642    }
+643
+644    /// Create a client configuration that trusts specified trust anchors
+645    pub fn with_root_certificates(
+646        roots: Arc<rustls::RootCertStore>,
+647    ) -> Result<Self, rustls::client::VerifierBuilderError> {
+648        Ok(Self::new(Arc::new(crypto::rustls::QuicClientConfig::new(
+649            WebPkiServerVerifier::builder_with_provider(roots, configured_provider()).build()?,
+650        ))))
+651    }
+652}
+653
+654impl fmt::Debug for ClientConfig {
+655    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+656        fmt.debug_struct("ClientConfig")
+657            .field("transport", &self.transport)
+658            // crypto not debug
+659            // token_store not debug
+660            .field("version", &self.version)
+661            .finish_non_exhaustive()
+662    }
+663}
+664
+665/// Errors in the configuration of an endpoint
+666#[derive(Debug, Error, Clone, PartialEq, Eq)]
+667#[non_exhaustive]
+668pub enum ConfigError {
+669    /// Value exceeds supported bounds
+670    #[error("value exceeds supported bounds")]
+671    OutOfBounds,
+672}
+673
+674impl From<TryFromIntError> for ConfigError {
+675    fn from(_: TryFromIntError) -> Self {
+676        Self::OutOfBounds
+677    }
+678}
+679
+680impl From<VarIntBoundsExceeded> for ConfigError {
+681    fn from(_: VarIntBoundsExceeded) -> Self {
+682        Self::OutOfBounds
+683    }
+684}
+685
+686/// Object to get current [`SystemTime`]
+687///
+688/// This exists to allow system time to be mocked in tests, or wherever else desired.
+689pub trait TimeSource: Send + Sync {
+690    /// Get [`SystemTime::now()`](SystemTime::now) or the mocked equivalent
+691    fn now(&self) -> SystemTime;
+692}
+693
+694/// Default implementation of [`TimeSource`]
+695///
+696/// Implements `now` by calling [`SystemTime::now()`](SystemTime::now).
+697pub struct StdSystemTime;
+698
+699impl TimeSource for StdSystemTime {
+700    fn now(&self) -> SystemTime {
+701        SystemTime::now()
+702    }
+703}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/config/qlog.rs.html b/pr/234/docs/src/iroh_quinn_proto/config/qlog.rs.html new file mode 100644 index 000000000..01d554ae9 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/config/qlog.rs.html @@ -0,0 +1,173 @@ +qlog.rs - source

iroh_quinn_proto/config/
qlog.rs

1#[cfg(feature = "qlog")]
+2use std::io;
+3use std::{io::BufWriter, net::SocketAddr, path::PathBuf, time::SystemTime};
+4
+5use tracing::{trace, warn};
+6
+7use crate::{ConnectionId, Instant, Side};
+8
+9/// Constructs a [`QlogConfig`] for individual connections.
+10///
+11/// This is set via [`TransportConfig::qlog_factory`].
+12///
+13/// [`TransportConfig::qlog_factory]: crate::config::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/234/docs/src/iroh_quinn_proto/config/transport.rs.html b/pr/234/docs/src/iroh_quinn_proto/config/transport.rs.html new file mode 100644 index 000000000..dc343d2ce --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/config/transport.rs.html @@ -0,0 +1,912 @@ +transport.rs - source

iroh_quinn_proto/config/
transport.rs

1#[cfg(feature = "qlog")]
+2use std::path::Path;
+3use std::{
+4    fmt,
+5    net::SocketAddr,
+6    num::{NonZeroU8, NonZeroU32},
+7    sync::Arc,
+8};
+9
+10use crate::{
+11    ConnectionId, Duration, INITIAL_MTU, Instant, MAX_UDP_PAYLOAD, Side, VarInt,
+12    VarIntBoundsExceeded, address_discovery, congestion, connection::qlog::QlogSink,
+13};
+14#[cfg(feature = "qlog")]
+15use crate::{QlogFactory, QlogFileFactory};
+16
+17/// When multipath is required and has not been explicitly enabled, this value will be used for
+18/// [`TransportConfig::max_concurrent_multipath_paths`].
+19const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_: NonZeroU32 = {
+20    match NonZeroU32::new(12) {
+21        Some(v) => v,
+22        None => panic!("to enable multipath this must be positive, which clearly it is"),
+23    }
+24};
+25
+26/// When multipath is required and has not been explicitly enabled, this value will be used for
+27///
+28/// [`TransportConfig::max_concurrent_multipath_paths`].
+29#[cfg(doc)]
+30pub const DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED: NonZeroU32 =
+31    DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_;
+32
+33/// Parameters governing the core QUIC state machine
+34///
+35/// Default values should be suitable for most internet applications. Applications protocols which
+36/// forbid remotely-initiated streams should set `max_concurrent_bidi_streams` and
+37/// `max_concurrent_uni_streams` to zero.
+38///
+39/// In some cases, performance or resource requirements can be improved by tuning these values to
+40/// suit a particular application and/or network connection. In particular, data window sizes can be
+41/// tuned for a particular expected round trip time, link capacity, and memory availability. Tuning
+42/// for higher bandwidths and latencies increases worst-case memory consumption, but does not impair
+43/// performance at lower bandwidths and latencies. The default configuration is tuned for a 100Mbps
+44/// link with a 100ms round trip time.
+45#[derive(Clone)]
+46pub struct TransportConfig {
+47    pub(crate) max_concurrent_bidi_streams: VarInt,
+48    pub(crate) max_concurrent_uni_streams: VarInt,
+49    pub(crate) max_idle_timeout: Option<VarInt>,
+50    pub(crate) stream_receive_window: VarInt,
+51    pub(crate) receive_window: VarInt,
+52    pub(crate) send_window: u64,
+53    pub(crate) send_fairness: bool,
+54
+55    pub(crate) packet_threshold: u32,
+56    pub(crate) time_threshold: f32,
+57    pub(crate) initial_rtt: Duration,
+58    pub(crate) initial_mtu: u16,
+59    pub(crate) min_mtu: u16,
+60    pub(crate) mtu_discovery_config: Option<MtuDiscoveryConfig>,
+61    pub(crate) pad_to_mtu: bool,
+62    pub(crate) ack_frequency_config: Option<AckFrequencyConfig>,
+63
+64    pub(crate) persistent_congestion_threshold: u32,
+65    pub(crate) keep_alive_interval: Option<Duration>,
+66    pub(crate) crypto_buffer_size: usize,
+67    pub(crate) allow_spin: bool,
+68    pub(crate) datagram_receive_buffer_size: Option<usize>,
+69    pub(crate) datagram_send_buffer_size: usize,
+70    #[cfg(test)]
+71    pub(crate) deterministic_packet_numbers: bool,
+72
+73    pub(crate) congestion_controller_factory: Arc<dyn congestion::ControllerFactory + Send + Sync>,
+74
+75    pub(crate) enable_segmentation_offload: bool,
+76
+77    pub(crate) address_discovery_role: address_discovery::Role,
+78
+79    pub(crate) max_concurrent_multipath_paths: Option<NonZeroU32>,
+80
+81    pub(crate) default_path_max_idle_timeout: Option<Duration>,
+82    pub(crate) default_path_keep_alive_interval: Option<Duration>,
+83
+84    pub(crate) max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+85
+86    #[cfg(feature = "qlog")]
+87    pub(crate) qlog_factory: Option<Arc<dyn QlogFactory>>,
+88}
+89
+90impl TransportConfig {
+91    /// Maximum number of incoming bidirectional streams that may be open concurrently
+92    ///
+93    /// Must be nonzero for the peer to open any bidirectional streams.
+94    ///
+95    /// Worst-case memory use is directly proportional to `max_concurrent_bidi_streams *
+96    /// stream_receive_window`, with an upper bound proportional to `receive_window`.
+97    pub fn max_concurrent_bidi_streams(&mut self, value: VarInt) -> &mut Self {
+98        self.max_concurrent_bidi_streams = value;
+99        self
+100    }
+101
+102    /// Variant of `max_concurrent_bidi_streams` affecting unidirectional streams
+103    pub fn max_concurrent_uni_streams(&mut self, value: VarInt) -> &mut Self {
+104        self.max_concurrent_uni_streams = value;
+105        self
+106    }
+107
+108    /// Maximum duration of inactivity to accept before timing out the connection.
+109    ///
+110    /// The true idle timeout is the minimum of this and the peer's own max idle timeout. `None`
+111    /// represents an infinite timeout. Defaults to 30 seconds.
+112    ///
+113    /// **WARNING**: If a peer or its network path malfunctions or acts maliciously, an infinite
+114    /// idle timeout can result in permanently hung futures!
+115    ///
+116    /// ```
+117    /// # use std::{convert::TryInto, time::Duration};
+118    /// # use iroh_quinn_proto::{TransportConfig, VarInt, VarIntBoundsExceeded};
+119    /// # fn main() -> Result<(), VarIntBoundsExceeded> {
+120    /// let mut config = TransportConfig::default();
+121    ///
+122    /// // Set the idle timeout as `VarInt`-encoded milliseconds
+123    /// config.max_idle_timeout(Some(VarInt::from_u32(10_000).into()));
+124    ///
+125    /// // Set the idle timeout as a `Duration`
+126    /// config.max_idle_timeout(Some(Duration::from_secs(10).try_into()?));
+127    /// # Ok(())
+128    /// # }
+129    /// ```
+130    pub fn max_idle_timeout(&mut self, value: Option<IdleTimeout>) -> &mut Self {
+131        self.max_idle_timeout = value.map(|t| t.0);
+132        self
+133    }
+134
+135    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+136    /// before becoming blocked.
+137    ///
+138    /// This should be set to at least the expected connection latency multiplied by the maximum
+139    /// desired throughput. Setting this smaller than `receive_window` helps ensure that a single
+140    /// stream doesn't monopolize receive buffers, which may otherwise occur if the application
+141    /// chooses not to read from a large stream for a time while still requiring data on other
+142    /// streams.
+143    pub fn stream_receive_window(&mut self, value: VarInt) -> &mut Self {
+144        self.stream_receive_window = value;
+145        self
+146    }
+147
+148    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+149    /// becoming blocked.
+150    ///
+151    /// This should be set to at least the expected connection latency multiplied by the maximum
+152    /// desired throughput. Larger values can be useful to allow maximum throughput within a
+153    /// stream while another is blocked.
+154    pub fn receive_window(&mut self, value: VarInt) -> &mut Self {
+155        self.receive_window = value;
+156        self
+157    }
+158
+159    /// Maximum number of bytes to transmit to a peer without acknowledgment
+160    ///
+161    /// Provides an upper bound on memory when communicating with peers that issue large amounts of
+162    /// flow control credit. Endpoints that wish to handle large numbers of connections robustly
+163    /// should take care to set this low enough to guarantee memory exhaustion does not occur if
+164    /// every connection uses the entire window.
+165    pub fn send_window(&mut self, value: u64) -> &mut Self {
+166        self.send_window = value;
+167        self
+168    }
+169
+170    /// Whether to implement fair queuing for send streams having the same priority.
+171    ///
+172    /// When enabled, connections schedule data from outgoing streams having the same priority in a
+173    /// round-robin fashion. When disabled, streams are scheduled in the order they are written to.
+174    ///
+175    /// Note that this only affects streams with the same priority. Higher priority streams always
+176    /// take precedence over lower priority streams.
+177    ///
+178    /// Disabling fairness can reduce fragmentation and protocol overhead for workloads that use
+179    /// many small streams.
+180    pub fn send_fairness(&mut self, value: bool) -> &mut Self {
+181        self.send_fairness = value;
+182        self
+183    }
+184
+185    /// Maximum reordering in packet number space before FACK style loss detection considers a
+186    /// packet lost. Should not be less than 3, per RFC5681.
+187    pub fn packet_threshold(&mut self, value: u32) -> &mut Self {
+188        self.packet_threshold = value;
+189        self
+190    }
+191
+192    /// Maximum reordering in time space before time based loss detection considers a packet lost,
+193    /// as a factor of RTT
+194    pub fn time_threshold(&mut self, value: f32) -> &mut Self {
+195        self.time_threshold = value;
+196        self
+197    }
+198
+199    /// The RTT used before an RTT sample is taken
+200    pub fn initial_rtt(&mut self, value: Duration) -> &mut Self {
+201        self.initial_rtt = value;
+202        self
+203    }
+204
+205    /// The initial value to be used as the maximum UDP payload size before running MTU discovery
+206    /// (see [`TransportConfig::mtu_discovery_config`]).
+207    ///
+208    /// Must be at least 1200, which is the default, and known to be safe for typical internet
+209    /// applications. Larger values are more efficient, but increase the risk of packet loss due to
+210    /// exceeding the network path's IP MTU. If the provided value is higher than what the network
+211    /// path actually supports, packet loss will eventually trigger black hole detection and bring
+212    /// it down to [`TransportConfig::min_mtu`].
+213    pub fn initial_mtu(&mut self, value: u16) -> &mut Self {
+214        self.initial_mtu = value.max(INITIAL_MTU);
+215        self
+216    }
+217
+218    pub(crate) fn get_initial_mtu(&self) -> u16 {
+219        self.initial_mtu.max(self.min_mtu)
+220    }
+221
+222    /// The maximum UDP payload size guaranteed to be supported by the network.
+223    ///
+224    /// Must be at least 1200, which is the default, and lower than or equal to
+225    /// [`TransportConfig::initial_mtu`].
+226    ///
+227    /// Real-world MTUs can vary according to ISP, VPN, and properties of intermediate network links
+228    /// outside of either endpoint's control. Extreme care should be used when raising this value
+229    /// outside of private networks where these factors are fully controlled. If the provided value
+230    /// is higher than what the network path actually supports, the result will be unpredictable and
+231    /// catastrophic packet loss, without a possibility of repair. Prefer
+232    /// [`TransportConfig::initial_mtu`] together with
+233    /// [`TransportConfig::mtu_discovery_config`] to set a maximum UDP payload size that robustly
+234    /// adapts to the network.
+235    pub fn min_mtu(&mut self, value: u16) -> &mut Self {
+236        self.min_mtu = value.max(INITIAL_MTU);
+237        self
+238    }
+239
+240    /// Specifies the MTU discovery config (see [`MtuDiscoveryConfig`] for details).
+241    ///
+242    /// Enabled by default.
+243    pub fn mtu_discovery_config(&mut self, value: Option<MtuDiscoveryConfig>) -> &mut Self {
+244        self.mtu_discovery_config = value;
+245        self
+246    }
+247
+248    /// Pad UDP datagrams carrying application data to current maximum UDP payload size
+249    ///
+250    /// Disabled by default. UDP datagrams containing loss probes are exempt from padding.
+251    ///
+252    /// Enabling this helps mitigate traffic analysis by network observers, but it increases
+253    /// bandwidth usage. Without this mitigation precise plain text size of application datagrams as
+254    /// well as the total size of stream write bursts can be inferred by observers under certain
+255    /// conditions. This analysis requires either an uncongested connection or application datagrams
+256    /// too large to be coalesced.
+257    pub fn pad_to_mtu(&mut self, value: bool) -> &mut Self {
+258        self.pad_to_mtu = value;
+259        self
+260    }
+261
+262    /// Specifies the ACK frequency config (see [`AckFrequencyConfig`] for details)
+263    ///
+264    /// The provided configuration will be ignored if the peer does not support the acknowledgement
+265    /// frequency QUIC extension.
+266    ///
+267    /// Defaults to `None`, which disables controlling the peer's acknowledgement frequency. Even
+268    /// if set to `None`, the local side still supports the acknowledgement frequency QUIC
+269    /// extension and may use it in other ways.
+270    pub fn ack_frequency_config(&mut self, value: Option<AckFrequencyConfig>) -> &mut Self {
+271        self.ack_frequency_config = value;
+272        self
+273    }
+274
+275    /// Number of consecutive PTOs after which network is considered to be experiencing persistent congestion.
+276    pub fn persistent_congestion_threshold(&mut self, value: u32) -> &mut Self {
+277        self.persistent_congestion_threshold = value;
+278        self
+279    }
+280
+281    /// Period of inactivity before sending a keep-alive packet
+282    ///
+283    /// Keep-alive packets prevent an inactive but otherwise healthy connection from timing out.
+284    ///
+285    /// `None` to disable, which is the default. Only one side of any given connection needs keep-alive
+286    /// enabled for the connection to be preserved. Must be set lower than the idle_timeout of both
+287    /// peers to be effective.
+288    pub fn keep_alive_interval(&mut self, value: Option<Duration>) -> &mut Self {
+289        self.keep_alive_interval = value;
+290        self
+291    }
+292
+293    /// Maximum quantity of out-of-order crypto layer data to buffer
+294    pub fn crypto_buffer_size(&mut self, value: usize) -> &mut Self {
+295        self.crypto_buffer_size = value;
+296        self
+297    }
+298
+299    /// Whether the implementation is permitted to set the spin bit on this connection
+300    ///
+301    /// This allows passive observers to easily judge the round trip time of a connection, which can
+302    /// be useful for network administration but sacrifices a small amount of privacy.
+303    pub fn allow_spin(&mut self, value: bool) -> &mut Self {
+304        self.allow_spin = value;
+305        self
+306    }
+307
+308    /// Maximum number of incoming application datagram bytes to buffer, or None to disable
+309    /// incoming datagrams
+310    ///
+311    /// The peer is forbidden to send single datagrams larger than this size. If the aggregate size
+312    /// of all datagrams that have been received from the peer but not consumed by the application
+313    /// exceeds this value, old datagrams are dropped until it is no longer exceeded.
+314    pub fn datagram_receive_buffer_size(&mut self, value: Option<usize>) -> &mut Self {
+315        self.datagram_receive_buffer_size = value;
+316        self
+317    }
+318
+319    /// Maximum number of outgoing application datagram bytes to buffer
+320    ///
+321    /// While datagrams are sent ASAP, it is possible for an application to generate data faster
+322    /// than the link, or even the underlying hardware, can transmit them. This limits the amount of
+323    /// memory that may be consumed in that case. When the send buffer is full and a new datagram is
+324    /// sent, older datagrams are dropped until sufficient space is available.
+325    pub fn datagram_send_buffer_size(&mut self, value: usize) -> &mut Self {
+326        self.datagram_send_buffer_size = value;
+327        self
+328    }
+329
+330    /// Whether to force every packet number to be used
+331    ///
+332    /// By default, packet numbers are occasionally skipped to ensure peers aren't ACKing packets
+333    /// before they see them.
+334    #[cfg(test)]
+335    pub(crate) fn deterministic_packet_numbers(&mut self, enabled: bool) -> &mut Self {
+336        self.deterministic_packet_numbers = enabled;
+337        self
+338    }
+339
+340    /// How to construct new `congestion::Controller`s
+341    ///
+342    /// Typically the refcounted configuration of a `congestion::Controller`,
+343    /// e.g. a `congestion::NewRenoConfig`.
+344    ///
+345    /// # Example
+346    /// ```
+347    /// # use iroh_quinn_proto::*; use std::sync::Arc;
+348    /// let mut config = TransportConfig::default();
+349    /// config.congestion_controller_factory(Arc::new(congestion::NewRenoConfig::default()));
+350    /// ```
+351    pub fn congestion_controller_factory(
+352        &mut self,
+353        factory: Arc<dyn congestion::ControllerFactory + Send + Sync + 'static>,
+354    ) -> &mut Self {
+355        self.congestion_controller_factory = factory;
+356        self
+357    }
+358
+359    /// Whether to use "Generic Segmentation Offload" to accelerate transmits, when supported by the
+360    /// environment
+361    ///
+362    /// Defaults to `true`.
+363    ///
+364    /// GSO dramatically reduces CPU consumption when sending large numbers of packets with the same
+365    /// headers, such as when transmitting bulk data on a connection. However, it is not supported
+366    /// by all network interface drivers or packet inspection tools. `quinn-udp` will attempt to
+367    /// disable GSO automatically when unavailable, but this can lead to spurious packet loss at
+368    /// startup, temporarily degrading performance.
+369    pub fn enable_segmentation_offload(&mut self, enabled: bool) -> &mut Self {
+370        self.enable_segmentation_offload = enabled;
+371        self
+372    }
+373
+374    /// Whether to send observed address reports to peers.
+375    ///
+376    /// This will aid peers in inferring their reachable address, which in most NATd networks
+377    /// will not be easily available to them.
+378    pub fn send_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+379        self.address_discovery_role.send_reports_to_peers(enabled);
+380        self
+381    }
+382
+383    /// Whether to receive observed address reports from other peers.
+384    ///
+385    /// Peers with the address discovery extension enabled that are willing to provide observed
+386    /// address reports will do so if this transport parameter is set. In general, observed address
+387    /// reports cannot be trusted. This, however, can aid the current endpoint in inferring its
+388    /// reachable address, which in most NATd networks will not be easily available.
+389    pub fn receive_observed_address_reports(&mut self, enabled: bool) -> &mut Self {
+390        self.address_discovery_role
+391            .receive_reports_from_peers(enabled);
+392        self
+393    }
+394
+395    /// Enables the Multipath Extension for QUIC.
+396    ///
+397    /// Setting this to any nonzero value will enable the Multipath Extension for QUIC,
+398    /// <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/>.
+399    ///
+400    /// The value provided specifies the number maximum number of paths this endpoint may open
+401    /// concurrently when multipath is negotiated. For any path to be opened, the remote must
+402    /// enable multipath as well.
+403    pub fn max_concurrent_multipath_paths(&mut self, max_concurrent: u32) -> &mut Self {
+404        self.max_concurrent_multipath_paths = NonZeroU32::new(max_concurrent);
+405        self
+406    }
+407
+408    /// Sets a default per-path maximum idle timeout
+409    ///
+410    /// If the path is idle for this long the path will be abandoned. Bear in mind this will
+411    /// interact with the [`TransportConfig::max_idle_timeout`], if the last path is
+412    /// abandoned the entire connection will be closed.
+413    ///
+414    /// You can also change this using [`Connection::set_path_max_idle_timeout`] for
+415    /// existing paths.
+416    ///
+417    /// [`Connection::set_path_max_idle_timeout`]: crate::Connection::set_path_max_idle_timeout
+418    pub fn default_path_max_idle_timeout(&mut self, timeout: Option<Duration>) -> &mut Self {
+419        self.default_path_max_idle_timeout = timeout;
+420        self
+421    }
+422
+423    /// Sets a default per-path keep alive interval
+424    ///
+425    /// Note that this does not interact with the connection-wide
+426    /// [`TransportConfig::keep_alive_interval`].  This setting will keep this path active,
+427    /// [`TransportConfig::keep_alive_interval`] will keep the connection active, with no
+428    /// control over which path is used for this.
+429    ///
+430    /// You can also change this using [`Connection::set_path_keep_alive_interval`] for
+431    /// existing path.
+432    ///
+433    /// [`Connection::set_path_keep_alive_interval`]: crate::Connection::set_path_keep_alive_interval
+434    pub fn default_path_keep_alive_interval(&mut self, interval: Option<Duration>) -> &mut Self {
+435        self.default_path_keep_alive_interval = interval;
+436        self
+437    }
+438
+439    /// Get the initial max [`crate::PathId`] this endpoint allows.
+440    ///
+441    /// Returns `None` if multipath is disabled.
+442    pub(crate) fn get_initial_max_path_id(&self) -> Option<crate::PathId> {
+443        self.max_concurrent_multipath_paths
+444            // a max_concurrent_multipath_paths value of 1 only allows the first path, which
+445            // has id 0
+446            .map(|nonzero_concurrent| nonzero_concurrent.get() - 1)
+447            .map(Into::into)
+448    }
+449
+450    /// Sets the maximum number of nat traversal addresses this endpoint allows the remote to
+451    /// advertise
+452    ///
+453    /// Setting this to any nonzero value will enable Iroh's holepunching, loosely based in the Nat
+454    /// Traversal Extension for QUIC, see
+455    /// <https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html>
+456    ///
+457    /// This implementation expects the multipath extension to be enabled as well. if not yet
+458    /// enabled via [`Self::max_concurrent_multipath_paths`], a default value of
+459    /// [`DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED`] will be used.
+460    pub fn set_max_remote_nat_traversal_addresses(&mut self, max_addresses: u8) -> &mut Self {
+461        self.max_remote_nat_traversal_addresses = NonZeroU8::new(max_addresses);
+462        if max_addresses != 0 && self.max_concurrent_multipath_paths.is_none() {
+463            self.max_concurrent_multipath_paths(
+464                DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED_.get(),
+465            );
+466        }
+467        self
+468    }
+469
+470    /// Configures qlog capturing by setting a [`QlogFactory`].
+471    ///
+472    /// This assigns a [`QlogFactory`] that produces qlog capture configurations for
+473    /// individual connections.
+474    #[cfg(feature = "qlog")]
+475    pub fn qlog_factory(&mut self, factory: Arc<dyn QlogFactory>) -> &mut Self {
+476        self.qlog_factory = Some(factory);
+477        self
+478    }
+479
+480    /// Configures qlog capturing through the `QLOGDIR` environment variable.
+481    ///
+482    /// This uses [`QlogFileFactory::from_env`] to create a factory to write qlog traces
+483    /// into the directory set through the `QLOGDIR` environment variable.
+484    ///
+485    /// If `QLOGDIR` is not set, no traces will be written. If `QLOGDIR` is set to a path
+486    /// that does not exist, it will be created.
+487    ///
+488    /// The files will be prefixed with `prefix`.
+489    #[cfg(feature = "qlog")]
+490    pub fn qlog_from_env(&mut self, prefix: &str) -> &mut Self {
+491        self.qlog_factory(Arc::new(QlogFileFactory::from_env().with_prefix(prefix)))
+492    }
+493
+494    /// Configures qlog capturing into a directory.
+495    ///
+496    /// This uses [`QlogFileFactory`] to create a factory to write qlog traces into
+497    /// the specified directory.  The files will be prefixed with `prefix`.
+498    #[cfg(feature = "qlog")]
+499    pub fn qlog_from_path(&mut self, path: impl AsRef<Path>, prefix: &str) -> &mut Self {
+500        self.qlog_factory(Arc::new(
+501            QlogFileFactory::new(path.as_ref().to_owned()).with_prefix(prefix),
+502        ))
+503    }
+504
+505    pub(crate) fn create_qlog_sink(
+506        &self,
+507        side: Side,
+508        remote: SocketAddr,
+509        initial_dst_cid: ConnectionId,
+510        now: Instant,
+511    ) -> QlogSink {
+512        #[cfg(not(feature = "qlog"))]
+513        let sink = {
+514            let _ = (side, remote, initial_dst_cid, now);
+515            QlogSink::default()
+516        };
+517
+518        #[cfg(feature = "qlog")]
+519        let sink = {
+520            if let Some(config) = self
+521                .qlog_factory
+522                .as_ref()
+523                .and_then(|factory| factory.for_connection(side, remote, initial_dst_cid, now))
+524            {
+525                QlogSink::new(config, initial_dst_cid, side, now)
+526            } else {
+527                QlogSink::default()
+528            }
+529        };
+530
+531        sink
+532    }
+533}
+534
+535impl Default for TransportConfig {
+536    fn default() -> Self {
+537        const EXPECTED_RTT: u32 = 100; // ms
+538        const MAX_STREAM_BANDWIDTH: u32 = 12500 * 1000; // bytes/s
+539        // Window size needed to avoid pipeline
+540        // stalls
+541        const STREAM_RWND: u32 = MAX_STREAM_BANDWIDTH / 1000 * EXPECTED_RTT;
+542
+543        Self {
+544            max_concurrent_bidi_streams: 100u32.into(),
+545            max_concurrent_uni_streams: 100u32.into(),
+546            // 30 second default recommended by RFC 9308 § 3.2
+547            max_idle_timeout: Some(VarInt(30_000)),
+548            stream_receive_window: STREAM_RWND.into(),
+549            receive_window: VarInt::MAX,
+550            send_window: (8 * STREAM_RWND).into(),
+551            send_fairness: true,
+552
+553            packet_threshold: 3,
+554            time_threshold: 9.0 / 8.0,
+555            initial_rtt: Duration::from_millis(333), // per spec, intentionally distinct from EXPECTED_RTT
+556            initial_mtu: INITIAL_MTU,
+557            min_mtu: INITIAL_MTU,
+558            mtu_discovery_config: Some(MtuDiscoveryConfig::default()),
+559            pad_to_mtu: false,
+560            ack_frequency_config: None,
+561
+562            persistent_congestion_threshold: 3,
+563            keep_alive_interval: None,
+564            crypto_buffer_size: 16 * 1024,
+565            allow_spin: true,
+566            datagram_receive_buffer_size: Some(STREAM_RWND as usize),
+567            datagram_send_buffer_size: 1024 * 1024,
+568            #[cfg(test)]
+569            deterministic_packet_numbers: false,
+570
+571            congestion_controller_factory: Arc::new(congestion::CubicConfig::default()),
+572
+573            enable_segmentation_offload: true,
+574
+575            address_discovery_role: address_discovery::Role::default(),
+576
+577            // disabled multipath by default
+578            max_concurrent_multipath_paths: None,
+579            default_path_max_idle_timeout: None,
+580            default_path_keep_alive_interval: None,
+581
+582            // nat traversal disabled by default
+583            max_remote_nat_traversal_addresses: None,
+584
+585            #[cfg(feature = "qlog")]
+586            qlog_factory: None,
+587        }
+588    }
+589}
+590
+591impl fmt::Debug for TransportConfig {
+592    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+593        let Self {
+594            max_concurrent_bidi_streams,
+595            max_concurrent_uni_streams,
+596            max_idle_timeout,
+597            stream_receive_window,
+598            receive_window,
+599            send_window,
+600            send_fairness,
+601            packet_threshold,
+602            time_threshold,
+603            initial_rtt,
+604            initial_mtu,
+605            min_mtu,
+606            mtu_discovery_config,
+607            pad_to_mtu,
+608            ack_frequency_config,
+609            persistent_congestion_threshold,
+610            keep_alive_interval,
+611            crypto_buffer_size,
+612            allow_spin,
+613            datagram_receive_buffer_size,
+614            datagram_send_buffer_size,
+615            #[cfg(test)]
+616                deterministic_packet_numbers: _,
+617            congestion_controller_factory: _,
+618            enable_segmentation_offload,
+619            address_discovery_role,
+620            max_concurrent_multipath_paths,
+621            default_path_max_idle_timeout,
+622            default_path_keep_alive_interval,
+623            max_remote_nat_traversal_addresses,
+624            #[cfg(feature = "qlog")]
+625            qlog_factory,
+626        } = self;
+627        let mut s = fmt.debug_struct("TransportConfig");
+628
+629        s.field("max_concurrent_bidi_streams", max_concurrent_bidi_streams)
+630            .field("max_concurrent_uni_streams", max_concurrent_uni_streams)
+631            .field("max_idle_timeout", max_idle_timeout)
+632            .field("stream_receive_window", stream_receive_window)
+633            .field("receive_window", receive_window)
+634            .field("send_window", send_window)
+635            .field("send_fairness", send_fairness)
+636            .field("packet_threshold", packet_threshold)
+637            .field("time_threshold", time_threshold)
+638            .field("initial_rtt", initial_rtt)
+639            .field("initial_mtu", initial_mtu)
+640            .field("min_mtu", min_mtu)
+641            .field("mtu_discovery_config", mtu_discovery_config)
+642            .field("pad_to_mtu", pad_to_mtu)
+643            .field("ack_frequency_config", ack_frequency_config)
+644            .field(
+645                "persistent_congestion_threshold",
+646                persistent_congestion_threshold,
+647            )
+648            .field("keep_alive_interval", keep_alive_interval)
+649            .field("crypto_buffer_size", crypto_buffer_size)
+650            .field("allow_spin", allow_spin)
+651            .field("datagram_receive_buffer_size", datagram_receive_buffer_size)
+652            .field("datagram_send_buffer_size", datagram_send_buffer_size)
+653            // congestion_controller_factory not debug
+654            .field("enable_segmentation_offload", enable_segmentation_offload)
+655            .field("address_discovery_role", address_discovery_role)
+656            .field(
+657                "max_concurrent_multipath_paths",
+658                max_concurrent_multipath_paths,
+659            )
+660            .field(
+661                "default_path_max_idle_timeout",
+662                default_path_max_idle_timeout,
+663            )
+664            .field(
+665                "default_path_keep_alive_interval",
+666                default_path_keep_alive_interval,
+667            )
+668            .field(
+669                "max_remote_nat_traversal_addresses",
+670                max_remote_nat_traversal_addresses,
+671            );
+672        #[cfg(feature = "qlog")]
+673        s.field("qlog_factory", &qlog_factory.is_some());
+674
+675        s.finish_non_exhaustive()
+676    }
+677}
+678
+679/// Parameters for controlling the peer's acknowledgement frequency
+680///
+681/// The parameters provided in this config will be sent to the peer at the beginning of the
+682/// connection, so it can take them into account when sending acknowledgements (see each parameter's
+683/// description for details on how it influences acknowledgement frequency).
+684///
+685/// Quinn's implementation follows the fourth draft of the
+686/// [QUIC Acknowledgement Frequency extension](https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency-04).
+687/// The defaults produce behavior slightly different than the behavior without this extension,
+688/// because they change the way reordered packets are handled (see
+689/// [`AckFrequencyConfig::reordering_threshold`] for details).
+690#[derive(Clone, Debug)]
+691pub struct AckFrequencyConfig {
+692    pub(crate) ack_eliciting_threshold: VarInt,
+693    pub(crate) max_ack_delay: Option<Duration>,
+694    pub(crate) reordering_threshold: VarInt,
+695}
+696
+697impl AckFrequencyConfig {
+698    /// The ack-eliciting threshold we will request the peer to use
+699    ///
+700    /// This threshold represents the number of ack-eliciting packets an endpoint may receive
+701    /// without immediately sending an ACK.
+702    ///
+703    /// The remote peer should send at least one ACK frame when more than this number of
+704    /// ack-eliciting packets have been received. A value of 0 results in a receiver immediately
+705    /// acknowledging every ack-eliciting packet.
+706    ///
+707    /// Defaults to 1, which sends ACK frames for every other ack-eliciting packet.
+708    pub fn ack_eliciting_threshold(&mut self, value: VarInt) -> &mut Self {
+709        self.ack_eliciting_threshold = value;
+710        self
+711    }
+712
+713    /// The `max_ack_delay` we will request the peer to use
+714    ///
+715    /// This parameter represents the maximum amount of time that an endpoint waits before sending
+716    /// an ACK when the ack-eliciting threshold hasn't been reached.
+717    ///
+718    /// The effective `max_ack_delay` will be clamped to be at least the peer's `min_ack_delay`
+719    /// transport parameter, and at most the greater of the current path RTT or 25ms.
+720    ///
+721    /// Defaults to `None`, in which case the peer's original `max_ack_delay` will be used, as
+722    /// obtained from its transport parameters.
+723    pub fn max_ack_delay(&mut self, value: Option<Duration>) -> &mut Self {
+724        self.max_ack_delay = value;
+725        self
+726    }
+727
+728    /// The reordering threshold we will request the peer to use
+729    ///
+730    /// This threshold represents the amount of out-of-order packets that will trigger an endpoint
+731    /// to send an ACK, without waiting for `ack_eliciting_threshold` to be exceeded or for
+732    /// `max_ack_delay` to be elapsed.
+733    ///
+734    /// A value of 0 indicates out-of-order packets do not elicit an immediate ACK. A value of 1
+735    /// immediately acknowledges any packets that are received out of order (this is also the
+736    /// behavior when the extension is disabled).
+737    ///
+738    /// It is recommended to set this value to [`TransportConfig::packet_threshold`] minus one.
+739    /// Since the default value for [`TransportConfig::packet_threshold`] is 3, this value defaults
+740    /// to 2.
+741    pub fn reordering_threshold(&mut self, value: VarInt) -> &mut Self {
+742        self.reordering_threshold = value;
+743        self
+744    }
+745}
+746
+747impl Default for AckFrequencyConfig {
+748    fn default() -> Self {
+749        Self {
+750            ack_eliciting_threshold: VarInt(1),
+751            max_ack_delay: None,
+752            reordering_threshold: VarInt(2),
+753        }
+754    }
+755}
+756
+757/// Parameters governing MTU discovery.
+758///
+759/// # The why of MTU discovery
+760///
+761/// By design, QUIC ensures during the handshake that the network path between the client and the
+762/// server is able to transmit unfragmented UDP packets with a body of 1200 bytes. In other words,
+763/// once the connection is established, we know that the network path's maximum transmission unit
+764/// (MTU) is of at least 1200 bytes (plus IP and UDP headers). Because of this, a QUIC endpoint can
+765/// split outgoing data in packets of 1200 bytes, with confidence that the network will be able to
+766/// deliver them (if the endpoint were to send bigger packets, they could prove too big and end up
+767/// being dropped).
+768///
+769/// There is, however, a significant overhead associated to sending a packet. If the same
+770/// information can be sent in fewer packets, that results in higher throughput. The amount of
+771/// packets that need to be sent is inversely proportional to the MTU: the higher the MTU, the
+772/// bigger the packets that can be sent, and the fewer packets that are needed to transmit a given
+773/// amount of bytes.
+774///
+775/// Most networks have an MTU higher than 1200. Through MTU discovery, endpoints can detect the
+776/// path's MTU and, if it turns out to be higher, start sending bigger packets.
+777///
+778/// # MTU discovery internals
+779///
+780/// Quinn implements MTU discovery through DPLPMTUD (Datagram Packetization Layer Path MTU
+781/// Discovery), described in [section 14.3 of RFC
+782/// 9000](https://www.rfc-editor.org/rfc/rfc9000.html#section-14.3). This method consists of sending
+783/// QUIC packets padded to a particular size (called PMTU probes), and waiting to see if the remote
+784/// peer responds with an ACK. If an ACK is received, that means the probe arrived at the remote
+785/// peer, which in turn means that the network path's MTU is of at least the packet's size. If the
+786/// probe is lost, it is sent another 2 times before concluding that the MTU is lower than the
+787/// packet's size.
+788///
+789/// MTU discovery runs on a schedule (e.g. every 600 seconds) specified through
+790/// [`MtuDiscoveryConfig::interval`]. The first run happens right after the handshake, and
+791/// subsequent discoveries are scheduled to run when the interval has elapsed, starting from the
+792/// last time when MTU discovery completed.
+793///
+794/// Since the search space for MTUs is quite big (the smallest possible MTU is 1200, and the highest
+795/// is 65527), Quinn performs a binary search to keep the number of probes as low as possible. The
+796/// lower bound of the search is equal to [`TransportConfig::initial_mtu`] in the
+797/// initial MTU discovery run, and is equal to the currently discovered MTU in subsequent runs. The
+798/// upper bound is determined by the minimum of [`MtuDiscoveryConfig::upper_bound`] and the
+799/// `max_udp_payload_size` transport parameter received from the peer during the handshake.
+800///
+801/// # Black hole detection
+802///
+803/// If, at some point, the network path no longer accepts packets of the detected size, packet loss
+804/// will eventually trigger black hole detection and reset the detected MTU to 1200. In that case,
+805/// MTU discovery will be triggered after [`MtuDiscoveryConfig::black_hole_cooldown`] (ignoring the
+806/// timer that was set based on [`MtuDiscoveryConfig::interval`]).
+807///
+808/// # Interaction between peers
+809///
+810/// There is no guarantee that the MTU on the path between A and B is the same as the MTU of the
+811/// path between B and A. Therefore, each peer in the connection needs to run MTU discovery
+812/// independently in order to discover the path's MTU.
+813#[derive(Clone, Debug)]
+814pub struct MtuDiscoveryConfig {
+815    pub(crate) interval: Duration,
+816    pub(crate) upper_bound: u16,
+817    pub(crate) minimum_change: u16,
+818    pub(crate) black_hole_cooldown: Duration,
+819}
+820
+821impl MtuDiscoveryConfig {
+822    /// Specifies the time to wait after completing MTU discovery before starting a new MTU
+823    /// discovery run.
+824    ///
+825    /// Defaults to 600 seconds, as recommended by [RFC
+826    /// 8899](https://www.rfc-editor.org/rfc/rfc8899).
+827    pub fn interval(&mut self, value: Duration) -> &mut Self {
+828        self.interval = value;
+829        self
+830    }
+831
+832    /// Specifies the upper bound to the max UDP payload size that MTU discovery will search for.
+833    ///
+834    /// Defaults to 1452, to stay within Ethernet's MTU when using IPv4 and IPv6. The highest
+835    /// allowed value is 65527, which corresponds to the maximum permitted UDP payload on IPv6.
+836    ///
+837    /// It is safe to use an arbitrarily high upper bound, regardless of the network path's MTU. The
+838    /// only drawback is that MTU discovery might take more time to finish.
+839    pub fn upper_bound(&mut self, value: u16) -> &mut Self {
+840        self.upper_bound = value.min(MAX_UDP_PAYLOAD);
+841        self
+842    }
+843
+844    /// Specifies the amount of time that MTU discovery should wait after a black hole was detected
+845    /// before running again. Defaults to one minute.
+846    ///
+847    /// Black hole detection can be spuriously triggered in case of congestion, so it makes sense to
+848    /// try MTU discovery again after a short period of time.
+849    pub fn black_hole_cooldown(&mut self, value: Duration) -> &mut Self {
+850        self.black_hole_cooldown = value;
+851        self
+852    }
+853
+854    /// Specifies the minimum MTU change to stop the MTU discovery phase.
+855    /// Defaults to 20.
+856    pub fn minimum_change(&mut self, value: u16) -> &mut Self {
+857        self.minimum_change = value;
+858        self
+859    }
+860}
+861
+862impl Default for MtuDiscoveryConfig {
+863    fn default() -> Self {
+864        Self {
+865            interval: Duration::from_secs(600),
+866            upper_bound: 1452,
+867            black_hole_cooldown: Duration::from_secs(60),
+868            minimum_change: 20,
+869        }
+870    }
+871}
+872
+873/// Maximum duration of inactivity to accept before timing out the connection
+874///
+875/// This wraps an underlying [`VarInt`], representing the duration in milliseconds. Values can be
+876/// constructed by converting directly from `VarInt`, or using `TryFrom<Duration>`.
+877///
+878/// ```
+879/// # use std::{convert::TryFrom, time::Duration};
+880/// # use iroh_quinn_proto::{IdleTimeout, VarIntBoundsExceeded, VarInt};
+881/// # fn main() -> Result<(), VarIntBoundsExceeded> {
+882/// // A `VarInt`-encoded value in milliseconds
+883/// let timeout = IdleTimeout::from(VarInt::from_u32(10_000));
+884///
+885/// // Try to convert a `Duration` into a `VarInt`-encoded timeout
+886/// let timeout = IdleTimeout::try_from(Duration::from_secs(10))?;
+887/// # Ok(())
+888/// # }
+889/// ```
+890#[derive(Default, Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
+891pub struct IdleTimeout(VarInt);
+892
+893impl From<VarInt> for IdleTimeout {
+894    fn from(inner: VarInt) -> Self {
+895        Self(inner)
+896    }
+897}
+898
+899impl std::convert::TryFrom<Duration> for IdleTimeout {
+900    type Error = VarIntBoundsExceeded;
+901
+902    fn try_from(timeout: Duration) -> Result<Self, Self::Error> {
+903        let inner = VarInt::try_from(timeout.as_millis())?;
+904        Ok(Self(inner))
+905    }
+906}
+907
+908impl fmt::Debug for IdleTimeout {
+909    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+910        self.0.fmt(f)
+911    }
+912}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion.rs.html new file mode 100644 index 000000000..4c4feb6bf --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion.rs.html @@ -0,0 +1,112 @@ +congestion.rs - source

iroh_quinn_proto/
congestion.rs

1//! Logic for controlling the rate at which data is sent
+2
+3use crate::Instant;
+4use crate::connection::RttEstimator;
+5use std::any::Any;
+6use std::sync::Arc;
+7
+8mod bbr;
+9mod cubic;
+10mod new_reno;
+11
+12pub use bbr::{Bbr, BbrConfig};
+13pub use cubic::{Cubic, CubicConfig};
+14pub use new_reno::{NewReno, NewRenoConfig};
+15
+16/// Common interface for different congestion controllers
+17pub trait Controller: Send + Sync + std::fmt::Debug {
+18    /// One or more packets were just sent
+19    #[allow(unused_variables)]
+20    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {}
+21
+22    /// Packet deliveries were confirmed
+23    ///
+24    /// `app_limited` indicates whether the connection was blocked on outgoing
+25    /// application data prior to receiving these acknowledgements.
+26    #[allow(unused_variables)]
+27    fn on_ack(
+28        &mut self,
+29        now: Instant,
+30        sent: Instant,
+31        bytes: u64,
+32        app_limited: bool,
+33        rtt: &RttEstimator,
+34    ) {
+35    }
+36
+37    /// Packets are acked in batches, all with the same `now` argument. This indicates one of those batches has completed.
+38    #[allow(unused_variables)]
+39    fn on_end_acks(
+40        &mut self,
+41        now: Instant,
+42        in_flight: u64,
+43        app_limited: bool,
+44        largest_packet_num_acked: Option<u64>,
+45    ) {
+46    }
+47
+48    /// Packets were deemed lost or marked congested
+49    ///
+50    /// `in_persistent_congestion` indicates whether all packets sent within the persistent
+51    /// congestion threshold period ending when the most recent packet in this batch was sent were
+52    /// lost.
+53    /// `lost_bytes` indicates how many bytes were lost. This value will be 0 for ECN triggers.
+54    fn on_congestion_event(
+55        &mut self,
+56        now: Instant,
+57        sent: Instant,
+58        is_persistent_congestion: bool,
+59        is_ecn: bool,
+60        lost_bytes: u64,
+61    );
+62
+63    /// Packets were incorrectly deemed lost
+64    ///
+65    /// This function is called when all packets that were deemed lost (for instance because
+66    /// of packet reordering) are acknowledged after the congestion event was raised.
+67    fn on_spurious_congestion_event(&mut self) {}
+68
+69    /// The known MTU for the current network path has been updated
+70    fn on_mtu_update(&mut self, new_mtu: u16);
+71
+72    /// Number of ack-eliciting bytes that may be in flight
+73    fn window(&self) -> u64;
+74
+75    /// Retrieve implementation-specific metrics used to populate `qlog` traces when they are enabled
+76    fn metrics(&self) -> ControllerMetrics {
+77        ControllerMetrics {
+78            congestion_window: self.window(),
+79            ssthresh: None,
+80            pacing_rate: None,
+81        }
+82    }
+83
+84    /// Duplicate the controller's state
+85    fn clone_box(&self) -> Box<dyn Controller>;
+86
+87    /// Initial congestion window
+88    fn initial_window(&self) -> u64;
+89
+90    /// Returns Self for use in down-casting to extract implementation details
+91    fn into_any(self: Box<Self>) -> Box<dyn Any>;
+92}
+93
+94/// Common congestion controller metrics
+95#[derive(Default, Debug)]
+96#[non_exhaustive]
+97pub struct ControllerMetrics {
+98    /// Congestion window (bytes)
+99    pub congestion_window: u64,
+100    /// Slow start threshold (bytes)
+101    pub ssthresh: Option<u64>,
+102    /// Pacing rate (bits/s)
+103    pub pacing_rate: Option<u64>,
+104}
+105
+106/// Constructs controllers on demand
+107pub trait ControllerFactory {
+108    /// Construct a fresh `Controller`
+109    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller>;
+110}
+111
+112const BASE_DATAGRAM_SIZE: u64 = 1200;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html new file mode 100644 index 000000000..1c23f5cd5 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/bw_estimation.rs.html @@ -0,0 +1,101 @@ +bw_estimation.rs - source

iroh_quinn_proto/congestion/bbr/
bw_estimation.rs

1use std::fmt::{Debug, Display, Formatter};
+2
+3use super::min_max::MinMax;
+4use crate::{Duration, Instant};
+5
+6#[derive(Clone, Debug, Default)]
+7pub(crate) struct BandwidthEstimation {
+8    total_acked: u64,
+9    prev_total_acked: u64,
+10    acked_time: Option<Instant>,
+11    prev_acked_time: Option<Instant>,
+12    total_sent: u64,
+13    prev_total_sent: u64,
+14    sent_time: Option<Instant>,
+15    prev_sent_time: Option<Instant>,
+16    max_filter: MinMax,
+17    acked_at_last_window: u64,
+18}
+19
+20impl BandwidthEstimation {
+21    pub(crate) fn on_sent(&mut self, now: Instant, bytes: u64) {
+22        self.prev_total_sent = self.total_sent;
+23        self.total_sent += bytes;
+24        self.prev_sent_time = self.sent_time;
+25        self.sent_time = Some(now);
+26    }
+27
+28    pub(crate) fn on_ack(
+29        &mut self,
+30        now: Instant,
+31        _sent: Instant,
+32        bytes: u64,
+33        round: u64,
+34        app_limited: bool,
+35    ) {
+36        self.prev_total_acked = self.total_acked;
+37        self.total_acked += bytes;
+38        self.prev_acked_time = self.acked_time;
+39        self.acked_time = Some(now);
+40
+41        let prev_sent_time = match self.prev_sent_time {
+42            Some(prev_sent_time) => prev_sent_time,
+43            None => return,
+44        };
+45
+46        let send_rate = match self.sent_time {
+47            Some(sent_time) if sent_time > prev_sent_time => Self::bw_from_delta(
+48                self.total_sent - self.prev_total_sent,
+49                sent_time - prev_sent_time,
+50            )
+51            .unwrap_or(0),
+52            _ => u64::MAX, // will take the min of send and ack, so this is just a skip
+53        };
+54
+55        let ack_rate = match self.prev_acked_time {
+56            Some(prev_acked_time) => Self::bw_from_delta(
+57                self.total_acked - self.prev_total_acked,
+58                now - prev_acked_time,
+59            )
+60            .unwrap_or(0),
+61            None => 0,
+62        };
+63
+64        let bandwidth = send_rate.min(ack_rate);
+65        if !app_limited && self.max_filter.get() < bandwidth {
+66            self.max_filter.update_max(round, bandwidth);
+67        }
+68    }
+69
+70    pub(crate) fn bytes_acked_this_window(&self) -> u64 {
+71        self.total_acked - self.acked_at_last_window
+72    }
+73
+74    pub(crate) fn end_acks(&mut self, _current_round: u64, _app_limited: bool) {
+75        self.acked_at_last_window = self.total_acked;
+76    }
+77
+78    pub(crate) fn get_estimate(&self) -> u64 {
+79        self.max_filter.get()
+80    }
+81
+82    pub(crate) const fn bw_from_delta(bytes: u64, delta: Duration) -> Option<u64> {
+83        let window_duration_ns = delta.as_nanos();
+84        if window_duration_ns == 0 {
+85            return None;
+86        }
+87        let b_ns = bytes * 1_000_000_000;
+88        let bytes_per_second = b_ns / (window_duration_ns as u64);
+89        Some(bytes_per_second)
+90    }
+91}
+92
+93impl Display for BandwidthEstimation {
+94    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+95        write!(
+96            f,
+97            "{:.3} MB/s",
+98            self.get_estimate() as f32 / (1024 * 1024) as f32
+99        )
+100    }
+101}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html new file mode 100644 index 000000000..a64b972e4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/min_max.rs.html @@ -0,0 +1,152 @@ +min_max.rs - source

iroh_quinn_proto/congestion/bbr/
min_max.rs

1/*
+2 * Based on Google code released under BSD license here:
+3 * https://groups.google.com/forum/#!topic/bbr-dev/3RTgkzi5ZD8
+4 */
+5
+6/*
+7 * Kathleen Nichols' algorithm for tracking the minimum (or maximum)
+8 * value of a data stream over some fixed time interval.  (E.g.,
+9 * the minimum RTT over the past five minutes.) It uses constant
+10 * space and constant time per update yet almost always delivers
+11 * the same minimum as an implementation that has to keep all the
+12 * data in the window.
+13 *
+14 * The algorithm keeps track of the best, 2nd best & 3rd best min
+15 * values, maintaining an invariant that the measurement time of
+16 * the n'th best >= n-1'th best. It also makes sure that the three
+17 * values are widely separated in the time window since that bounds
+18 * the worse case error when that data is monotonically increasing
+19 * over the window.
+20 *
+21 * Upon getting a new min, we can forget everything earlier because
+22 * it has no value - the new min is <= everything else in the window
+23 * by definition and it samples the most recent. So we restart fresh on
+24 * every new min and overwrites 2nd & 3rd choices. The same property
+25 * holds for 2nd & 3rd best.
+26 */
+27
+28use std::fmt::Debug;
+29
+30#[derive(Copy, Clone, Debug)]
+31pub(super) struct MinMax {
+32    /// round count, not a timestamp
+33    window: u64,
+34    samples: [MinMaxSample; 3],
+35}
+36
+37impl MinMax {
+38    pub(super) fn get(&self) -> u64 {
+39        self.samples[0].value
+40    }
+41
+42    fn fill(&mut self, sample: MinMaxSample) {
+43        self.samples.fill(sample);
+44    }
+45
+46    pub(super) fn reset(&mut self) {
+47        self.fill(Default::default())
+48    }
+49
+50    /// update_min is also defined in the original source, but removed here since it is not used.
+51    pub(super) fn update_max(&mut self, current_round: u64, measurement: u64) {
+52        let sample = MinMaxSample {
+53            time: current_round,
+54            value: measurement,
+55        };
+56
+57        if self.samples[0].value == 0  /* uninitialised */
+58            || /* found new max? */ sample.value >= self.samples[0].value
+59            || /* nothing left in window? */ sample.time - self.samples[2].time > self.window
+60        {
+61            self.fill(sample); /* forget earlier samples */
+62            return;
+63        }
+64
+65        if sample.value >= self.samples[1].value {
+66            self.samples[2] = sample;
+67            self.samples[1] = sample;
+68        } else if sample.value >= self.samples[2].value {
+69            self.samples[2] = sample;
+70        }
+71
+72        self.subwin_update(sample);
+73    }
+74
+75    /* As time advances, update the 1st, 2nd, and 3rd choices. */
+76    fn subwin_update(&mut self, sample: MinMaxSample) {
+77        let dt = sample.time - self.samples[0].time;
+78        if dt > self.window {
+79            /*
+80             * Passed entire window without a new sample so make 2nd
+81             * choice the new sample & 3rd choice the new 2nd choice.
+82             * we may have to iterate this since our 2nd choice
+83             * may also be outside the window (we checked on entry
+84             * that the third choice was in the window).
+85             */
+86            self.samples[0] = self.samples[1];
+87            self.samples[1] = self.samples[2];
+88            self.samples[2] = sample;
+89            if sample.time - self.samples[0].time > self.window {
+90                self.samples[0] = self.samples[1];
+91                self.samples[1] = self.samples[2];
+92                self.samples[2] = sample;
+93            }
+94        } else if self.samples[1].time == self.samples[0].time && dt > self.window / 4 {
+95            /*
+96             * We've passed a quarter of the window without a new sample
+97             * so take a 2nd choice from the 2nd quarter of the window.
+98             */
+99            self.samples[2] = sample;
+100            self.samples[1] = sample;
+101        } else if self.samples[2].time == self.samples[1].time && dt > self.window / 2 {
+102            /*
+103             * We've passed half the window without finding a new sample
+104             * so take a 3rd choice from the last half of the window
+105             */
+106            self.samples[2] = sample;
+107        }
+108    }
+109}
+110
+111impl Default for MinMax {
+112    fn default() -> Self {
+113        Self {
+114            window: 10,
+115            samples: [Default::default(); 3],
+116        }
+117    }
+118}
+119
+120#[derive(Debug, Copy, Clone, Default)]
+121struct MinMaxSample {
+122    /// round number, not a timestamp
+123    time: u64,
+124    value: u64,
+125}
+126
+127#[cfg(test)]
+128mod test {
+129    use super::*;
+130
+131    #[test]
+132    fn test() {
+133        let round = 25;
+134        let mut min_max = MinMax::default();
+135        min_max.update_max(round + 1, 100);
+136        assert_eq!(100, min_max.get());
+137        min_max.update_max(round + 3, 120);
+138        assert_eq!(120, min_max.get());
+139        min_max.update_max(round + 5, 160);
+140        assert_eq!(160, min_max.get());
+141        min_max.update_max(round + 7, 100);
+142        assert_eq!(160, min_max.get());
+143        min_max.update_max(round + 10, 100);
+144        assert_eq!(160, min_max.get());
+145        min_max.update_max(round + 14, 100);
+146        assert_eq!(160, min_max.get());
+147        min_max.update_max(round + 16, 100);
+148        assert_eq!(100, min_max.get());
+149        min_max.update_max(round + 18, 130);
+150        assert_eq!(130, min_max.get());
+151    }
+152}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html new file mode 100644 index 000000000..5290f93a7 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion/bbr/mod.rs.html @@ -0,0 +1,647 @@ +mod.rs - source

iroh_quinn_proto/congestion/bbr/
mod.rs

1use std::any::Any;
+2use std::fmt::Debug;
+3use std::sync::Arc;
+4
+5use rand::{Rng, SeedableRng};
+6
+7use crate::congestion::ControllerMetrics;
+8use crate::congestion::bbr::bw_estimation::BandwidthEstimation;
+9use crate::congestion::bbr::min_max::MinMax;
+10use crate::connection::RttEstimator;
+11use crate::{Duration, Instant};
+12
+13use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+14
+15mod bw_estimation;
+16mod min_max;
+17
+18/// Experimental! Use at your own risk.
+19///
+20/// Aims for reduced buffer bloat and improved performance over high bandwidth-delay product networks.
+21/// Based on google's quiche implementation <https://source.chromium.org/chromium/chromium/src/+/master:net/third_party/quiche/src/quic/core/congestion_control/bbr_sender.cc>
+22/// of BBR <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control>.
+23/// More discussion and links at <https://groups.google.com/g/bbr-dev>.
+24#[derive(Debug, Clone)]
+25pub struct Bbr {
+26    config: Arc<BbrConfig>,
+27    current_mtu: u64,
+28    max_bandwidth: BandwidthEstimation,
+29    acked_bytes: u64,
+30    mode: Mode,
+31    loss_state: LossState,
+32    recovery_state: RecoveryState,
+33    recovery_window: u64,
+34    is_at_full_bandwidth: bool,
+35    pacing_gain: f32,
+36    high_gain: f32,
+37    drain_gain: f32,
+38    cwnd_gain: f32,
+39    high_cwnd_gain: f32,
+40    last_cycle_start: Option<Instant>,
+41    current_cycle_offset: u8,
+42    init_cwnd: u64,
+43    min_cwnd: u64,
+44    prev_in_flight_count: u64,
+45    exit_probe_rtt_at: Option<Instant>,
+46    probe_rtt_last_started_at: Option<Instant>,
+47    min_rtt: Duration,
+48    exiting_quiescence: bool,
+49    pacing_rate: u64,
+50    max_acked_packet_number: u64,
+51    max_sent_packet_number: u64,
+52    end_recovery_at_packet_number: u64,
+53    cwnd: u64,
+54    current_round_trip_end_packet_number: u64,
+55    round_count: u64,
+56    bw_at_last_round: u64,
+57    round_wo_bw_gain: u64,
+58    ack_aggregation: AckAggregationState,
+59    random_number_generator: rand::rngs::StdRng,
+60}
+61
+62impl Bbr {
+63    /// Construct a state using the given `config` and current time `now`
+64    pub fn new(config: Arc<BbrConfig>, current_mtu: u16) -> Self {
+65        let initial_window = config.initial_window;
+66        Self {
+67            config,
+68            current_mtu: current_mtu as u64,
+69            max_bandwidth: BandwidthEstimation::default(),
+70            acked_bytes: 0,
+71            mode: Mode::Startup,
+72            loss_state: Default::default(),
+73            recovery_state: RecoveryState::NotInRecovery,
+74            recovery_window: 0,
+75            is_at_full_bandwidth: false,
+76            pacing_gain: K_DEFAULT_HIGH_GAIN,
+77            high_gain: K_DEFAULT_HIGH_GAIN,
+78            drain_gain: 1.0 / K_DEFAULT_HIGH_GAIN,
+79            cwnd_gain: K_DEFAULT_HIGH_GAIN,
+80            high_cwnd_gain: K_DEFAULT_HIGH_GAIN,
+81            last_cycle_start: None,
+82            current_cycle_offset: 0,
+83            init_cwnd: initial_window,
+84            min_cwnd: calculate_min_window(current_mtu as u64),
+85            prev_in_flight_count: 0,
+86            exit_probe_rtt_at: None,
+87            probe_rtt_last_started_at: None,
+88            min_rtt: Default::default(),
+89            exiting_quiescence: false,
+90            pacing_rate: 0,
+91            max_acked_packet_number: 0,
+92            max_sent_packet_number: 0,
+93            end_recovery_at_packet_number: 0,
+94            cwnd: initial_window,
+95            current_round_trip_end_packet_number: 0,
+96            round_count: 0,
+97            bw_at_last_round: 0,
+98            round_wo_bw_gain: 0,
+99            ack_aggregation: AckAggregationState::default(),
+100            random_number_generator: rand::rngs::StdRng::from_os_rng(),
+101        }
+102    }
+103
+104    fn enter_startup_mode(&mut self) {
+105        self.mode = Mode::Startup;
+106        self.pacing_gain = self.high_gain;
+107        self.cwnd_gain = self.high_cwnd_gain;
+108    }
+109
+110    fn enter_probe_bandwidth_mode(&mut self, now: Instant) {
+111        self.mode = Mode::ProbeBw;
+112        self.cwnd_gain = K_DERIVED_HIGH_CWNDGAIN;
+113        self.last_cycle_start = Some(now);
+114        // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is
+115        // excluded because in that case increased gain and decreased gain would not
+116        // follow each other.
+117        let mut rand_index = self
+118            .random_number_generator
+119            .random_range(0..K_PACING_GAIN.len() as u8 - 1);
+120        if rand_index >= 1 {
+121            rand_index += 1;
+122        }
+123        self.current_cycle_offset = rand_index;
+124        self.pacing_gain = K_PACING_GAIN[rand_index as usize];
+125    }
+126
+127    fn update_recovery_state(&mut self, is_round_start: bool) {
+128        // Exit recovery when there are no losses for a round.
+129        if self.loss_state.has_losses() {
+130            self.end_recovery_at_packet_number = self.max_sent_packet_number;
+131        }
+132        match self.recovery_state {
+133            // Enter conservation on the first loss.
+134            RecoveryState::NotInRecovery if self.loss_state.has_losses() => {
+135                self.recovery_state = RecoveryState::Conservation;
+136                // This will cause the |recovery_window| to be set to the
+137                // correct value in CalculateRecoveryWindow().
+138                self.recovery_window = 0;
+139                // Since the conservation phase is meant to be lasting for a whole
+140                // round, extend the current round as if it were started right now.
+141                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+142            }
+143            RecoveryState::Growth | RecoveryState::Conservation => {
+144                if self.recovery_state == RecoveryState::Conservation && is_round_start {
+145                    self.recovery_state = RecoveryState::Growth;
+146                }
+147                // Exit recovery if appropriate.
+148                if !self.loss_state.has_losses()
+149                    && self.max_acked_packet_number > self.end_recovery_at_packet_number
+150                {
+151                    self.recovery_state = RecoveryState::NotInRecovery;
+152                }
+153            }
+154            _ => {}
+155        }
+156    }
+157
+158    fn update_gain_cycle_phase(&mut self, now: Instant, in_flight: u64) {
+159        // In most cases, the cycle is advanced after an RTT passes.
+160        let mut should_advance_gain_cycling = self
+161            .last_cycle_start
+162            .map(|last_cycle_start| now.duration_since(last_cycle_start) > self.min_rtt)
+163            .unwrap_or(false);
+164        // If the pacing gain is above 1.0, the connection is trying to probe the
+165        // bandwidth by increasing the number of bytes in flight to at least
+166        // pacing_gain * BDP.  Make sure that it actually reaches the target, as
+167        // long as there are no losses suggesting that the buffers are not able to
+168        // hold that much.
+169        if self.pacing_gain > 1.0
+170            && !self.loss_state.has_losses()
+171            && self.prev_in_flight_count < self.get_target_cwnd(self.pacing_gain)
+172        {
+173            should_advance_gain_cycling = false;
+174        }
+175
+176        // If pacing gain is below 1.0, the connection is trying to drain the extra
+177        // queue which could have been incurred by probing prior to it.  If the
+178        // number of bytes in flight falls down to the estimated BDP value earlier,
+179        // conclude that the queue has been successfully drained and exit this cycle
+180        // early.
+181        if self.pacing_gain < 1.0 && in_flight <= self.get_target_cwnd(1.0) {
+182            should_advance_gain_cycling = true;
+183        }
+184
+185        if should_advance_gain_cycling {
+186            self.current_cycle_offset = (self.current_cycle_offset + 1) % K_PACING_GAIN.len() as u8;
+187            self.last_cycle_start = Some(now);
+188            // Stay in low gain mode until the target BDP is hit.  Low gain mode
+189            // will be exited immediately when the target BDP is achieved.
+190            if DRAIN_TO_TARGET
+191                && self.pacing_gain < 1.0
+192                && (K_PACING_GAIN[self.current_cycle_offset as usize] - 1.0).abs() < f32::EPSILON
+193                && in_flight > self.get_target_cwnd(1.0)
+194            {
+195                return;
+196            }
+197            self.pacing_gain = K_PACING_GAIN[self.current_cycle_offset as usize];
+198        }
+199    }
+200
+201    fn maybe_exit_startup_or_drain(&mut self, now: Instant, in_flight: u64) {
+202        if self.mode == Mode::Startup && self.is_at_full_bandwidth {
+203            self.mode = Mode::Drain;
+204            self.pacing_gain = self.drain_gain;
+205            self.cwnd_gain = self.high_cwnd_gain;
+206        }
+207        if self.mode == Mode::Drain && in_flight <= self.get_target_cwnd(1.0) {
+208            self.enter_probe_bandwidth_mode(now);
+209        }
+210    }
+211
+212    fn is_min_rtt_expired(&self, now: Instant, app_limited: bool) -> bool {
+213        !app_limited
+214            && self
+215                .probe_rtt_last_started_at
+216                .map(|last| now.saturating_duration_since(last) > Duration::from_secs(10))
+217                .unwrap_or(true)
+218    }
+219
+220    fn maybe_enter_or_exit_probe_rtt(
+221        &mut self,
+222        now: Instant,
+223        is_round_start: bool,
+224        bytes_in_flight: u64,
+225        app_limited: bool,
+226    ) {
+227        let min_rtt_expired = self.is_min_rtt_expired(now, app_limited);
+228        if min_rtt_expired && !self.exiting_quiescence && self.mode != Mode::ProbeRtt {
+229            self.mode = Mode::ProbeRtt;
+230            self.pacing_gain = 1.0;
+231            // Do not decide on the time to exit ProbeRtt until the
+232            // |bytes_in_flight| is at the target small value.
+233            self.exit_probe_rtt_at = None;
+234            self.probe_rtt_last_started_at = Some(now);
+235        }
+236
+237        if self.mode == Mode::ProbeRtt {
+238            if self.exit_probe_rtt_at.is_none() {
+239                // If the window has reached the appropriate size, schedule exiting
+240                // ProbeRtt.  The CWND during ProbeRtt is
+241                // kMinimumCongestionWindow, but we allow an extra packet since QUIC
+242                // checks CWND before sending a packet.
+243                if bytes_in_flight < self.get_probe_rtt_cwnd() + self.current_mtu {
+244                    const K_PROBE_RTT_TIME: Duration = Duration::from_millis(200);
+245                    self.exit_probe_rtt_at = Some(now + K_PROBE_RTT_TIME);
+246                }
+247            } else if is_round_start && now >= self.exit_probe_rtt_at.unwrap() {
+248                if !self.is_at_full_bandwidth {
+249                    self.enter_startup_mode();
+250                } else {
+251                    self.enter_probe_bandwidth_mode(now);
+252                }
+253            }
+254        }
+255
+256        self.exiting_quiescence = false;
+257    }
+258
+259    fn get_target_cwnd(&self, gain: f32) -> u64 {
+260        let bw = self.max_bandwidth.get_estimate();
+261        let bdp = self.min_rtt.as_micros() as u64 * bw;
+262        let bdpf = bdp as f64;
+263        let cwnd = ((gain as f64 * bdpf) / 1_000_000f64) as u64;
+264        // BDP estimate will be zero if no bandwidth samples are available yet.
+265        if cwnd == 0 {
+266            return self.init_cwnd;
+267        }
+268        cwnd.max(self.min_cwnd)
+269    }
+270
+271    fn get_probe_rtt_cwnd(&self) -> u64 {
+272        const K_MODERATE_PROBE_RTT_MULTIPLIER: f32 = 0.75;
+273        if PROBE_RTT_BASED_ON_BDP {
+274            return self.get_target_cwnd(K_MODERATE_PROBE_RTT_MULTIPLIER);
+275        }
+276        self.min_cwnd
+277    }
+278
+279    fn calculate_pacing_rate(&mut self) {
+280        let bw = self.max_bandwidth.get_estimate();
+281        if bw == 0 {
+282            return;
+283        }
+284        let target_rate = (bw as f64 * self.pacing_gain as f64) as u64;
+285        if self.is_at_full_bandwidth {
+286            self.pacing_rate = target_rate;
+287            return;
+288        }
+289
+290        // Pace at the rate of initial_window / RTT as soon as RTT measurements are
+291        // available.
+292        if self.pacing_rate == 0 && self.min_rtt.as_nanos() != 0 {
+293            self.pacing_rate =
+294                BandwidthEstimation::bw_from_delta(self.init_cwnd, self.min_rtt).unwrap();
+295            return;
+296        }
+297
+298        // Do not decrease the pacing rate during startup.
+299        if self.pacing_rate < target_rate {
+300            self.pacing_rate = target_rate;
+301        }
+302    }
+303
+304    fn calculate_cwnd(&mut self, bytes_acked: u64, excess_acked: u64) {
+305        if self.mode == Mode::ProbeRtt {
+306            return;
+307        }
+308        let mut target_window = self.get_target_cwnd(self.cwnd_gain);
+309        if self.is_at_full_bandwidth {
+310            // Add the max recently measured ack aggregation to CWND.
+311            target_window += self.ack_aggregation.max_ack_height.get();
+312        } else {
+313            // Add the most recent excess acked.  Because CWND never decreases in
+314            // STARTUP, this will automatically create a very localized max filter.
+315            target_window += excess_acked;
+316        }
+317        // Instead of immediately setting the target CWND as the new one, BBR grows
+318        // the CWND towards |target_window| by only increasing it |bytes_acked| at a
+319        // time.
+320        if self.is_at_full_bandwidth {
+321            self.cwnd = target_window.min(self.cwnd + bytes_acked);
+322        } else if (self.cwnd_gain < target_window as f32) || (self.acked_bytes < self.init_cwnd) {
+323            // If the connection is not yet out of startup phase, do not decrease
+324            // the window.
+325            self.cwnd += bytes_acked;
+326        }
+327
+328        // Enforce the limits on the congestion window.
+329        if self.cwnd < self.min_cwnd {
+330            self.cwnd = self.min_cwnd;
+331        }
+332    }
+333
+334    fn calculate_recovery_window(&mut self, bytes_acked: u64, bytes_lost: u64, in_flight: u64) {
+335        if !self.recovery_state.in_recovery() {
+336            return;
+337        }
+338        // Set up the initial recovery window.
+339        if self.recovery_window == 0 {
+340            self.recovery_window = self.min_cwnd.max(in_flight + bytes_acked);
+341            return;
+342        }
+343
+344        // Remove losses from the recovery window, while accounting for a potential
+345        // integer underflow.
+346        if self.recovery_window >= bytes_lost {
+347            self.recovery_window -= bytes_lost;
+348        } else {
+349            // k_max_segment_size = current_mtu
+350            self.recovery_window = self.current_mtu;
+351        }
+352        // In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,
+353        // release additional |bytes_acked| to achieve a slow-start-like behavior.
+354        if self.recovery_state == RecoveryState::Growth {
+355            self.recovery_window += bytes_acked;
+356        }
+357
+358        // Sanity checks.  Ensure that we always allow to send at least an MSS or
+359        // |bytes_acked| in response, whichever is larger.
+360        self.recovery_window = self
+361            .recovery_window
+362            .max(in_flight + bytes_acked)
+363            .max(self.min_cwnd);
+364    }
+365
+366    /// <https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control#section-4.3.2.2>
+367    fn check_if_full_bw_reached(&mut self, app_limited: bool) {
+368        if app_limited {
+369            return;
+370        }
+371        let target = (self.bw_at_last_round as f64 * K_STARTUP_GROWTH_TARGET as f64) as u64;
+372        let bw = self.max_bandwidth.get_estimate();
+373        if bw >= target {
+374            self.bw_at_last_round = bw;
+375            self.round_wo_bw_gain = 0;
+376            self.ack_aggregation.max_ack_height.reset();
+377            return;
+378        }
+379
+380        self.round_wo_bw_gain += 1;
+381        if self.round_wo_bw_gain >= K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP as u64
+382            || (self.recovery_state.in_recovery())
+383        {
+384            self.is_at_full_bandwidth = true;
+385        }
+386    }
+387}
+388
+389impl Controller for Bbr {
+390    fn on_sent(&mut self, now: Instant, bytes: u64, last_packet_number: u64) {
+391        self.max_sent_packet_number = last_packet_number;
+392        self.max_bandwidth.on_sent(now, bytes);
+393    }
+394
+395    fn on_ack(
+396        &mut self,
+397        now: Instant,
+398        sent: Instant,
+399        bytes: u64,
+400        app_limited: bool,
+401        rtt: &RttEstimator,
+402    ) {
+403        self.max_bandwidth
+404            .on_ack(now, sent, bytes, self.round_count, app_limited);
+405        self.acked_bytes += bytes;
+406        if self.is_min_rtt_expired(now, app_limited) || self.min_rtt > rtt.min() {
+407            self.min_rtt = rtt.min();
+408        }
+409    }
+410
+411    fn on_end_acks(
+412        &mut self,
+413        now: Instant,
+414        in_flight: u64,
+415        app_limited: bool,
+416        largest_packet_num_acked: Option<u64>,
+417    ) {
+418        let bytes_acked = self.max_bandwidth.bytes_acked_this_window();
+419        let excess_acked = self.ack_aggregation.update_ack_aggregation_bytes(
+420            bytes_acked,
+421            now,
+422            self.round_count,
+423            self.max_bandwidth.get_estimate(),
+424        );
+425        self.max_bandwidth.end_acks(self.round_count, app_limited);
+426        if let Some(largest_acked_packet) = largest_packet_num_acked {
+427            self.max_acked_packet_number = largest_acked_packet;
+428        }
+429
+430        let mut is_round_start = false;
+431        if bytes_acked > 0 {
+432            is_round_start =
+433                self.max_acked_packet_number > self.current_round_trip_end_packet_number;
+434            if is_round_start {
+435                self.current_round_trip_end_packet_number = self.max_sent_packet_number;
+436                self.round_count += 1;
+437            }
+438        }
+439
+440        self.update_recovery_state(is_round_start);
+441
+442        if self.mode == Mode::ProbeBw {
+443            self.update_gain_cycle_phase(now, in_flight);
+444        }
+445
+446        if is_round_start && !self.is_at_full_bandwidth {
+447            self.check_if_full_bw_reached(app_limited);
+448        }
+449
+450        self.maybe_exit_startup_or_drain(now, in_flight);
+451
+452        self.maybe_enter_or_exit_probe_rtt(now, is_round_start, in_flight, app_limited);
+453
+454        // After the model is updated, recalculate the pacing rate and congestion window.
+455        self.calculate_pacing_rate();
+456        self.calculate_cwnd(bytes_acked, excess_acked);
+457        self.calculate_recovery_window(bytes_acked, self.loss_state.lost_bytes, in_flight);
+458
+459        self.prev_in_flight_count = in_flight;
+460        self.loss_state.reset();
+461    }
+462
+463    fn on_congestion_event(
+464        &mut self,
+465        _now: Instant,
+466        _sent: Instant,
+467        _is_persistent_congestion: bool,
+468        _is_ecn: bool,
+469        lost_bytes: u64,
+470    ) {
+471        self.loss_state.lost_bytes += lost_bytes;
+472    }
+473
+474    fn on_mtu_update(&mut self, new_mtu: u16) {
+475        self.current_mtu = new_mtu as u64;
+476        self.min_cwnd = calculate_min_window(self.current_mtu);
+477        self.init_cwnd = self.config.initial_window.max(self.min_cwnd);
+478        self.cwnd = self.cwnd.max(self.min_cwnd);
+479    }
+480
+481    fn window(&self) -> u64 {
+482        if self.mode == Mode::ProbeRtt {
+483            return self.get_probe_rtt_cwnd();
+484        } else if self.recovery_state.in_recovery() && self.mode != Mode::Startup {
+485            return self.cwnd.min(self.recovery_window);
+486        }
+487        self.cwnd
+488    }
+489
+490    fn metrics(&self) -> ControllerMetrics {
+491        ControllerMetrics {
+492            congestion_window: self.window(),
+493            ssthresh: None,
+494            pacing_rate: Some(self.pacing_rate * 8),
+495        }
+496    }
+497
+498    fn clone_box(&self) -> Box<dyn Controller> {
+499        Box::new(self.clone())
+500    }
+501
+502    fn initial_window(&self) -> u64 {
+503        self.config.initial_window
+504    }
+505
+506    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+507        self
+508    }
+509}
+510
+511/// Configuration for the [`Bbr`] congestion controller
+512#[derive(Debug, Clone)]
+513pub struct BbrConfig {
+514    initial_window: u64,
+515}
+516
+517impl BbrConfig {
+518    /// Default limit on the amount of outstanding data in bytes.
+519    ///
+520    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+521    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+522        self.initial_window = value;
+523        self
+524    }
+525}
+526
+527impl Default for BbrConfig {
+528    fn default() -> Self {
+529        Self {
+530            initial_window: K_MAX_INITIAL_CONGESTION_WINDOW * BASE_DATAGRAM_SIZE,
+531        }
+532    }
+533}
+534
+535impl ControllerFactory for BbrConfig {
+536    fn build(self: Arc<Self>, _now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+537        Box::new(Bbr::new(self, current_mtu))
+538    }
+539}
+540
+541#[derive(Debug, Default, Copy, Clone)]
+542struct AckAggregationState {
+543    max_ack_height: MinMax,
+544    aggregation_epoch_start_time: Option<Instant>,
+545    aggregation_epoch_bytes: u64,
+546}
+547
+548impl AckAggregationState {
+549    fn update_ack_aggregation_bytes(
+550        &mut self,
+551        newly_acked_bytes: u64,
+552        now: Instant,
+553        round: u64,
+554        max_bandwidth: u64,
+555    ) -> u64 {
+556        // Compute how many bytes are expected to be delivered, assuming max
+557        // bandwidth is correct.
+558        let expected_bytes_acked = max_bandwidth
+559            * now
+560                .saturating_duration_since(self.aggregation_epoch_start_time.unwrap_or(now))
+561                .as_micros() as u64
+562            / 1_000_000;
+563
+564        // Reset the current aggregation epoch as soon as the ack arrival rate is
+565        // less than or equal to the max bandwidth.
+566        if self.aggregation_epoch_bytes <= expected_bytes_acked {
+567            // Reset to start measuring a new aggregation epoch.
+568            self.aggregation_epoch_bytes = newly_acked_bytes;
+569            self.aggregation_epoch_start_time = Some(now);
+570            return 0;
+571        }
+572
+573        // Compute how many extra bytes were delivered vs max bandwidth.
+574        // Include the bytes most recently acknowledged to account for stretch acks.
+575        self.aggregation_epoch_bytes += newly_acked_bytes;
+576        let diff = self.aggregation_epoch_bytes - expected_bytes_acked;
+577        self.max_ack_height.update_max(round, diff);
+578        diff
+579    }
+580}
+581
+582#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+583enum Mode {
+584    // Startup phase of the connection.
+585    Startup,
+586    // After achieving the highest possible bandwidth during the startup, lower
+587    // the pacing rate in order to drain the queue.
+588    Drain,
+589    // Cruising mode.
+590    ProbeBw,
+591    // Temporarily slow down sending in order to empty the buffer and measure
+592    // the real minimum RTT.
+593    ProbeRtt,
+594}
+595
+596// Indicates how the congestion control limits the amount of bytes in flight.
+597#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+598enum RecoveryState {
+599    // Do not limit.
+600    NotInRecovery,
+601    // Allow an extra outstanding byte for each byte acknowledged.
+602    Conservation,
+603    // Allow two extra outstanding bytes for each byte acknowledged (slow
+604    // start).
+605    Growth,
+606}
+607
+608impl RecoveryState {
+609    pub(super) fn in_recovery(&self) -> bool {
+610        !matches!(self, Self::NotInRecovery)
+611    }
+612}
+613
+614#[derive(Debug, Clone, Default)]
+615struct LossState {
+616    lost_bytes: u64,
+617}
+618
+619impl LossState {
+620    pub(super) fn reset(&mut self) {
+621        self.lost_bytes = 0;
+622    }
+623
+624    pub(super) fn has_losses(&self) -> bool {
+625        self.lost_bytes != 0
+626    }
+627}
+628
+629fn calculate_min_window(current_mtu: u64) -> u64 {
+630    4 * current_mtu
+631}
+632
+633// The gain used for the STARTUP, equal to 2/ln(2).
+634const K_DEFAULT_HIGH_GAIN: f32 = 2.885;
+635// The newly derived CWND gain for STARTUP, 2.
+636const K_DERIVED_HIGH_CWNDGAIN: f32 = 2.0;
+637// The cycle of gains used during the ProbeBw stage.
+638const K_PACING_GAIN: [f32; 8] = [1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+639
+640const K_STARTUP_GROWTH_TARGET: f32 = 1.25;
+641const K_ROUND_TRIPS_WITHOUT_GROWTH_BEFORE_EXITING_STARTUP: u8 = 3;
+642
+643// Do not allow initial congestion window to be greater than 200 packets.
+644const K_MAX_INITIAL_CONGESTION_WINDOW: u64 = 200;
+645
+646const PROBE_RTT_BASED_ON_BDP: bool = true;
+647const DRAIN_TO_TARGET: bool = true;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion/cubic.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion/cubic.rs.html new file mode 100644 index 000000000..d0beb3ee4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion/cubic.rs.html @@ -0,0 +1,299 @@ +cubic.rs - source

iroh_quinn_proto/congestion/
cubic.rs

1use std::any::Any;
+2use std::cmp;
+3use std::sync::Arc;
+4
+5use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+6use crate::connection::RttEstimator;
+7use crate::{Duration, Instant};
+8
+9/// CUBIC Constants.
+10///
+11/// These are recommended value in RFC8312.
+12const BETA_CUBIC: f64 = 0.7;
+13
+14const C: f64 = 0.4;
+15
+16/// CUBIC State Variables.
+17///
+18/// We need to keep those variables across the connection.
+19/// k, w_max are described in the RFC.
+20#[derive(Debug, Default, Clone)]
+21pub(super) struct State {
+22    /// Time period that the cubic function takes to increase the window size to W_max.
+23    k: f64,
+24
+25    /// Congestion window size when the last congestion event occurred.
+26    w_max: f64,
+27
+28    /// Congestion window increment stored during congestion avoidance.
+29    cwnd_inc: u64,
+30
+31    /// Maximum number of bytes in flight that may be sent.
+32    window: u64,
+33
+34    /// Slow start threshold in bytes.
+35    ///
+36    /// When the congestion window is below ssthresh, the mode is slow start
+37    /// and the window grows by the number of bytes acknowledged.
+38    ssthresh: u64,
+39
+40    /// The time when QUIC first detects a loss, causing it to enter recovery. When a packet sent
+41    /// after this time is acknowledged, QUIC exits recovery.
+42    recovery_start_time: Option<Instant>,
+43}
+44
+45/// CUBIC Functions.
+46///
+47/// Note that these calculations are based on a count of cwnd as bytes,
+48/// not packets.
+49/// Unit of t (duration) and RTT are based on seconds (f64).
+50impl State {
+51    // K = cbrt(w_max * (1 - beta_cubic) / C) (Eq. 2)
+52    fn cubic_k(&self, max_datagram_size: u64) -> f64 {
+53        let w_max = self.w_max / max_datagram_size as f64;
+54        (w_max * (1.0 - BETA_CUBIC) / C).cbrt()
+55    }
+56
+57    // W_cubic(t) = C * (t - K)^3 + w_max (Eq. 1)
+58    fn w_cubic(&self, t: Duration, max_datagram_size: u64) -> f64 {
+59        let w_max = self.w_max / max_datagram_size as f64;
+60
+61        (C * (t.as_secs_f64() - self.k).powi(3) + w_max) * max_datagram_size as f64
+62    }
+63
+64    // W_est(t) = w_max * beta_cubic + 3 * (1 - beta_cubic) / (1 + beta_cubic) *
+65    // (t / RTT) (Eq. 4)
+66    fn w_est(&self, t: Duration, rtt: Duration, max_datagram_size: u64) -> f64 {
+67        let w_max = self.w_max / max_datagram_size as f64;
+68        (w_max * BETA_CUBIC
+69            + 3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC) * t.as_secs_f64() / rtt.as_secs_f64())
+70            * max_datagram_size as f64
+71    }
+72}
+73
+74/// The RFC8312 congestion controller, as widely used for TCP
+75#[derive(Debug, Clone)]
+76pub struct Cubic {
+77    config: Arc<CubicConfig>,
+78    current_mtu: u64,
+79    state: State,
+80    /// Copy of the controller state to restore when a spurious congestion event is detected.
+81    pre_congestion_state: Option<State>,
+82}
+83
+84impl Cubic {
+85    /// Construct a state using the given `config` and current time `now`
+86    pub fn new(config: Arc<CubicConfig>, _now: Instant, current_mtu: u16) -> Self {
+87        Self {
+88            state: State {
+89                window: config.initial_window,
+90                ssthresh: u64::MAX,
+91                ..Default::default()
+92            },
+93            current_mtu: current_mtu as u64,
+94            pre_congestion_state: None,
+95            config,
+96        }
+97    }
+98
+99    fn minimum_window(&self) -> u64 {
+100        2 * self.current_mtu
+101    }
+102}
+103
+104impl Controller for Cubic {
+105    fn on_ack(
+106        &mut self,
+107        now: Instant,
+108        sent: Instant,
+109        bytes: u64,
+110        app_limited: bool,
+111        rtt: &RttEstimator,
+112    ) {
+113        if app_limited
+114            || self
+115                .state
+116                .recovery_start_time
+117                .map(|recovery_start_time| sent <= recovery_start_time)
+118                .unwrap_or(false)
+119        {
+120            return;
+121        }
+122
+123        if self.state.window < self.state.ssthresh {
+124            // Slow start
+125            self.state.window += bytes;
+126        } else {
+127            // Congestion avoidance.
+128            let ca_start_time;
+129
+130            match self.state.recovery_start_time {
+131                Some(t) => ca_start_time = t,
+132                None => {
+133                    // When we come here without congestion_event() triggered,
+134                    // initialize congestion_recovery_start_time, w_max and k.
+135                    ca_start_time = now;
+136                    self.state.recovery_start_time = Some(now);
+137
+138                    self.state.w_max = self.state.window as f64;
+139                    self.state.k = 0.0;
+140                }
+141            }
+142
+143            let t = now - ca_start_time;
+144
+145            // w_cubic(t + rtt)
+146            let w_cubic = self.state.w_cubic(t + rtt.get(), self.current_mtu);
+147
+148            // w_est(t)
+149            let w_est = self.state.w_est(t, rtt.get(), self.current_mtu);
+150
+151            let mut cubic_cwnd = self.state.window;
+152
+153            if w_cubic < w_est {
+154                // TCP friendly region.
+155                cubic_cwnd = cmp::max(cubic_cwnd, w_est as u64);
+156            } else if cubic_cwnd < w_cubic as u64 {
+157                // Concave region or convex region use same increment.
+158                let cubic_inc =
+159                    (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 * self.current_mtu as f64;
+160
+161                cubic_cwnd += cubic_inc as u64;
+162            }
+163
+164            // Update the increment and increase cwnd by MSS.
+165            self.state.cwnd_inc += cubic_cwnd - self.state.window;
+166
+167            // cwnd_inc can be more than 1 MSS in the late stage of max probing.
+168            // however RFC9002 §7.3.3 (Congestion Avoidance) limits
+169            // the increase of cwnd to 1 max_datagram_size per cwnd acknowledged.
+170            if self.state.cwnd_inc >= self.current_mtu {
+171                self.state.window += self.current_mtu;
+172                self.state.cwnd_inc = 0;
+173            }
+174        }
+175    }
+176
+177    fn on_congestion_event(
+178        &mut self,
+179        now: Instant,
+180        sent: Instant,
+181        is_persistent_congestion: bool,
+182        is_ecn: bool,
+183        _lost_bytes: u64,
+184    ) {
+185        if self
+186            .state
+187            .recovery_start_time
+188            .map(|recovery_start_time| sent <= recovery_start_time)
+189            .unwrap_or(false)
+190        {
+191            return;
+192        }
+193
+194        // Save state in case this event ends up being spurious
+195        if !is_ecn {
+196            self.pre_congestion_state = Some(self.state.clone());
+197        }
+198
+199        self.state.recovery_start_time = Some(now);
+200
+201        // Fast convergence
+202        if (self.state.window as f64) < self.state.w_max {
+203            self.state.w_max = self.state.window as f64 * (1.0 + BETA_CUBIC) / 2.0;
+204        } else {
+205            self.state.w_max = self.state.window as f64;
+206        }
+207
+208        self.state.ssthresh = cmp::max(
+209            (self.state.w_max * BETA_CUBIC) as u64,
+210            self.minimum_window(),
+211        );
+212        self.state.window = self.state.ssthresh;
+213        self.state.k = self.state.cubic_k(self.current_mtu);
+214
+215        self.state.cwnd_inc = (self.state.cwnd_inc as f64 * BETA_CUBIC) as u64;
+216
+217        if is_persistent_congestion {
+218            self.state.recovery_start_time = None;
+219            self.state.w_max = self.state.window as f64;
+220
+221            // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC
+222            self.state.ssthresh = cmp::max(
+223                (self.state.window as f64 * BETA_CUBIC) as u64,
+224                self.minimum_window(),
+225            );
+226
+227            self.state.cwnd_inc = 0;
+228
+229            self.state.window = self.minimum_window();
+230        }
+231    }
+232
+233    fn on_spurious_congestion_event(&mut self) {
+234        if let Some(prior_state) = self.pre_congestion_state.take() {
+235            if self.state.window < prior_state.window {
+236                self.state = prior_state;
+237            }
+238        }
+239    }
+240
+241    fn on_mtu_update(&mut self, new_mtu: u16) {
+242        self.current_mtu = new_mtu as u64;
+243        self.state.window = self.state.window.max(self.minimum_window());
+244    }
+245
+246    fn window(&self) -> u64 {
+247        self.state.window
+248    }
+249
+250    fn metrics(&self) -> super::ControllerMetrics {
+251        super::ControllerMetrics {
+252            congestion_window: self.window(),
+253            ssthresh: Some(self.state.ssthresh),
+254            pacing_rate: None,
+255        }
+256    }
+257
+258    fn clone_box(&self) -> Box<dyn Controller> {
+259        Box::new(self.clone())
+260    }
+261
+262    fn initial_window(&self) -> u64 {
+263        self.config.initial_window
+264    }
+265
+266    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+267        self
+268    }
+269}
+270
+271/// Configuration for the `Cubic` congestion controller
+272#[derive(Debug, Clone)]
+273pub struct CubicConfig {
+274    initial_window: u64,
+275}
+276
+277impl CubicConfig {
+278    /// Default limit on the amount of outstanding data in bytes.
+279    ///
+280    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+281    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+282        self.initial_window = value;
+283        self
+284    }
+285}
+286
+287impl Default for CubicConfig {
+288    fn default() -> Self {
+289        Self {
+290            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+291        }
+292    }
+293}
+294
+295impl ControllerFactory for CubicConfig {
+296    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+297        Box::new(Cubic::new(self, now, current_mtu))
+298    }
+299}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html b/pr/234/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html new file mode 100644 index 000000000..c6b1f772b --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/congestion/new_reno.rs.html @@ -0,0 +1,173 @@ +new_reno.rs - source

iroh_quinn_proto/congestion/
new_reno.rs

1use std::any::Any;
+2use std::sync::Arc;
+3
+4use super::{BASE_DATAGRAM_SIZE, Controller, ControllerFactory};
+5use crate::Instant;
+6use crate::connection::RttEstimator;
+7
+8/// A simple, standard congestion controller
+9#[derive(Debug, Clone)]
+10pub struct NewReno {
+11    config: Arc<NewRenoConfig>,
+12    current_mtu: u64,
+13    /// Maximum number of bytes in flight that may be sent.
+14    window: u64,
+15    /// Slow start threshold in bytes. When the congestion window is below ssthresh, the mode is
+16    /// slow start and the window grows by the number of bytes acknowledged.
+17    ssthresh: u64,
+18    /// The time when QUIC first detects a loss, causing it to enter recovery. When a packet sent
+19    /// after this time is acknowledged, QUIC exits recovery.
+20    recovery_start_time: Instant,
+21    /// Bytes which had been acked by the peer since leaving slow start
+22    bytes_acked: u64,
+23}
+24
+25impl NewReno {
+26    /// Construct a state using the given `config` and current time `now`
+27    pub fn new(config: Arc<NewRenoConfig>, now: Instant, current_mtu: u16) -> Self {
+28        Self {
+29            window: config.initial_window,
+30            ssthresh: u64::MAX,
+31            recovery_start_time: now,
+32            current_mtu: current_mtu as u64,
+33            config,
+34            bytes_acked: 0,
+35        }
+36    }
+37
+38    fn minimum_window(&self) -> u64 {
+39        2 * self.current_mtu
+40    }
+41}
+42
+43impl Controller for NewReno {
+44    fn on_ack(
+45        &mut self,
+46        _now: Instant,
+47        sent: Instant,
+48        bytes: u64,
+49        app_limited: bool,
+50        _rtt: &RttEstimator,
+51    ) {
+52        if app_limited || sent <= self.recovery_start_time {
+53            return;
+54        }
+55
+56        if self.window < self.ssthresh {
+57            // Slow start
+58            self.window += bytes;
+59
+60            if self.window >= self.ssthresh {
+61                // Exiting slow start
+62                // Initialize `bytes_acked` for congestion avoidance. The idea
+63                // here is that any bytes over `sshthresh` will already be counted
+64                // towards the congestion avoidance phase - independent of when
+65                // how close to `sshthresh` the `window` was when switching states,
+66                // and independent of datagram sizes.
+67                self.bytes_acked = self.window - self.ssthresh;
+68            }
+69        } else {
+70            // Congestion avoidance
+71            // This implementation uses the method which does not require
+72            // floating point math, which also increases the window by 1 datagram
+73            // for every round trip.
+74            // This mechanism is called Appropriate Byte Counting in
+75            // https://tools.ietf.org/html/rfc3465
+76            self.bytes_acked += bytes;
+77
+78            if self.bytes_acked >= self.window {
+79                self.bytes_acked -= self.window;
+80                self.window += self.current_mtu;
+81            }
+82        }
+83    }
+84
+85    fn on_congestion_event(
+86        &mut self,
+87        now: Instant,
+88        sent: Instant,
+89        is_persistent_congestion: bool,
+90        _is_ecn: bool,
+91        _lost_bytes: u64,
+92    ) {
+93        if sent <= self.recovery_start_time {
+94            return;
+95        }
+96
+97        self.recovery_start_time = now;
+98        self.window = (self.window as f32 * self.config.loss_reduction_factor) as u64;
+99        self.window = self.window.max(self.minimum_window());
+100        self.ssthresh = self.window;
+101
+102        if is_persistent_congestion {
+103            self.window = self.minimum_window();
+104        }
+105    }
+106
+107    fn on_mtu_update(&mut self, new_mtu: u16) {
+108        self.current_mtu = new_mtu as u64;
+109        self.window = self.window.max(self.minimum_window());
+110    }
+111
+112    fn window(&self) -> u64 {
+113        self.window
+114    }
+115
+116    fn metrics(&self) -> super::ControllerMetrics {
+117        super::ControllerMetrics {
+118            congestion_window: self.window(),
+119            ssthresh: Some(self.ssthresh),
+120            pacing_rate: None,
+121        }
+122    }
+123
+124    fn clone_box(&self) -> Box<dyn Controller> {
+125        Box::new(self.clone())
+126    }
+127
+128    fn initial_window(&self) -> u64 {
+129        self.config.initial_window
+130    }
+131
+132    fn into_any(self: Box<Self>) -> Box<dyn Any> {
+133        self
+134    }
+135}
+136
+137/// Configuration for the `NewReno` congestion controller
+138#[derive(Debug, Clone)]
+139pub struct NewRenoConfig {
+140    initial_window: u64,
+141    loss_reduction_factor: f32,
+142}
+143
+144impl NewRenoConfig {
+145    /// Default limit on the amount of outstanding data in bytes.
+146    ///
+147    /// Recommended value: `min(10 * max_datagram_size, max(2 * max_datagram_size, 14720))`
+148    pub fn initial_window(&mut self, value: u64) -> &mut Self {
+149        self.initial_window = value;
+150        self
+151    }
+152
+153    /// Reduction in congestion window when a new loss event is detected.
+154    pub fn loss_reduction_factor(&mut self, value: f32) -> &mut Self {
+155        self.loss_reduction_factor = value;
+156        self
+157    }
+158}
+159
+160impl Default for NewRenoConfig {
+161    fn default() -> Self {
+162        Self {
+163            initial_window: 14720.clamp(2 * BASE_DATAGRAM_SIZE, 10 * BASE_DATAGRAM_SIZE),
+164            loss_reduction_factor: 0.5,
+165        }
+166    }
+167}
+168
+169impl ControllerFactory for NewRenoConfig {
+170    fn build(self: Arc<Self>, now: Instant, current_mtu: u16) -> Box<dyn Controller> {
+171        Box::new(NewReno::new(self, now, current_mtu))
+172    }
+173}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html new file mode 100644 index 000000000..f6c1cd72b --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/ack_frequency.rs.html @@ -0,0 +1,159 @@ +ack_frequency.rs - source

iroh_quinn_proto/connection/
ack_frequency.rs

1use crate::Duration;
+2use crate::frame::AckFrequency;
+3use crate::transport_parameters::TransportParameters;
+4use crate::{AckFrequencyConfig, TIMER_GRANULARITY, TransportError, VarInt};
+5
+6use super::PathId;
+7
+8/// State associated to ACK frequency
+9pub(super) struct AckFrequencyState {
+10    //
+11    // Sending ACK_FREQUENCY frames
+12    //
+13    /// The path ID, packet number and value of the in-flight ACK_FREQUENCY frame
+14    in_flight_ack_frequency_frame: Option<(PathId, u64, Duration)>,
+15    next_outgoing_sequence_number: VarInt,
+16    pub(super) peer_max_ack_delay: Duration,
+17
+18    //
+19    // Receiving ACK_FREQUENCY frames
+20    //
+21    /// The sequence number of the most recently received ACK_FREQUENCY frame
+22    last_ack_frequency_frame: Option<u64>,
+23    pub(super) max_ack_delay: Duration,
+24}
+25
+26impl AckFrequencyState {
+27    pub(super) fn new(default_max_ack_delay: Duration) -> Self {
+28        Self {
+29            in_flight_ack_frequency_frame: None,
+30            next_outgoing_sequence_number: VarInt(0),
+31            peer_max_ack_delay: default_max_ack_delay,
+32
+33            last_ack_frequency_frame: None,
+34            max_ack_delay: default_max_ack_delay,
+35        }
+36    }
+37
+38    /// Returns the `max_ack_delay` that should be requested of the peer when sending an
+39    /// ACK_FREQUENCY frame
+40    pub(super) fn candidate_max_ack_delay(
+41        &self,
+42        rtt: Duration,
+43        config: &AckFrequencyConfig,
+44        peer_params: &TransportParameters,
+45    ) -> Duration {
+46        // Use the peer's max_ack_delay if no custom max_ack_delay was provided in the config
+47        let min_ack_delay =
+48            Duration::from_micros(peer_params.min_ack_delay.map_or(0, |x| x.into()));
+49        config
+50            .max_ack_delay
+51            .unwrap_or(self.peer_max_ack_delay)
+52            .clamp(min_ack_delay, rtt.max(MIN_AUTOMATIC_ACK_DELAY))
+53    }
+54
+55    /// Returns the `max_ack_delay` for the purposes of calculating the PTO
+56    ///
+57    /// This `max_ack_delay` is defined as the maximum of the peer's current `max_ack_delay` and all
+58    /// in-flight `max_ack_delay`s (i.e. proposed values that haven't been acknowledged yet, but
+59    /// might be already in use by the peer).
+60    pub(super) fn max_ack_delay_for_pto(&self) -> Duration {
+61        // Note: we have at most one in-flight ACK_FREQUENCY frame
+62        if let Some((_, _, max_ack_delay)) = self.in_flight_ack_frequency_frame {
+63            self.peer_max_ack_delay.max(max_ack_delay)
+64        } else {
+65            self.peer_max_ack_delay
+66        }
+67    }
+68
+69    /// Returns the next sequence number for an ACK_FREQUENCY frame
+70    pub(super) fn next_sequence_number(&mut self) -> VarInt {
+71        assert!(self.next_outgoing_sequence_number <= VarInt::MAX);
+72
+73        let seq = self.next_outgoing_sequence_number;
+74        self.next_outgoing_sequence_number.0 += 1;
+75        seq
+76    }
+77
+78    /// Returns true if we should send an ACK_FREQUENCY frame
+79    pub(super) fn should_send_ack_frequency(
+80        &self,
+81        rtt: Duration,
+82        config: &AckFrequencyConfig,
+83        peer_params: &TransportParameters,
+84    ) -> bool {
+85        if self.next_outgoing_sequence_number.0 == 0 {
+86            // Always send at startup
+87            return true;
+88        }
+89        let current = self
+90            .in_flight_ack_frequency_frame
+91            .map_or(self.peer_max_ack_delay, |(_, _, pending)| pending);
+92        let desired = self.candidate_max_ack_delay(rtt, config, peer_params);
+93        let error = (desired.as_secs_f32() / current.as_secs_f32()) - 1.0;
+94        error.abs() > MAX_RTT_ERROR
+95    }
+96
+97    /// Notifies the [`AckFrequencyState`] that a packet containing an ACK_FREQUENCY frame was sent
+98    pub(super) fn ack_frequency_sent(
+99        &mut self,
+100        path_id: PathId,
+101        pn: u64,
+102        requested_max_ack_delay: Duration,
+103    ) {
+104        self.in_flight_ack_frequency_frame = Some((path_id, pn, requested_max_ack_delay));
+105    }
+106
+107    /// Notifies the [`AckFrequencyState`] that a packet has been ACKed
+108    pub(super) fn on_acked(&mut self, path_id: PathId, pn: u64) {
+109        match self.in_flight_ack_frequency_frame {
+110            Some((path, number, requested_max_ack_delay)) if path == path_id && number == pn => {
+111                self.in_flight_ack_frequency_frame = None;
+112                self.peer_max_ack_delay = requested_max_ack_delay;
+113            }
+114            _ => {}
+115        }
+116    }
+117
+118    /// Notifies the [`AckFrequencyState`] that an ACK_FREQUENCY frame was received
+119    ///
+120    /// Updates the endpoint's params according to the payload of the ACK_FREQUENCY frame, or
+121    /// returns an error in case the requested `max_ack_delay` is invalid.
+122    ///
+123    /// Returns `true` if the frame was processed and `false` if it was ignored because of being
+124    /// stale.
+125    pub(super) fn ack_frequency_received(
+126        &mut self,
+127        frame: &AckFrequency,
+128    ) -> Result<bool, TransportError> {
+129        if self
+130            .last_ack_frequency_frame
+131            .is_some_and(|highest_sequence_nr| frame.sequence.into_inner() <= highest_sequence_nr)
+132        {
+133            return Ok(false);
+134        }
+135
+136        self.last_ack_frequency_frame = Some(frame.sequence.into_inner());
+137
+138        // Update max_ack_delay
+139        let max_ack_delay = Duration::from_micros(frame.request_max_ack_delay.into_inner());
+140        if max_ack_delay < TIMER_GRANULARITY {
+141            return Err(TransportError::PROTOCOL_VIOLATION(
+142                "Requested Max Ack Delay in ACK_FREQUENCY frame is less than min_ack_delay",
+143            ));
+144        }
+145        self.max_ack_delay = max_ack_delay;
+146
+147        Ok(true)
+148    }
+149}
+150
+151/// Maximum proportion difference between the most recently requested max ACK delay and the
+152/// currently desired one before a new request is sent, when the peer supports the ACK frequency
+153/// extension and an explicit max ACK delay is not configured.
+154const MAX_RTT_ERROR: f32 = 0.2;
+155
+156/// Minimum value to request the peer set max ACK delay to when the peer supports the ACK frequency
+157/// extension and an explicit max ACK delay is not configured.
+158// Keep in sync with `AckFrequencyConfig::max_ack_delay` documentation
+159const MIN_AUTOMATIC_ACK_DELAY: Duration = Duration::from_millis(25);
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/assembler.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/assembler.rs.html new file mode 100644 index 000000000..92fc59de0 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/assembler.rs.html @@ -0,0 +1,845 @@ +assembler.rs - source

iroh_quinn_proto/connection/
assembler.rs

1use std::{
+2    cmp::Ordering,
+3    collections::{BinaryHeap, binary_heap::PeekMut},
+4    mem,
+5};
+6
+7use bytes::{Buf, Bytes, BytesMut};
+8
+9use crate::range_set::ArrayRangeSet;
+10
+11/// Helper to assemble unordered stream frames into an ordered stream
+12#[derive(Debug, Default)]
+13pub(super) struct Assembler {
+14    state: State,
+15    data: BinaryHeap<Buffer>,
+16    /// Total number of buffered bytes, including duplicates in ordered mode.
+17    buffered: usize,
+18    /// Estimated number of allocated bytes, will never be less than `buffered`.
+19    allocated: usize,
+20    /// Number of bytes read by the application. When only ordered reads have been used, this is the
+21    /// length of the contiguous prefix of the stream which has been consumed by the application,
+22    /// aka the stream offset.
+23    bytes_read: u64,
+24    end: u64,
+25}
+26
+27impl Assembler {
+28    pub(super) fn new() -> Self {
+29        Self::default()
+30    }
+31
+32    /// Reset to the initial state
+33    pub(super) fn reinit(&mut self) {
+34        let old_data = mem::take(&mut self.data);
+35        *self = Self::default();
+36        self.data = old_data;
+37        self.data.clear();
+38    }
+39
+40    pub(super) fn ensure_ordering(&mut self, ordered: bool) -> Result<(), IllegalOrderedRead> {
+41        if ordered && !self.state.is_ordered() {
+42            return Err(IllegalOrderedRead);
+43        } else if !ordered && self.state.is_ordered() {
+44            // Enter unordered mode
+45            if !self.data.is_empty() {
+46                // Get rid of possible duplicates
+47                self.defragment();
+48            }
+49            let mut recvd = ArrayRangeSet::new();
+50            recvd.insert(0..self.bytes_read);
+51            for chunk in &self.data {
+52                recvd.insert(chunk.offset..chunk.offset + chunk.bytes.len() as u64);
+53            }
+54            self.state = State::Unordered { recvd };
+55        }
+56        Ok(())
+57    }
+58
+59    /// Get the the next chunk
+60    pub(super) fn read(&mut self, max_length: usize, ordered: bool) -> Option<Chunk> {
+61        loop {
+62            let mut chunk = self.data.peek_mut()?;
+63
+64            if ordered {
+65                if chunk.offset > self.bytes_read {
+66                    // Next chunk is after current read index
+67                    return None;
+68                } else if (chunk.offset + chunk.bytes.len() as u64) <= self.bytes_read {
+69                    // Next chunk is useless as the read index is beyond its end
+70                    self.buffered -= chunk.bytes.len();
+71                    self.allocated -= chunk.allocation_size;
+72                    PeekMut::pop(chunk);
+73                    continue;
+74                }
+75
+76                // Determine `start` and `len` of the slice of useful data in chunk
+77                let start = (self.bytes_read - chunk.offset) as usize;
+78                if start > 0 {
+79                    chunk.bytes.advance(start);
+80                    chunk.offset += start as u64;
+81                    self.buffered -= start;
+82                }
+83            }
+84
+85            return Some(if max_length < chunk.bytes.len() {
+86                self.bytes_read += max_length as u64;
+87                let offset = chunk.offset;
+88                chunk.offset += max_length as u64;
+89                self.buffered -= max_length;
+90                Chunk::new(offset, chunk.bytes.split_to(max_length))
+91            } else {
+92                self.bytes_read += chunk.bytes.len() as u64;
+93                self.buffered -= chunk.bytes.len();
+94                self.allocated -= chunk.allocation_size;
+95                let chunk = PeekMut::pop(chunk);
+96                Chunk::new(chunk.offset, chunk.bytes)
+97            });
+98        }
+99    }
+100
+101    /// Copy fragmented chunk data to new chunks backed by a single buffer
+102    ///
+103    /// This makes sure we're not unnecessarily holding on to many larger allocations.
+104    /// We merge contiguous chunks in the process of doing so.
+105    fn defragment(&mut self) {
+106        let new = BinaryHeap::with_capacity(self.data.len());
+107        let old = mem::replace(&mut self.data, new);
+108        let mut buffers = old.into_sorted_vec();
+109        self.buffered = 0;
+110        let mut fragmented_buffered = 0;
+111        let mut offset = self.bytes_read;
+112        for chunk in buffers.iter_mut().rev() {
+113            chunk.try_mark_defragment(offset);
+114            let size = chunk.bytes.len();
+115            offset = chunk.offset + size as u64;
+116            self.buffered += size;
+117            if !chunk.defragmented {
+118                fragmented_buffered += size;
+119            }
+120        }
+121        self.allocated = self.buffered;
+122        let mut buffer = BytesMut::with_capacity(fragmented_buffered);
+123        let mut offset = self.bytes_read;
+124        for chunk in buffers.into_iter().rev() {
+125            if chunk.defragmented {
+126                // bytes might be empty after try_mark_defragment
+127                if !chunk.bytes.is_empty() {
+128                    self.data.push(chunk);
+129                }
+130                continue;
+131            }
+132            // Overlap is resolved by try_mark_defragment
+133            if chunk.offset != offset + (buffer.len() as u64) {
+134                if !buffer.is_empty() {
+135                    self.data
+136                        .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+137                }
+138                offset = chunk.offset;
+139            }
+140            buffer.extend_from_slice(&chunk.bytes);
+141        }
+142        if !buffer.is_empty() {
+143            self.data
+144                .push(Buffer::new_defragmented(offset, buffer.split().freeze()));
+145        }
+146    }
+147
+148    // Note: If a packet contains many frames from the same stream, the estimated over-allocation
+149    // will be much higher because we are counting the same allocation multiple times.
+150    pub(super) fn insert(&mut self, mut offset: u64, mut bytes: Bytes, allocation_size: usize) {
+151        debug_assert!(
+152            bytes.len() <= allocation_size,
+153            "allocation_size less than bytes.len(): {:?} < {:?}",
+154            allocation_size,
+155            bytes.len()
+156        );
+157        self.end = self.end.max(offset + bytes.len() as u64);
+158        if let State::Unordered { ref mut recvd } = self.state {
+159            // Discard duplicate data
+160            let range = offset..offset + bytes.len() as u64;
+161            for duplicate in recvd.iter_range(range.clone()) {
+162                if duplicate.start > offset {
+163                    let buffer = Buffer::new(
+164                        offset,
+165                        bytes.split_to((duplicate.start - offset) as usize),
+166                        allocation_size,
+167                    );
+168                    self.buffered += buffer.bytes.len();
+169                    self.allocated += buffer.allocation_size;
+170                    self.data.push(buffer);
+171                    offset = duplicate.start;
+172                }
+173                bytes.advance((duplicate.end - offset) as usize);
+174                offset = duplicate.end;
+175            }
+176            recvd.insert(range);
+177        } else if offset < self.bytes_read {
+178            if (offset + bytes.len() as u64) <= self.bytes_read {
+179                return;
+180            } else {
+181                let diff = self.bytes_read - offset;
+182                offset += diff;
+183                bytes.advance(diff as usize);
+184            }
+185        }
+186
+187        if bytes.is_empty() {
+188            return;
+189        }
+190        let buffer = Buffer::new(offset, bytes, allocation_size);
+191        self.buffered += buffer.bytes.len();
+192        self.allocated += buffer.allocation_size;
+193        self.data.push(buffer);
+194        // `self.buffered` also counts duplicate bytes, therefore we use
+195        // `self.end - self.bytes_read` as an upper bound of buffered unique
+196        // bytes. This will cause a defragmentation if the amount of duplicate
+197        // bytes exceedes a proportion of the receive window size.
+198        let buffered = self.buffered.min((self.end - self.bytes_read) as usize);
+199        let over_allocation = self.allocated - buffered;
+200        // Rationale: on the one hand, we want to defragment rarely, ideally never
+201        // in non-pathological scenarios. However, a pathological or malicious
+202        // peer could send us one-byte frames, and since we use reference-counted
+203        // buffers in order to prevent copying, this could result in keeping a lot
+204        // of memory allocated. This limits over-allocation in proportion to the
+205        // buffered data. The constants are chosen somewhat arbitrarily and try to
+206        // balance between defragmentation overhead and over-allocation.
+207        let threshold = 32768.max(buffered * 3 / 2);
+208        if over_allocation > threshold {
+209            self.defragment()
+210        }
+211    }
+212
+213    /// Number of bytes consumed by the application
+214    pub(super) fn bytes_read(&self) -> u64 {
+215        self.bytes_read
+216    }
+217
+218    /// Discard all buffered data
+219    pub(super) fn clear(&mut self) {
+220        self.data.clear();
+221        self.buffered = 0;
+222        self.allocated = 0;
+223    }
+224}
+225
+226/// A chunk of data from the receive stream
+227#[derive(Debug, PartialEq, Eq)]
+228pub struct Chunk {
+229    /// The offset in the stream
+230    pub offset: u64,
+231    /// The contents of the chunk
+232    pub bytes: Bytes,
+233}
+234
+235impl Chunk {
+236    fn new(offset: u64, bytes: Bytes) -> Self {
+237        Self { offset, bytes }
+238    }
+239}
+240
+241#[derive(Debug, Eq)]
+242struct Buffer {
+243    offset: u64,
+244    bytes: Bytes,
+245    /// Size of the allocation behind `bytes`, if `defragmented == false`.
+246    /// Otherwise this will be set to `bytes.len()` by `try_mark_defragment`.
+247    /// Will never be less than `bytes.len()`.
+248    allocation_size: usize,
+249    defragmented: bool,
+250}
+251
+252impl Buffer {
+253    /// Constructs a new fragmented Buffer
+254    fn new(offset: u64, bytes: Bytes, allocation_size: usize) -> Self {
+255        Self {
+256            offset,
+257            bytes,
+258            allocation_size,
+259            defragmented: false,
+260        }
+261    }
+262
+263    /// Constructs a new defragmented Buffer
+264    fn new_defragmented(offset: u64, bytes: Bytes) -> Self {
+265        let allocation_size = bytes.len();
+266        Self {
+267            offset,
+268            bytes,
+269            allocation_size,
+270            defragmented: true,
+271        }
+272    }
+273
+274    /// Discards data before `offset` and flags `self` as defragmented if it has good utilization
+275    fn try_mark_defragment(&mut self, offset: u64) {
+276        let duplicate = offset.saturating_sub(self.offset) as usize;
+277        self.offset = self.offset.max(offset);
+278        if duplicate >= self.bytes.len() {
+279            // All bytes are duplicate
+280            self.bytes = Bytes::new();
+281            self.defragmented = true;
+282            self.allocation_size = 0;
+283            return;
+284        }
+285        self.bytes.advance(duplicate);
+286        // Make sure that fragmented buffers with high utilization become defragmented and
+287        // defragmented buffers remain defragmented
+288        self.defragmented = self.defragmented || self.bytes.len() * 6 / 5 >= self.allocation_size;
+289        if self.defragmented {
+290            // Make sure that defragmented buffers do not contribute to over-allocation
+291            self.allocation_size = self.bytes.len();
+292        }
+293    }
+294}
+295
+296impl Ord for Buffer {
+297    // Invert ordering based on offset (max-heap, min offset first),
+298    // prioritize longer chunks at the same offset.
+299    fn cmp(&self, other: &Self) -> Ordering {
+300        self.offset
+301            .cmp(&other.offset)
+302            .reverse()
+303            .then(self.bytes.len().cmp(&other.bytes.len()))
+304    }
+305}
+306
+307impl PartialOrd for Buffer {
+308    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+309        Some(self.cmp(other))
+310    }
+311}
+312
+313impl PartialEq for Buffer {
+314    fn eq(&self, other: &Self) -> bool {
+315        (self.offset, self.bytes.len()) == (other.offset, other.bytes.len())
+316    }
+317}
+318
+319#[derive(Debug, Default)]
+320enum State {
+321    #[default]
+322    Ordered,
+323    Unordered {
+324        /// The set of offsets that have been received from the peer, including portions not yet
+325        /// read by the application.
+326        recvd: ArrayRangeSet,
+327    },
+328}
+329
+330impl State {
+331    fn is_ordered(&self) -> bool {
+332        matches!(self, Self::Ordered)
+333    }
+334}
+335
+336/// Error indicating that an ordered read was performed on a stream after an unordered read
+337#[derive(Debug)]
+338pub struct IllegalOrderedRead;
+339
+340#[cfg(test)]
+341mod test {
+342    use super::*;
+343    use assert_matches::assert_matches;
+344
+345    #[test]
+346    fn assemble_ordered() {
+347        let mut x = Assembler::new();
+348        assert_matches!(next(&mut x, 32), None);
+349        x.insert(0, Bytes::from_static(b"123"), 3);
+350        assert_matches!(next(&mut x, 1), Some(ref y) if &y[..] == b"1");
+351        assert_matches!(next(&mut x, 3), Some(ref y) if &y[..] == b"23");
+352        x.insert(3, Bytes::from_static(b"456"), 3);
+353        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+354        x.insert(6, Bytes::from_static(b"789"), 3);
+355        x.insert(9, Bytes::from_static(b"10"), 2);
+356        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"789");
+357        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"10");
+358        assert_matches!(next(&mut x, 32), None);
+359    }
+360
+361    #[test]
+362    fn assemble_unordered() {
+363        let mut x = Assembler::new();
+364        x.ensure_ordering(false).unwrap();
+365        x.insert(3, Bytes::from_static(b"456"), 3);
+366        assert_matches!(next(&mut x, 32), None);
+367        x.insert(0, Bytes::from_static(b"123"), 3);
+368        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+369        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"456");
+370        assert_matches!(next(&mut x, 32), None);
+371    }
+372
+373    #[test]
+374    fn assemble_duplicate() {
+375        let mut x = Assembler::new();
+376        x.insert(0, Bytes::from_static(b"123"), 3);
+377        x.insert(0, Bytes::from_static(b"123"), 3);
+378        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+379        assert_matches!(next(&mut x, 32), None);
+380    }
+381
+382    #[test]
+383    fn assemble_duplicate_compact() {
+384        let mut x = Assembler::new();
+385        x.insert(0, Bytes::from_static(b"123"), 3);
+386        x.insert(0, Bytes::from_static(b"123"), 3);
+387        x.defragment();
+388        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+389        assert_matches!(next(&mut x, 32), None);
+390    }
+391
+392    #[test]
+393    fn assemble_contained() {
+394        let mut x = Assembler::new();
+395        x.insert(0, Bytes::from_static(b"12345"), 5);
+396        x.insert(1, Bytes::from_static(b"234"), 3);
+397        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+398        assert_matches!(next(&mut x, 32), None);
+399    }
+400
+401    #[test]
+402    fn assemble_contained_compact() {
+403        let mut x = Assembler::new();
+404        x.insert(0, Bytes::from_static(b"12345"), 5);
+405        x.insert(1, Bytes::from_static(b"234"), 3);
+406        x.defragment();
+407        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+408        assert_matches!(next(&mut x, 32), None);
+409    }
+410
+411    #[test]
+412    fn assemble_contains() {
+413        let mut x = Assembler::new();
+414        x.insert(1, Bytes::from_static(b"234"), 3);
+415        x.insert(0, Bytes::from_static(b"12345"), 5);
+416        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+417        assert_matches!(next(&mut x, 32), None);
+418    }
+419
+420    #[test]
+421    fn assemble_contains_compact() {
+422        let mut x = Assembler::new();
+423        x.insert(1, Bytes::from_static(b"234"), 3);
+424        x.insert(0, Bytes::from_static(b"12345"), 5);
+425        x.defragment();
+426        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"12345");
+427        assert_matches!(next(&mut x, 32), None);
+428    }
+429
+430    #[test]
+431    fn assemble_overlapping() {
+432        let mut x = Assembler::new();
+433        x.insert(0, Bytes::from_static(b"123"), 3);
+434        x.insert(1, Bytes::from_static(b"234"), 3);
+435        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123");
+436        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"4");
+437        assert_matches!(next(&mut x, 32), None);
+438    }
+439
+440    #[test]
+441    fn assemble_overlapping_compact() {
+442        let mut x = Assembler::new();
+443        x.insert(0, Bytes::from_static(b"123"), 4);
+444        x.insert(1, Bytes::from_static(b"234"), 4);
+445        x.defragment();
+446        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+447        assert_matches!(next(&mut x, 32), None);
+448    }
+449
+450    #[test]
+451    fn assemble_complex() {
+452        let mut x = Assembler::new();
+453        x.insert(0, Bytes::from_static(b"1"), 1);
+454        x.insert(2, Bytes::from_static(b"3"), 1);
+455        x.insert(4, Bytes::from_static(b"5"), 1);
+456        x.insert(0, Bytes::from_static(b"123456"), 6);
+457        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+458        assert_matches!(next(&mut x, 32), None);
+459    }
+460
+461    #[test]
+462    fn assemble_complex_compact() {
+463        let mut x = Assembler::new();
+464        x.insert(0, Bytes::from_static(b"1"), 1);
+465        x.insert(2, Bytes::from_static(b"3"), 1);
+466        x.insert(4, Bytes::from_static(b"5"), 1);
+467        x.insert(0, Bytes::from_static(b"123456"), 6);
+468        x.defragment();
+469        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"123456");
+470        assert_matches!(next(&mut x, 32), None);
+471    }
+472
+473    #[test]
+474    fn assemble_old() {
+475        let mut x = Assembler::new();
+476        x.insert(0, Bytes::from_static(b"1234"), 4);
+477        assert_matches!(next(&mut x, 32), Some(ref y) if &y[..] == b"1234");
+478        x.insert(0, Bytes::from_static(b"1234"), 4);
+479        assert_matches!(next(&mut x, 32), None);
+480    }
+481
+482    #[test]
+483    fn compact() {
+484        let mut x = Assembler::new();
+485        x.insert(0, Bytes::from_static(b"abc"), 4);
+486        x.insert(3, Bytes::from_static(b"def"), 4);
+487        x.insert(9, Bytes::from_static(b"jkl"), 4);
+488        x.insert(12, Bytes::from_static(b"mno"), 4);
+489        x.defragment();
+490        assert_eq!(
+491            next_unordered(&mut x),
+492            Chunk::new(0, Bytes::from_static(b"abcdef"))
+493        );
+494        assert_eq!(
+495            next_unordered(&mut x),
+496            Chunk::new(9, Bytes::from_static(b"jklmno"))
+497        );
+498    }
+499
+500    #[test]
+501    fn defrag_with_missing_prefix() {
+502        let mut x = Assembler::new();
+503        x.insert(3, Bytes::from_static(b"def"), 3);
+504        x.defragment();
+505        assert_eq!(
+506            next_unordered(&mut x),
+507            Chunk::new(3, Bytes::from_static(b"def"))
+508        );
+509    }
+510
+511    #[test]
+512    fn defrag_read_chunk() {
+513        let mut x = Assembler::new();
+514        x.insert(3, Bytes::from_static(b"def"), 4);
+515        x.insert(0, Bytes::from_static(b"abc"), 4);
+516        x.insert(7, Bytes::from_static(b"hij"), 4);
+517        x.insert(11, Bytes::from_static(b"lmn"), 4);
+518        x.defragment();
+519        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"abcdef");
+520        x.insert(5, Bytes::from_static(b"fghijklmn"), 9);
+521        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"ghijklmn");
+522        x.insert(13, Bytes::from_static(b"nopq"), 4);
+523        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"opq");
+524        x.insert(15, Bytes::from_static(b"pqrs"), 4);
+525        assert_matches!(x.read(usize::MAX, true), Some(ref y) if &y.bytes[..] == b"rs");
+526        assert_matches!(x.read(usize::MAX, true), None);
+527    }
+528
+529    #[test]
+530    fn unordered_happy_path() {
+531        let mut x = Assembler::new();
+532        x.ensure_ordering(false).unwrap();
+533        x.insert(0, Bytes::from_static(b"abc"), 3);
+534        assert_eq!(
+535            next_unordered(&mut x),
+536            Chunk::new(0, Bytes::from_static(b"abc"))
+537        );
+538        assert_eq!(x.read(usize::MAX, false), None);
+539        x.insert(3, Bytes::from_static(b"def"), 3);
+540        assert_eq!(
+541            next_unordered(&mut x),
+542            Chunk::new(3, Bytes::from_static(b"def"))
+543        );
+544        assert_eq!(x.read(usize::MAX, false), None);
+545    }
+546
+547    #[test]
+548    fn unordered_dedup() {
+549        let mut x = Assembler::new();
+550        x.ensure_ordering(false).unwrap();
+551        x.insert(3, Bytes::from_static(b"def"), 3);
+552        assert_eq!(
+553            next_unordered(&mut x),
+554            Chunk::new(3, Bytes::from_static(b"def"))
+555        );
+556        assert_eq!(x.read(usize::MAX, false), None);
+557        x.insert(0, Bytes::from_static(b"a"), 1);
+558        x.insert(0, Bytes::from_static(b"abcdefghi"), 9);
+559        x.insert(0, Bytes::from_static(b"abcd"), 4);
+560        assert_eq!(
+561            next_unordered(&mut x),
+562            Chunk::new(0, Bytes::from_static(b"a"))
+563        );
+564        assert_eq!(
+565            next_unordered(&mut x),
+566            Chunk::new(1, Bytes::from_static(b"bc"))
+567        );
+568        assert_eq!(
+569            next_unordered(&mut x),
+570            Chunk::new(6, Bytes::from_static(b"ghi"))
+571        );
+572        assert_eq!(x.read(usize::MAX, false), None);
+573        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+574        assert_eq!(
+575            next_unordered(&mut x),
+576            Chunk::new(9, Bytes::from_static(b"jkl"))
+577        );
+578        assert_eq!(x.read(usize::MAX, false), None);
+579        x.insert(12, Bytes::from_static(b"mno"), 3);
+580        assert_eq!(
+581            next_unordered(&mut x),
+582            Chunk::new(12, Bytes::from_static(b"mno"))
+583        );
+584        assert_eq!(x.read(usize::MAX, false), None);
+585        x.insert(2, Bytes::from_static(b"cde"), 3);
+586        assert_eq!(x.read(usize::MAX, false), None);
+587    }
+588
+589    #[test]
+590    fn chunks_dedup() {
+591        let mut x = Assembler::new();
+592        x.insert(3, Bytes::from_static(b"def"), 3);
+593        assert_eq!(x.read(usize::MAX, true), None);
+594        x.insert(0, Bytes::from_static(b"a"), 1);
+595        x.insert(1, Bytes::from_static(b"bcdefghi"), 9);
+596        x.insert(0, Bytes::from_static(b"abcd"), 4);
+597        assert_eq!(
+598            x.read(usize::MAX, true),
+599            Some(Chunk::new(0, Bytes::from_static(b"abcd")))
+600        );
+601        assert_eq!(
+602            x.read(usize::MAX, true),
+603            Some(Chunk::new(4, Bytes::from_static(b"efghi")))
+604        );
+605        assert_eq!(x.read(usize::MAX, true), None);
+606        x.insert(8, Bytes::from_static(b"ijkl"), 4);
+607        assert_eq!(
+608            x.read(usize::MAX, true),
+609            Some(Chunk::new(9, Bytes::from_static(b"jkl")))
+610        );
+611        assert_eq!(x.read(usize::MAX, true), None);
+612        x.insert(12, Bytes::from_static(b"mno"), 3);
+613        assert_eq!(
+614            x.read(usize::MAX, true),
+615            Some(Chunk::new(12, Bytes::from_static(b"mno")))
+616        );
+617        assert_eq!(x.read(usize::MAX, true), None);
+618        x.insert(2, Bytes::from_static(b"cde"), 3);
+619        assert_eq!(x.read(usize::MAX, true), None);
+620    }
+621
+622    #[test]
+623    fn ordered_eager_discard() {
+624        let mut x = Assembler::new();
+625        x.insert(0, Bytes::from_static(b"abc"), 3);
+626        assert_eq!(x.data.len(), 1);
+627        assert_eq!(
+628            x.read(usize::MAX, true),
+629            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+630        );
+631        x.insert(0, Bytes::from_static(b"ab"), 2);
+632        assert_eq!(x.data.len(), 0);
+633        x.insert(2, Bytes::from_static(b"cd"), 2);
+634        assert_eq!(
+635            x.data.peek(),
+636            Some(&Buffer::new(3, Bytes::from_static(b"d"), 2))
+637        );
+638    }
+639
+640    #[test]
+641    fn ordered_insert_unordered_read() {
+642        let mut x = Assembler::new();
+643        x.insert(0, Bytes::from_static(b"abc"), 3);
+644        x.insert(0, Bytes::from_static(b"abc"), 3);
+645        x.ensure_ordering(false).unwrap();
+646        assert_eq!(
+647            x.read(3, false),
+648            Some(Chunk::new(0, Bytes::from_static(b"abc")))
+649        );
+650        assert_eq!(x.read(3, false), None);
+651    }
+652
+653    #[test]
+654    fn no_duplicate_after_mode_switch() {
+655        // Regression test: bytes read in ordered mode should not be returned again in unordered mode
+656        let mut x = Assembler::new();
+657        x.insert(0, Bytes::from_static(b"a"), 1);
+658        x.insert(0, Bytes::from_static(b"a"), 1); // duplicate
+659        assert_eq!(
+660            x.read(1, true),
+661            Some(Chunk::new(0, Bytes::from_static(b"a")))
+662        );
+663        x.ensure_ordering(false).unwrap();
+664        assert_eq!(x.read(1, false), None); // should be None, byte 0 already returned
+665    }
+666
+667    fn next_unordered(x: &mut Assembler) -> Chunk {
+668        x.read(usize::MAX, false).unwrap()
+669    }
+670
+671    fn next(x: &mut Assembler, size: usize) -> Option<Bytes> {
+672        x.read(size, true).map(|chunk| chunk.bytes)
+673    }
+674}
+675
+676#[cfg(all(test, not(target_family = "wasm")))]
+677mod proptests {
+678    use proptest::prelude::*;
+679    use test_strategy::{Arbitrary, proptest};
+680
+681    use super::*;
+682
+683    const MAX_OFFSET: u64 = 512;
+684    const MAX_LEN: usize = 64;
+685
+686    #[derive(Debug, Clone, Arbitrary)]
+687    enum Op {
+688        #[weight(10)]
+689        Insert {
+690            #[strategy(0..MAX_OFFSET)]
+691            offset: u64,
+692            #[strategy(1..MAX_LEN)]
+693            len: usize,
+694        },
+695        #[weight(10)]
+696        Read {
+697            #[strategy(1..MAX_LEN)]
+698            max_len: usize,
+699        },
+700        #[weight(1)]
+701        EnsureOrdering { ordered: bool },
+702        #[weight(1)]
+703        Defragment,
+704    }
+705
+706    /// Tracks the state of the assembler for verification
+707    struct RefState {
+708        received: Vec<bool>,
+709        returned: Vec<bool>,
+710        ordered: bool,
+711    }
+712
+713    fn set_range(bits: &mut [bool], start: u64, len: usize) {
+714        for i in start..(start + len as u64).min(bits.len() as u64) {
+715            bits[i as usize] = true;
+716        }
+717    }
+718
+719    impl RefState {
+720        fn new() -> Self {
+721            Self {
+722                received: vec![false; MAX_OFFSET as usize],
+723                returned: vec![false; MAX_OFFSET as usize],
+724                ordered: true,
+725            }
+726        }
+727
+728        fn insert(&mut self, offset: u64, len: usize) {
+729            set_range(&mut self.received, offset, len);
+730        }
+731
+732        fn ensure_ordering(&mut self, ordered: bool) -> bool {
+733            if ordered && !self.ordered {
+734                return false;
+735            }
+736            self.ordered = ordered;
+737            true
+738        }
+739
+740        fn bytes_read(&self) -> u64 {
+741            self.returned.iter().filter(|&&x| x).count() as u64
+742        }
+743    }
+744
+745    fn make_data() -> Vec<u8> {
+746        use rand::{Rng, SeedableRng};
+747        let mut rng = rand::rngs::StdRng::seed_from_u64(0xDEADBEEF);
+748        let mut data = vec![0u8; MAX_OFFSET as usize];
+749        rng.fill(data.as_mut_slice());
+750        data
+751    }
+752
+753    fn get_slice(data: &[u8], offset: u64, len: usize) -> Bytes {
+754        let start = offset as usize;
+755        let end = (start + len).min(data.len());
+756        Bytes::copy_from_slice(&data[start..end])
+757    }
+758
+759    fn verify_chunk(data: &[u8], chunk: &Chunk) -> bool {
+760        let start = chunk.offset as usize;
+761        chunk.bytes[..] == data[start..start + chunk.bytes.len()]
+762    }
+763
+764    #[proptest]
+765    fn assembler_matches_reference(
+766        #[strategy(proptest::collection::vec(any::<Op>(), 1..100))] ops: Vec<Op>,
+767    ) {
+768        let data = make_data();
+769        let mut asm = Assembler::new();
+770        let mut reference = RefState::new();
+771
+772        for op in ops {
+773            match op {
+774                Op::Insert { offset, len } => {
+775                    let bytes = get_slice(&data, offset, len);
+776                    asm.insert(offset, bytes, len);
+777                    reference.insert(offset, len);
+778                }
+779                Op::Read { max_len } => {
+780                    let ordered = reference.ordered;
+781                    let actual = asm.read(max_len, ordered);
+782
+783                    match actual {
+784                        None => {
+785                            // Should only be None if no unreturned received bytes available
+786                            let has_available = if ordered {
+787                                // In ordered mode, check if the first unreturned byte is received
+788                                reference
+789                                    .returned
+790                                    .iter()
+791                                    .position(|&x| !x)
+792                                    .is_some_and(|pos| reference.received[pos])
+793                            } else {
+794                                // In unordered mode, check if any unreturned received byte exists
+795                                reference
+796                                    .received
+797                                    .iter()
+798                                    .zip(&reference.returned)
+799                                    .any(|(&r, &ret)| r && !ret)
+800                            };
+801                            prop_assert!(
+802                                !has_available,
+803                                "read returned None but data was available"
+804                            );
+805                        }
+806                        Some(chunk) => {
+807                            prop_assert!(chunk.bytes.len() <= max_len, "chunk exceeds max_len");
+808                            prop_assert!(verify_chunk(&data, &chunk), "data corruption");
+809                            // Mark as returned, check for duplicates
+810                            for i in 0..chunk.bytes.len() {
+811                                let offset = chunk.offset as usize + i;
+812                                prop_assert!(
+813                                    reference.received[offset],
+814                                    "returned unreceived byte at {offset}"
+815                                );
+816                                prop_assert!(
+817                                    !reference.returned[offset],
+818                                    "duplicate byte at {offset}"
+819                                );
+820                                reference.returned[offset] = true;
+821                            }
+822                        }
+823                    }
+824                }
+825                Op::EnsureOrdering { ordered } => {
+826                    let actual = asm.ensure_ordering(ordered).is_ok();
+827                    let expected = reference.ensure_ordering(ordered);
+828                    prop_assert_eq!(actual, expected, "ensure_ordering result mismatch");
+829                }
+830                Op::Defragment => {
+831                    if asm.state.is_ordered() {
+832                        asm.defragment();
+833                    }
+834                }
+835            }
+836        }
+837
+838        // Invariant: bytes_read matches
+839        prop_assert_eq!(
+840            asm.bytes_read(),
+841            reference.bytes_read(),
+842            "bytes_read mismatch"
+843        );
+844    }
+845}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/cid_state.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/cid_state.rs.html new file mode 100644 index 000000000..f756a56ed --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/cid_state.rs.html @@ -0,0 +1,229 @@ +cid_state.rs - source

iroh_quinn_proto/connection/
cid_state.rs

1//! Maintain the state of local connection IDs
+2use std::collections::VecDeque;
+3
+4use rustc_hash::FxHashSet;
+5use tracing::{debug, trace};
+6
+7use crate::{Duration, Instant, TransportError, shared::IssuedCid};
+8
+9/// Local connection ID management
+10#[derive(Debug)]
+11pub(super) struct CidState {
+12    /// Timestamp when issued cids should be retired
+13    ///
+14    /// Each entry indicates the expiration of all timestamps up to the sequence number in
+15    /// the entry.  This means one entry can expire multiple CIDs if the sequence number
+16    /// jumps by more than 1 between entries.
+17    retire_timestamp: VecDeque<CidTimestamp>,
+18    /// Number of local connection IDs that have been issued in NEW_CONNECTION_ID frames.
+19    ///
+20    /// This is thus also the sequence number of the next CID to be issued.
+21    issued: u64,
+22    /// Sequence numbers of local connection IDs not yet retired by the peer
+23    active_seq: FxHashSet<u64>,
+24    /// Sequence number the peer has already retired all CIDs below at our request via `retire_prior_to`
+25    prev_retire_seq: u64,
+26    /// Sequence number to set in retire_prior_to field in NEW_CONNECTION_ID frame
+27    retire_seq: u64,
+28    /// cid length used to decode short packet
+29    cid_len: usize,
+30    /// cid lifetime
+31    cid_lifetime: Option<Duration>,
+32}
+33
+34impl CidState {
+35    pub(crate) fn new(
+36        cid_len: usize,
+37        cid_lifetime: Option<Duration>,
+38        now: Instant,
+39        issued: u64,
+40    ) -> Self {
+41        let mut active_seq = FxHashSet::default();
+42        // Add sequence number of CIDs used in handshaking into tracking set
+43        for seq in 0..issued {
+44            active_seq.insert(seq);
+45        }
+46        let mut this = Self {
+47            retire_timestamp: VecDeque::new(),
+48            issued,
+49            active_seq,
+50            prev_retire_seq: 0,
+51            retire_seq: 0,
+52            cid_len,
+53            cid_lifetime,
+54        };
+55        // Track lifetime of CIDs used in handshaking
+56        for seq in 0..issued {
+57            this.track_lifetime(seq, now);
+58        }
+59        this
+60    }
+61
+62    /// Find the earliest time when previously issued CID should be retired
+63    pub(crate) fn next_timeout(&self) -> Option<Instant> {
+64        self.retire_timestamp.front().map(|nc| {
+65            trace!("CID {} will expire at {:?}", nc.sequence, nc.timestamp);
+66            nc.timestamp
+67        })
+68    }
+69
+70    /// Track the lifetime of issued cids in `retire_timestamp`
+71    fn track_lifetime(&mut self, new_cid_seq: u64, now: Instant) {
+72        let Some(lifetime) = self.cid_lifetime else {
+73            return;
+74        };
+75        let Some(expire_at) = now.checked_add(lifetime) else {
+76            return;
+77        };
+78
+79        let last_record = self.retire_timestamp.back_mut();
+80        if let Some(last) = last_record {
+81            // Compare the timestamp with the last inserted record
+82            // Combine into a single batch if timestamp of current cid is same as the last record
+83            if expire_at == last.timestamp {
+84                debug_assert!(new_cid_seq > last.sequence);
+85                last.sequence = new_cid_seq;
+86                return;
+87            }
+88        }
+89
+90        self.retire_timestamp.push_back(CidTimestamp {
+91            sequence: new_cid_seq,
+92            timestamp: expire_at,
+93        });
+94    }
+95
+96    /// Update local CID state when previously issued CID is retired
+97    ///
+98    /// Return whether a new CID needs to be pushed that notifies remote peer to respond `RETIRE_CONNECTION_ID`
+99    pub(crate) fn on_cid_timeout(&mut self) -> bool {
+100        // Whether the peer hasn't retired all the CIDs we asked it to yet
+101        let unretired_ids_found =
+102            (self.prev_retire_seq..self.retire_seq).any(|seq| self.active_seq.contains(&seq));
+103
+104        let current_retire_prior_to = self.retire_seq;
+105        let next_retire_sequence = self
+106            .retire_timestamp
+107            .pop_front()
+108            .map(|seq| seq.sequence + 1);
+109
+110        // According to RFC:
+111        // Endpoints SHOULD NOT issue updates of the Retire Prior To field
+112        // before receiving RETIRE_CONNECTION_ID frames that retire all
+113        // connection IDs indicated by the previous Retire Prior To value.
+114        // https://tools.ietf.org/html/draft-ietf-quic-transport-29#section-5.1.2
+115        if !unretired_ids_found {
+116            // All Cids are retired, `prev_retire_cid_seq` can be assigned to `retire_cid_seq`
+117            self.prev_retire_seq = self.retire_seq;
+118            // Advance `retire_seq` if next cid that needs to be retired exists
+119            if let Some(next_retire_prior_to) = next_retire_sequence {
+120                self.retire_seq = next_retire_prior_to;
+121            }
+122        }
+123
+124        // Check if retirement of all CIDs that reach their lifetime is still needed
+125        // According to RFC:
+126        // An endpoint MUST NOT
+127        // provide more connection IDs than the peer's limit.  An endpoint MAY
+128        // send connection IDs that temporarily exceed a peer's limit if the
+129        // NEW_CONNECTION_ID frame also requires the retirement of any excess,
+130        // by including a sufficiently large value in the Retire Prior To field.
+131        //
+132        // If yes (return true), a new CID must be pushed with updated `retire_prior_to` field to remote peer.
+133        // If no (return false), it means CIDs that reach the end of lifetime have been retired already. Do not push a new CID in order to avoid violating above RFC.
+134        (current_retire_prior_to..self.retire_seq).any(|seq| self.active_seq.contains(&seq))
+135    }
+136
+137    /// Update cid state when `NewIdentifiers` event is received
+138    ///
+139    /// These are newly generated CIDs which we'll send to the peer in
+140    /// (PATH_)NEW_CONNECTION_ID frames in the next packet that is sent.  This records them
+141    /// and tracks their lifetime.
+142    pub(crate) fn new_cids(&mut self, ids: &[IssuedCid], now: Instant) {
+143        // `ids` could be `None` once active_connection_id_limit is set to 1 by peer
+144        let last_cid = match ids.last() {
+145            Some(cid) => cid,
+146            None => return,
+147        };
+148        self.issued += ids.len() as u64;
+149        // Record the timestamp of CID with the largest seq number
+150        let sequence = last_cid.sequence;
+151        ids.iter().for_each(|frame| {
+152            self.active_seq.insert(frame.sequence);
+153        });
+154        self.track_lifetime(sequence, now);
+155    }
+156
+157    /// Update CidState for receipt of a `RETIRE_CONNECTION_ID` frame
+158    ///
+159    /// Returns whether a new CID can be issued, or an error if the frame was illegal.
+160    pub(crate) fn on_cid_retirement(
+161        &mut self,
+162        sequence: u64,
+163        limit: u64,
+164    ) -> Result<bool, TransportError> {
+165        if self.cid_len == 0 {
+166            return Err(TransportError::PROTOCOL_VIOLATION(
+167                "RETIRE_CONNECTION_ID when CIDs aren't in use",
+168            ));
+169        }
+170        if sequence > self.issued {
+171            debug!(
+172                sequence,
+173                "got RETIRE_CONNECTION_ID for unissued sequence number"
+174            );
+175            return Err(TransportError::PROTOCOL_VIOLATION(
+176                "RETIRE_CONNECTION_ID for unissued sequence number",
+177            ));
+178        }
+179        self.active_seq.remove(&sequence);
+180        // Consider a scenario where peer A has active remote cid 0,1,2.
+181        // Peer B first send a NEW_CONNECTION_ID with cid 3 and retire_prior_to set to 1.
+182        // Peer A processes this NEW_CONNECTION_ID frame; update remote cid to 1,2,3
+183        // and meanwhile send a RETIRE_CONNECTION_ID to retire cid 0 to peer B.
+184        // If peer B doesn't check the cid limit here and send a new cid again, peer A will then face CONNECTION_ID_LIMIT_ERROR
+185        Ok(limit > self.active_seq.len() as u64)
+186    }
+187
+188    /// Length of local Connection IDs
+189    pub(crate) fn cid_len(&self) -> usize {
+190        self.cid_len
+191    }
+192
+193    /// The value for `retire_prior_to` field in `NEW_CONNECTION_ID` frame
+194    pub(crate) fn retire_prior_to(&self) -> u64 {
+195        self.retire_seq
+196    }
+197
+198    pub(crate) fn active_seq(&self) -> (u64, u64) {
+199        let mut min = u64::MAX;
+200        let mut max = u64::MIN;
+201        for n in self.active_seq.iter() {
+202            if n < &min {
+203                min = *n;
+204            }
+205            if n > &max {
+206                max = *n;
+207            }
+208        }
+209        (min, max)
+210    }
+211
+212    #[cfg(test)]
+213    pub(crate) fn assign_retire_seq(&mut self, v: u64) -> u64 {
+214        // Cannot retire more CIDs than what have been issued
+215        debug_assert!(v <= *self.active_seq.iter().max().unwrap() + 1);
+216        let n = v.checked_sub(self.retire_seq).unwrap();
+217        self.retire_seq = v;
+218        n
+219    }
+220}
+221
+222/// Data structure that records when issued cids should be retired
+223#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+224struct CidTimestamp {
+225    /// Highest cid sequence number created in a batch
+226    sequence: u64,
+227    /// Timestamp when cid needs to be retired
+228    timestamp: Instant,
+229}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/datagrams.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/datagrams.rs.html new file mode 100644 index 000000000..82006587d --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/datagrams.rs.html @@ -0,0 +1,214 @@ +datagrams.rs - source

iroh_quinn_proto/connection/
datagrams.rs

1use std::collections::VecDeque;
+2
+3use bytes::{BufMut, Bytes};
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::Connection;
+8use crate::{
+9    TransportError,
+10    frame::{Datagram, FrameStruct},
+11};
+12
+13/// API to control datagram traffic
+14pub struct Datagrams<'a> {
+15    pub(super) conn: &'a mut Connection,
+16}
+17
+18impl Datagrams<'_> {
+19    /// Queue an unreliable, unordered datagram for immediate transmission
+20    ///
+21    /// If `drop` is true, previously queued datagrams which are still unsent may be discarded to
+22    /// make space for this datagram, in order of oldest to newest. If `drop` is false, and there
+23    /// isn't enough space due to previously queued datagrams, this function will return
+24    /// `SendDatagramError::Blocked`. `Event::DatagramsUnblocked` will be emitted once datagrams
+25    /// have been sent.
+26    ///
+27    /// Returns `Err` iff a `len`-byte datagram cannot currently be sent.
+28    pub fn send(&mut self, data: Bytes, drop: bool) -> Result<(), SendDatagramError> {
+29        if self.conn.config.datagram_receive_buffer_size.is_none() {
+30            return Err(SendDatagramError::Disabled);
+31        }
+32        let max = self
+33            .max_size()
+34            .ok_or(SendDatagramError::UnsupportedByPeer)?;
+35        if data.len() > max {
+36            return Err(SendDatagramError::TooLarge);
+37        }
+38        if drop {
+39            while self.conn.datagrams.outgoing_total > self.conn.config.datagram_send_buffer_size {
+40                let prev = self
+41                    .conn
+42                    .datagrams
+43                    .outgoing
+44                    .pop_front()
+45                    .expect("datagrams.outgoing_total desynchronized");
+46                trace!(len = prev.data.len(), "dropping outgoing datagram");
+47                self.conn.datagrams.outgoing_total -= prev.data.len();
+48            }
+49        } else if self.conn.datagrams.outgoing_total + data.len()
+50            > self.conn.config.datagram_send_buffer_size
+51        {
+52            self.conn.datagrams.send_blocked = true;
+53            return Err(SendDatagramError::Blocked(data));
+54        }
+55        self.conn.datagrams.outgoing_total += data.len();
+56        self.conn.datagrams.outgoing.push_back(Datagram { data });
+57        Ok(())
+58    }
+59
+60    /// Compute the maximum size of datagrams that may be passed to `send_datagram`
+61    ///
+62    /// Returns `None` if datagrams are unsupported by the peer or disabled locally.
+63    ///
+64    /// This may change over the lifetime of a connection according to variation in the path MTU
+65    /// estimate. The peer can also enforce an arbitrarily small fixed limit, but if the peer's
+66    /// limit is large this is guaranteed to be a little over a kilobyte at minimum.
+67    ///
+68    /// Not necessarily the maximum size of received datagrams.
+69    ///
+70    /// When multipath is enabled, this is calculated using the smallest MTU across all
+71    /// available paths.
+72    pub fn max_size(&self) -> Option<usize> {
+73        // We use the conservative overhead bound for any packet number, reducing the budget by at
+74        // most 3 bytes, so that PN size fluctuations don't cause users sending maximum-size
+75        // datagrams to suffer avoidable packet loss.
+76        let max_size = self.conn.current_mtu() as usize
+77            - self.conn.predict_1rtt_overhead_no_pn()
+78            - Datagram::SIZE_BOUND;
+79        let limit = self
+80            .conn
+81            .peer_params
+82            .max_datagram_frame_size?
+83            .into_inner()
+84            .saturating_sub(Datagram::SIZE_BOUND as u64);
+85        Some(limit.min(max_size as u64) as usize)
+86    }
+87
+88    /// Receive an unreliable, unordered datagram
+89    pub fn recv(&mut self) -> Option<Bytes> {
+90        self.conn.datagrams.recv()
+91    }
+92
+93    /// Bytes available in the outgoing datagram buffer
+94    ///
+95    /// When greater than zero, [`send`](Self::send)ing a datagram of at most this size is
+96    /// guaranteed not to cause older datagrams to be dropped.
+97    pub fn send_buffer_space(&self) -> usize {
+98        self.conn
+99            .config
+100            .datagram_send_buffer_size
+101            .saturating_sub(self.conn.datagrams.outgoing_total)
+102    }
+103}
+104
+105#[derive(Default)]
+106pub(super) struct DatagramState {
+107    /// Number of bytes of datagrams that have been received by the local transport but not
+108    /// delivered to the application
+109    pub(super) recv_buffered: usize,
+110    pub(super) incoming: VecDeque<Datagram>,
+111    pub(super) outgoing: VecDeque<Datagram>,
+112    pub(super) outgoing_total: usize,
+113    pub(super) send_blocked: bool,
+114}
+115
+116impl DatagramState {
+117    pub(super) fn received(
+118        &mut self,
+119        datagram: Datagram,
+120        window: &Option<usize>,
+121    ) -> Result<bool, TransportError> {
+122        let window = match window {
+123            None => {
+124                return Err(TransportError::PROTOCOL_VIOLATION(
+125                    "unexpected DATAGRAM frame",
+126                ));
+127            }
+128            Some(x) => *x,
+129        };
+130
+131        if datagram.data.len() > window {
+132            return Err(TransportError::PROTOCOL_VIOLATION("oversized datagram"));
+133        }
+134
+135        let was_empty = self.recv_buffered == 0;
+136        while datagram.data.len() + self.recv_buffered > window {
+137            debug!("dropping stale datagram");
+138            self.recv();
+139        }
+140
+141        self.recv_buffered += datagram.data.len();
+142        self.incoming.push_back(datagram);
+143        Ok(was_empty)
+144    }
+145
+146    /// Discard outgoing datagrams with a payload larger than `max_payload` bytes
+147    ///
+148    /// Used to ensure that reductions in MTU don't get us stuck in a state where we have a datagram
+149    /// queued but can't send it.
+150    pub(super) fn drop_oversized(&mut self, max_payload: usize) {
+151        self.outgoing.retain(|datagram| {
+152            let result = datagram.data.len() < max_payload;
+153            if !result {
+154                trace!(
+155                    "dropping {} byte datagram violating {} byte limit",
+156                    datagram.data.len(),
+157                    max_payload
+158                );
+159                self.outgoing_total -= datagram.data.len();
+160            }
+161            result
+162        });
+163    }
+164
+165    /// Attempt to write a datagram frame into `buf`, consuming it from `self.outgoing`
+166    ///
+167    /// Returns whether a frame was written. At most `max_size` bytes will be written, including
+168    /// framing.
+169    pub(super) fn write(&mut self, buf: &mut impl BufMut) -> bool {
+170        let datagram = match self.outgoing.pop_front() {
+171            Some(x) => x,
+172            None => return false,
+173        };
+174
+175        if buf.remaining_mut() < datagram.size(true) {
+176            // Future work: we could be more clever about cramming small datagrams into
+177            // mostly-full packets when a larger one is queued first
+178            self.outgoing.push_front(datagram);
+179            return false;
+180        }
+181
+182        trace!(len = datagram.data.len(), "DATAGRAM");
+183
+184        self.outgoing_total -= datagram.data.len();
+185        datagram.encode(true, buf);
+186        true
+187    }
+188
+189    pub(super) fn recv(&mut self) -> Option<Bytes> {
+190        let x = self.incoming.pop_front()?.data;
+191        self.recv_buffered -= x.len();
+192        Some(x)
+193    }
+194}
+195
+196/// Errors that can arise when sending a datagram
+197#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+198pub enum SendDatagramError {
+199    /// The peer does not support receiving datagram frames
+200    #[error("datagrams not supported by peer")]
+201    UnsupportedByPeer,
+202    /// Datagram support is disabled locally
+203    #[error("datagram support disabled")]
+204    Disabled,
+205    /// The datagram is larger than the connection can currently accommodate
+206    ///
+207    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
+208    /// exceeded.
+209    #[error("datagram too large")]
+210    TooLarge,
+211    /// Send would block
+212    #[error("datagram send blocked")]
+213    Blocked(Bytes),
+214}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/mod.rs.html new file mode 100644 index 000000000..3fe7cf64b --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -0,0 +1,6690 @@ +mod.rs - source

iroh_quinn_proto/connection/
mod.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, VecDeque, btree_map},
+4    convert::TryFrom,
+5    fmt, io, mem,
+6    net::{IpAddr, SocketAddr},
+7    num::NonZeroU32,
+8    ops::Not,
+9    sync::Arc,
+10};
+11
+12use bytes::{BufMut, Bytes, BytesMut};
+13use frame::StreamMetaVec;
+14
+15use rand::{Rng, SeedableRng, rngs::StdRng};
+16use rustc_hash::{FxHashMap, FxHashSet};
+17use thiserror::Error;
+18use tracing::{debug, error, trace, trace_span, warn};
+19
+20use crate::{
+21    Dir, Duration, EndpointConfig, Frame, INITIAL_MTU, Instant, MAX_CID_SIZE, MAX_STREAM_COUNT,
+22    MIN_INITIAL_SIZE, Side, StreamId, TIMER_GRANULARITY, TokenStore, Transmit, TransportError,
+23    TransportErrorCode, VarInt,
+24    cid_generator::ConnectionIdGenerator,
+25    cid_queue::CidQueue,
+26    coding::BufMutExt,
+27    config::{ServerConfig, TransportConfig},
+28    congestion::Controller,
+29    connection::{
+30        qlog::{QlogRecvPacket, QlogSentPacket, QlogSink},
+31        spaces::LostPacket,
+32        timer::{ConnTimer, PathTimer},
+33    },
+34    crypto::{self, KeyPair, Keys, PacketKey},
+35    frame::{self, Close, Datagram, FrameStruct, NewToken, ObservedAddr},
+36    iroh_hp,
+37    packet::{
+38        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet,
+39        PacketNumber, PartialDecode, SpaceId,
+40    },
+41    range_set::ArrayRangeSet,
+42    shared::{
+43        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+44        EndpointEvent, EndpointEventInner,
+45    },
+46    token::{ResetToken, Token, TokenPayload},
+47    transport_parameters::TransportParameters,
+48};
+49
+50mod ack_frequency;
+51use ack_frequency::AckFrequencyState;
+52
+53mod assembler;
+54pub use assembler::Chunk;
+55
+56mod cid_state;
+57use cid_state::CidState;
+58
+59mod datagrams;
+60use datagrams::DatagramState;
+61pub use datagrams::{Datagrams, SendDatagramError};
+62
+63mod mtud;
+64mod pacing;
+65
+66mod packet_builder;
+67use packet_builder::{PacketBuilder, PadDatagram};
+68
+69mod packet_crypto;
+70use packet_crypto::{PrevCrypto, ZeroRttCrypto};
+71
+72mod paths;
+73pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError};
+74use paths::{PathData, PathState};
+75
+76pub(crate) mod qlog;
+77
+78mod send_buffer;
+79
+80mod spaces;
+81#[cfg(fuzzing)]
+82pub use spaces::Retransmits;
+83#[cfg(not(fuzzing))]
+84use spaces::Retransmits;
+85use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits};
+86
+87mod stats;
+88pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats};
+89
+90mod streams;
+91#[cfg(fuzzing)]
+92pub use streams::StreamsState;
+93#[cfg(not(fuzzing))]
+94use streams::StreamsState;
+95pub use streams::{
+96    Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream,
+97    ShouldTransmit, StreamEvent, Streams, WriteError, Written,
+98};
+99
+100mod timer;
+101use timer::{Timer, TimerTable};
+102
+103mod transmit_buf;
+104use transmit_buf::TransmitBuf;
+105
+106mod state;
+107
+108#[cfg(not(fuzzing))]
+109use state::State;
+110#[cfg(fuzzing)]
+111pub use state::State;
+112use state::StateType;
+113
+114/// Protocol state and logic for a single QUIC connection
+115///
+116/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application
+117/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and
+118/// expects timeouts through various methods. A number of simple getter methods are exposed
+119/// to allow callers to inspect some of the connection state.
+120///
+121/// `Connection` has roughly 4 types of methods:
+122///
+123/// - A. Simple getters, taking `&self`
+124/// - B. Handlers for incoming events from the network or system, named `handle_*`.
+125/// - C. State machine mutators, for incoming commands from the application. For convenience we
+126///   refer to this as "performing I/O" below, however as per the design of this library none of the
+127///   functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and
+128///   [`write`](SendStream::write), but also things like [`reset`](SendStream::reset).
+129/// - D. Polling functions for outgoing events or actions for the caller to
+130///   take, named `poll_*`.
+131///
+132/// The simplest way to use this API correctly is to call (B) and (C) whenever
+133/// appropriate, then after each of those calls, as soon as feasible call all
+134/// polling methods (D) and deal with their outputs appropriately, e.g. by
+135/// passing it to the application or by making a system-level I/O call. You
+136/// should call the polling functions in this order:
+137///
+138/// 1. [`poll_transmit`](Self::poll_transmit)
+139/// 2. [`poll_timeout`](Self::poll_timeout)
+140/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events)
+141/// 4. [`poll`](Self::poll)
+142///
+143/// Currently the only actual dependency is from (2) to (1), however additional
+144/// dependencies may be added in future, so the above order is recommended.
+145///
+146/// (A) may be called whenever desired.
+147///
+148/// Care should be made to ensure that the input events represent monotonically
+149/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout)
+150/// with events of the same [`Instant`] may be interleaved in any order with a
+151/// call to [`handle_event`](Self::handle_event) at that same instant; however
+152/// events or timeouts with different instants must not be interleaved.
+153pub struct Connection {
+154    endpoint_config: Arc<EndpointConfig>,
+155    config: Arc<TransportConfig>,
+156    rng: StdRng,
+157    crypto: Box<dyn crypto::Session>,
+158    /// The CID we initially chose, for use during the handshake
+159    handshake_cid: ConnectionId,
+160    /// The CID the peer initially chose, for use during the handshake
+161    rem_handshake_cid: ConnectionId,
+162    /// The "real" local IP address which was was used to receive the initial packet.
+163    /// This is only populated for the server case, and if known
+164    local_ip: Option<IpAddr>,
+165    /// The [`PathData`] for each path
+166    ///
+167    /// This needs to be ordered because [`Connection::poll_transmit`] needs to
+168    /// deterministically select the next PathId to send on.
+169    // TODO(flub): well does it really? But deterministic is nice for now.
+170    paths: BTreeMap<PathId, PathState>,
+171    /// Incremented every time we see a new path
+172    ///
+173    /// Stored separately from `path.generation` to account for aborted migrations
+174    path_counter: u64,
+175    /// Whether MTU detection is supported in this environment
+176    allow_mtud: bool,
+177    state: State,
+178    side: ConnectionSide,
+179    /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance.
+180    zero_rtt_enabled: bool,
+181    /// Set if 0-RTT is supported, then cleared when no longer needed.
+182    zero_rtt_crypto: Option<ZeroRttCrypto>,
+183    key_phase: bool,
+184    /// How many packets are in the current key phase. Used only for `Data` space.
+185    key_phase_size: u64,
+186    /// Transport parameters set by the peer
+187    peer_params: TransportParameters,
+188    /// Source ConnectionId of the first packet received from the peer
+189    orig_rem_cid: ConnectionId,
+190    /// Destination ConnectionId sent by the client on the first Initial
+191    initial_dst_cid: ConnectionId,
+192    /// The value that the server included in the Source Connection ID field of a Retry packet, if
+193    /// one was received
+194    retry_src_cid: Option<ConnectionId>,
+195    /// Events returned by [`Connection::poll`]
+196    events: VecDeque<Event>,
+197    endpoint_events: VecDeque<EndpointEventInner>,
+198    /// Whether the spin bit is in use for this connection
+199    spin_enabled: bool,
+200    /// Outgoing spin bit state
+201    spin: bool,
+202    /// Packet number spaces: initial, handshake, 1-RTT
+203    spaces: [PacketSpace; 3],
+204    /// Highest usable [`SpaceId`]
+205    highest_space: SpaceId,
+206    /// 1-RTT keys used prior to a key update
+207    prev_crypto: Option<PrevCrypto>,
+208    /// 1-RTT keys to be used for the next key update
+209    ///
+210    /// These are generated in advance to prevent timing attacks and/or DoS by third-party attackers
+211    /// spoofing key updates.
+212    next_crypto: Option<KeyPair<Box<dyn PacketKey>>>,
+213    accepted_0rtt: bool,
+214    /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted.
+215    permit_idle_reset: bool,
+216    /// Negotiated idle timeout
+217    idle_timeout: Option<Duration>,
+218    timers: TimerTable,
+219    /// Number of packets received which could not be authenticated
+220    authentication_failures: u64,
+221
+222    //
+223    // Queued non-retransmittable 1-RTT data
+224    //
+225    /// If the CONNECTION_CLOSE frame needs to be sent
+226    close: bool,
+227
+228    //
+229    // ACK frequency
+230    //
+231    ack_frequency: AckFrequencyState,
+232
+233    //
+234    // Congestion Control
+235    //
+236    /// Whether the most recently received packet had an ECN codepoint set
+237    receiving_ecn: bool,
+238    /// Number of packets authenticated
+239    total_authed_packets: u64,
+240    /// Whether the last `poll_transmit` call yielded no data because there was
+241    /// no outgoing application data.
+242    app_limited: bool,
+243
+244    //
+245    // ObservedAddr
+246    //
+247    /// Sequence number for the next observed address frame sent to the peer.
+248    next_observed_addr_seq_no: VarInt,
+249
+250    streams: StreamsState,
+251    /// Surplus remote CIDs for future use on new paths
+252    ///
+253    /// These are given out before multiple paths exist, also for paths that will never
+254    /// exist.  So if multipath is supported the number of paths here will be higher than
+255    /// the actual number of paths in use.
+256    rem_cids: FxHashMap<PathId, CidQueue>,
+257    /// Attributes of CIDs generated by local endpoint
+258    ///
+259    /// Any path that is allowed to be opened is present in this map, as well as the already
+260    /// opened paths. However since CIDs are issued async by the endpoint driver via
+261    /// connection events it can not be used to know if CIDs have been issued for a path or
+262    /// not. See [`Connection::max_path_id_with_cids`] for this.
+263    local_cid_state: FxHashMap<PathId, CidState>,
+264    /// State of the unreliable datagram extension
+265    datagrams: DatagramState,
+266    /// Connection level statistics
+267    stats: ConnectionStats,
+268    /// Path level statistics
+269    path_stats: FxHashMap<PathId, PathStats>,
+270    /// QUIC version used for the connection.
+271    version: u32,
+272
+273    //
+274    // Multipath
+275    //
+276    /// Maximum number of concurrent paths
+277    ///
+278    /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even
+279    /// when multipath is disabled this will be set to 1, it is not used in that case
+280    /// though.
+281    max_concurrent_paths: NonZeroU32,
+282    /// Local maximum [`PathId`] to be used
+283    ///
+284    /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath
+285    /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of
+286    /// the highest MAX_PATH_ID frame sent.
+287    ///
+288    /// Any path with an ID equal or below this [`PathId`] is either:
+289    ///
+290    /// - Abandoned, if it is also in [`Connection::abandoned_paths`].
+291    /// - Open, in this case it is present in [`Connection::paths`]
+292    /// - Not yet opened, if it is in neither of these two places.
+293    ///
+294    /// Note that for not-yet-open there may or may not be any CIDs issued. See
+295    /// [`Connection::max_path_id_with_cids`].
+296    local_max_path_id: PathId,
+297    /// Remote's maximum [`PathId`] to be used
+298    ///
+299    /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when
+300    /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit
+301    /// by sending [`Frame::MaxPathId`] frames.
+302    remote_max_path_id: PathId,
+303    /// The greatest [`PathId`] we have issued CIDs for
+304    ///
+305    /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not
+306    /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued
+307    /// since they are issued asynchronously by the endpoint driver.
+308    max_path_id_with_cids: PathId,
+309    /// The paths already abandoned
+310    ///
+311    /// They may still have some state left in [`Connection::paths`] or
+312    /// [`Connection::local_cid_state`] since some of this has to be kept around for some
+313    /// time after a path is abandoned.
+314    // TODO(flub): Make this a more efficient data structure.  Like ranges of abandoned
+315    //    paths.  Or a set together with a minimum.  Or something.
+316    abandoned_paths: FxHashSet<PathId>,
+317
+318    iroh_hp: iroh_hp::State,
+319    qlog: QlogSink,
+320}
+321
+322impl Connection {
+323    pub(crate) fn new(
+324        endpoint_config: Arc<EndpointConfig>,
+325        config: Arc<TransportConfig>,
+326        init_cid: ConnectionId,
+327        loc_cid: ConnectionId,
+328        rem_cid: ConnectionId,
+329        remote: SocketAddr,
+330        local_ip: Option<IpAddr>,
+331        crypto: Box<dyn crypto::Session>,
+332        cid_gen: &dyn ConnectionIdGenerator,
+333        now: Instant,
+334        version: u32,
+335        allow_mtud: bool,
+336        rng_seed: [u8; 32],
+337        side_args: SideArgs,
+338        qlog: QlogSink,
+339    ) -> Self {
+340        let pref_addr_cid = side_args.pref_addr_cid();
+341        let path_validated = side_args.path_validated();
+342        let connection_side = ConnectionSide::from(side_args);
+343        let side = connection_side.side();
+344        let mut rng = StdRng::from_seed(rng_seed);
+345        let initial_space = {
+346            let mut space = PacketSpace::new(now, SpaceId::Initial, &mut rng);
+347            space.crypto = Some(crypto.initial_keys(init_cid, side));
+348            space
+349        };
+350        let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng);
+351        #[cfg(test)]
+352        let data_space = match config.deterministic_packet_numbers {
+353            true => PacketSpace::new_deterministic(now, SpaceId::Data),
+354            false => PacketSpace::new(now, SpaceId::Data, &mut rng),
+355        };
+356        #[cfg(not(test))]
+357        let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng);
+358        let state = State::handshake(state::Handshake {
+359            rem_cid_set: side.is_server(),
+360            expected_token: Bytes::new(),
+361            client_hello: None,
+362            allow_server_migration: side.is_client(),
+363        });
+364        let local_cid_state = FxHashMap::from_iter([(
+365            PathId::ZERO,
+366            CidState::new(
+367                cid_gen.cid_len(),
+368                cid_gen.cid_lifetime(),
+369                now,
+370                if pref_addr_cid.is_some() { 2 } else { 1 },
+371            ),
+372        )]);
+373
+374        let mut path = PathData::new(remote, allow_mtud, None, 0, now, &config);
+375        // TODO(@divma): consider if we want to delay this until the path is validated
+376        path.open = true;
+377        let mut this = Self {
+378            endpoint_config,
+379            crypto,
+380            handshake_cid: loc_cid,
+381            rem_handshake_cid: rem_cid,
+382            local_cid_state,
+383            paths: BTreeMap::from_iter([(
+384                PathId::ZERO,
+385                PathState {
+386                    data: path,
+387                    prev: None,
+388                },
+389            )]),
+390            path_counter: 0,
+391            allow_mtud,
+392            local_ip,
+393            state,
+394            side: connection_side,
+395            zero_rtt_enabled: false,
+396            zero_rtt_crypto: None,
+397            key_phase: false,
+398            // A small initial key phase size ensures peers that don't handle key updates correctly
+399            // fail sooner rather than later. It's okay for both peers to do this, as the first one
+400            // to perform an update will reset the other's key phase size in `update_keys`, and a
+401            // simultaneous key update by both is just like a regular key update with a really fast
+402            // response. Inspired by quic-go's similar behavior of performing the first key update
+403            // at the 100th short-header packet.
+404            key_phase_size: rng.random_range(10..1000),
+405            peer_params: TransportParameters::default(),
+406            orig_rem_cid: rem_cid,
+407            initial_dst_cid: init_cid,
+408            retry_src_cid: None,
+409            events: VecDeque::new(),
+410            endpoint_events: VecDeque::new(),
+411            spin_enabled: config.allow_spin && rng.random_ratio(7, 8),
+412            spin: false,
+413            spaces: [initial_space, handshake_space, data_space],
+414            highest_space: SpaceId::Initial,
+415            prev_crypto: None,
+416            next_crypto: None,
+417            accepted_0rtt: false,
+418            permit_idle_reset: true,
+419            idle_timeout: match config.max_idle_timeout {
+420                None | Some(VarInt(0)) => None,
+421                Some(dur) => Some(Duration::from_millis(dur.0)),
+422            },
+423            timers: TimerTable::default(),
+424            authentication_failures: 0,
+425            close: false,
+426
+427            ack_frequency: AckFrequencyState::new(get_max_ack_delay(
+428                &TransportParameters::default(),
+429            )),
+430
+431            app_limited: false,
+432            receiving_ecn: false,
+433            total_authed_packets: 0,
+434
+435            next_observed_addr_seq_no: 0u32.into(),
+436
+437            streams: StreamsState::new(
+438                side,
+439                config.max_concurrent_uni_streams,
+440                config.max_concurrent_bidi_streams,
+441                config.send_window,
+442                config.receive_window,
+443                config.stream_receive_window,
+444            ),
+445            datagrams: DatagramState::default(),
+446            config,
+447            rem_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(rem_cid))]),
+448            rng,
+449            stats: ConnectionStats::default(),
+450            path_stats: Default::default(),
+451            version,
+452
+453            // peer params are not yet known, so multipath is not enabled
+454            max_concurrent_paths: NonZeroU32::MIN,
+455            local_max_path_id: PathId::ZERO,
+456            remote_max_path_id: PathId::ZERO,
+457            max_path_id_with_cids: PathId::ZERO,
+458            abandoned_paths: Default::default(),
+459
+460            // iroh's nat traversal
+461            iroh_hp: Default::default(),
+462            qlog,
+463        };
+464        if path_validated {
+465            this.on_path_validated(PathId::ZERO);
+466        }
+467        if side.is_client() {
+468            // Kick off the connection
+469            this.write_crypto();
+470            this.init_0rtt(now);
+471        }
+472        this.qlog.emit_tuple_assigned(PathId::ZERO, remote, now);
+473        this
+474    }
+475
+476    /// Returns the next time at which `handle_timeout` should be called
+477    ///
+478    /// The value returned may change after:
+479    /// - the application performed some I/O on the connection
+480    /// - a call was made to `handle_event`
+481    /// - a call to `poll_transmit` returned `Some`
+482    /// - a call was made to `handle_timeout`
+483    #[must_use]
+484    pub fn poll_timeout(&mut self) -> Option<Instant> {
+485        self.timers.peek()
+486    }
+487
+488    /// Returns application-facing events
+489    ///
+490    /// Connections should be polled for events after:
+491    /// - a call was made to `handle_event`
+492    /// - a call was made to `handle_timeout`
+493    #[must_use]
+494    pub fn poll(&mut self) -> Option<Event> {
+495        if let Some(x) = self.events.pop_front() {
+496            return Some(x);
+497        }
+498
+499        if let Some(event) = self.streams.poll() {
+500            return Some(Event::Stream(event));
+501        }
+502
+503        if let Some(reason) = self.state.take_error() {
+504            return Some(Event::ConnectionLost { reason });
+505        }
+506
+507        None
+508    }
+509
+510    /// Return endpoint-facing events
+511    #[must_use]
+512    pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> {
+513        self.endpoint_events.pop_front().map(EndpointEvent)
+514    }
+515
+516    /// Provide control over streams
+517    #[must_use]
+518    pub fn streams(&mut self) -> Streams<'_> {
+519        Streams {
+520            state: &mut self.streams,
+521            conn_state: &self.state,
+522        }
+523    }
+524
+525    /// Provide control over streams
+526    #[must_use]
+527    pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> {
+528        assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side());
+529        RecvStream {
+530            id,
+531            state: &mut self.streams,
+532            pending: &mut self.spaces[SpaceId::Data].pending,
+533        }
+534    }
+535
+536    /// Provide control over streams
+537    #[must_use]
+538    pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> {
+539        assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side());
+540        SendStream {
+541            id,
+542            state: &mut self.streams,
+543            pending: &mut self.spaces[SpaceId::Data].pending,
+544            conn_state: &self.state,
+545        }
+546    }
+547
+548    /// Opens a new path only if no path to the remote address exists so far
+549    ///
+550    /// See [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id,
+551    /// false)` if was opened.
+552    ///
+553    /// [`open_path`]: Connection::open_path
+554    pub fn open_path_ensure(
+555        &mut self,
+556        remote: SocketAddr,
+557        initial_status: PathStatus,
+558        now: Instant,
+559    ) -> Result<(PathId, bool), PathError> {
+560        match self
+561            .paths
+562            .iter()
+563            .find(|(_id, path)| path.data.remote == remote)
+564        {
+565            Some((path_id, _state)) => Ok((*path_id, true)),
+566            None => self
+567                .open_path(remote, initial_status, now)
+568                .map(|id| (id, false)),
+569        }
+570    }
+571
+572    /// Opens a new path
+573    ///
+574    /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events.
+575    /// When the path is opened it will be reported as an [`PathEvent::Opened`].
+576    pub fn open_path(
+577        &mut self,
+578        remote: SocketAddr,
+579        initial_status: PathStatus,
+580        now: Instant,
+581    ) -> Result<PathId, PathError> {
+582        if !self.is_multipath_negotiated() {
+583            return Err(PathError::MultipathNotNegotiated);
+584        }
+585        if self.side().is_server() {
+586            return Err(PathError::ServerSideNotAllowed);
+587        }
+588
+589        let max_abandoned = self.abandoned_paths.iter().max().copied();
+590        let max_used = self.paths.keys().last().copied();
+591        let path_id = max_abandoned
+592            .max(max_used)
+593            .unwrap_or(PathId::ZERO)
+594            .saturating_add(1u8);
+595
+596        if Some(path_id) > self.max_path_id() {
+597            return Err(PathError::MaxPathIdReached);
+598        }
+599        if path_id > self.remote_max_path_id {
+600            self.spaces[SpaceId::Data].pending.paths_blocked = true;
+601            return Err(PathError::MaxPathIdReached);
+602        }
+603        if self.rem_cids.get(&path_id).map(CidQueue::active).is_none() {
+604            self.spaces[SpaceId::Data]
+605                .pending
+606                .path_cids_blocked
+607                .push(path_id);
+608            return Err(PathError::RemoteCidsExhausted);
+609        }
+610
+611        let path = self.ensure_path(path_id, remote, now, None);
+612        path.status.local_update(initial_status);
+613
+614        Ok(path_id)
+615    }
+616
+617    /// Closes a path by sending a PATH_ABANDON frame
+618    ///
+619    /// This will not allow closing the last path. It does allow closing paths which have
+620    /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer
+621    /// for a path that was never opened locally.
+622    pub fn close_path(
+623        &mut self,
+624        now: Instant,
+625        path_id: PathId,
+626        error_code: VarInt,
+627    ) -> Result<(), ClosePathError> {
+628        if self.abandoned_paths.contains(&path_id)
+629            || Some(path_id) > self.max_path_id()
+630            || !self.paths.contains_key(&path_id)
+631        {
+632            return Err(ClosePathError::ClosedPath);
+633        }
+634        if self
+635            .paths
+636            .iter()
+637            // Would there be any remaining, non-abandoned, validated paths
+638            .any(|(id, path)| {
+639                *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated
+640            })
+641            .not()
+642        {
+643            return Err(ClosePathError::LastOpenPath);
+644        }
+645
+646        // Send PATH_ABANDON
+647        self.spaces[SpaceId::Data]
+648            .pending
+649            .path_abandon
+650            .insert(path_id, error_code.into());
+651
+652        // Remove pending NEW CIDs for this path
+653        let pending_space = &mut self.spaces[SpaceId::Data].pending;
+654        pending_space.new_cids.retain(|cid| cid.path_id != path_id);
+655        pending_space.path_cids_blocked.retain(|&id| id != path_id);
+656        pending_space.path_status.retain(|&id| id != path_id);
+657
+658        // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths)
+659        for space in self.spaces[SpaceId::Data].iter_paths_mut() {
+660            for sent_packet in space.sent_packets.values_mut() {
+661                if let Some(retransmits) = sent_packet.retransmits.get_mut() {
+662                    retransmits.new_cids.retain(|cid| cid.path_id != path_id);
+663                    retransmits.path_cids_blocked.retain(|&id| id != path_id);
+664                    retransmits.path_status.retain(|&id| id != path_id);
+665                }
+666            }
+667        }
+668
+669        // Consider remotely issued CIDs as retired.
+670        // Technically we don't have to do this just yet.  We only need to do this *after*
+671        // the ABANDON_PATH frame is sent, allowing us to still send it on the
+672        // to-be-abandoned path.  However it is recommended to send it on another path, and
+673        // we do not allow abandoning the last path anyway.
+674        self.rem_cids.remove(&path_id);
+675        self.endpoint_events
+676            .push_back(EndpointEventInner::RetireResetToken(path_id));
+677
+678        let pto = self.pto_max_path(SpaceId::Data);
+679
+680        let path = self.paths.get_mut(&path_id).expect("checked above");
+681
+682        // We record the time at which receiving data on this path generates a transport error.
+683        path.data.last_allowed_receive = Some(now + 3 * pto);
+684        self.abandoned_paths.insert(path_id);
+685
+686        self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8));
+687
+688        // The peer MUST respond with a corresponding PATH_ABANDON frame.
+689        // If we receive packets on the abandoned path after 3 * PTO we trigger a transport error.
+690        // In any case, we completely discard the path after 6 * PTO whether we receive a
+691        // PATH_ABANDON or not.
+692        self.timers.set(
+693            Timer::PerPath(path_id, PathTimer::DiscardPath),
+694            now + 6 * pto,
+695            self.qlog.with_time(now),
+696        );
+697        Ok(())
+698    }
+699
+700    /// Gets the [`PathData`] for a known [`PathId`].
+701    ///
+702    /// Will panic if the path_id does not reference any known path.
+703    #[track_caller]
+704    fn path_data(&self, path_id: PathId) -> &PathData {
+705        if let Some(data) = self.paths.get(&path_id) {
+706            &data.data
+707        } else {
+708            panic!(
+709                "unknown path: {path_id}, currently known paths: {:?}",
+710                self.paths.keys().collect::<Vec<_>>()
+711            );
+712        }
+713    }
+714
+715    /// Gets a reference to the [`PathData`] for a [`PathId`]
+716    fn path(&self, path_id: PathId) -> Option<&PathData> {
+717        self.paths.get(&path_id).map(|path_state| &path_state.data)
+718    }
+719
+720    /// Gets a mutable reference to the [`PathData`] for a [`PathId`]
+721    fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> {
+722        self.paths
+723            .get_mut(&path_id)
+724            .map(|path_state| &mut path_state.data)
+725    }
+726
+727    /// Returns all known paths.
+728    ///
+729    /// There is no guarantee any of these paths are open or usable.
+730    pub fn paths(&self) -> Vec<PathId> {
+731        self.paths.keys().copied().collect()
+732    }
+733
+734    /// Gets the local [`PathStatus`] for a known [`PathId`]
+735    pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> {
+736        self.path(path_id)
+737            .map(PathData::local_status)
+738            .ok_or(ClosedPath { _private: () })
+739    }
+740
+741    /// Returns the path's remote socket address
+742    pub fn path_remote_address(&self, path_id: PathId) -> Result<SocketAddr, ClosedPath> {
+743        self.path(path_id)
+744            .map(|path| path.remote)
+745            .ok_or(ClosedPath { _private: () })
+746    }
+747
+748    /// Sets the [`PathStatus`] for a known [`PathId`]
+749    ///
+750    /// Returns the previous path status on success.
+751    pub fn set_path_status(
+752        &mut self,
+753        path_id: PathId,
+754        status: PathStatus,
+755    ) -> Result<PathStatus, SetPathStatusError> {
+756        if !self.is_multipath_negotiated() {
+757            return Err(SetPathStatusError::MultipathNotNegotiated);
+758        }
+759        let path = self
+760            .path_mut(path_id)
+761            .ok_or(SetPathStatusError::ClosedPath)?;
+762        let prev = match path.status.local_update(status) {
+763            Some(prev) => {
+764                self.spaces[SpaceId::Data]
+765                    .pending
+766                    .path_status
+767                    .insert(path_id);
+768                prev
+769            }
+770            None => path.local_status(),
+771        };
+772        Ok(prev)
+773    }
+774
+775    /// Returns the remote path status
+776    // TODO(flub): Probably should also be some kind of path event?  Not even sure if I like
+777    //    this as an API, but for now it allows me to write a test easily.
+778    // TODO(flub): Technically this should be a Result<Option<PathSTatus>>?
+779    pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> {
+780        self.path(path_id).and_then(|path| path.remote_status())
+781    }
+782
+783    /// Sets the max_idle_timeout for a specific path
+784    ///
+785    /// See [`TransportConfig::default_path_max_idle_timeout`] for details.
+786    ///
+787    /// Returns the previous value of the setting.
+788    pub fn set_path_max_idle_timeout(
+789        &mut self,
+790        path_id: PathId,
+791        timeout: Option<Duration>,
+792    ) -> Result<Option<Duration>, ClosedPath> {
+793        let path = self
+794            .paths
+795            .get_mut(&path_id)
+796            .ok_or(ClosedPath { _private: () })?;
+797        Ok(std::mem::replace(&mut path.data.idle_timeout, timeout))
+798    }
+799
+800    /// Sets the keep_alive_interval for a specific path
+801    ///
+802    /// See [`TransportConfig::default_path_keep_alive_interval`] for details.
+803    ///
+804    /// Returns the previous value of the setting.
+805    pub fn set_path_keep_alive_interval(
+806        &mut self,
+807        path_id: PathId,
+808        interval: Option<Duration>,
+809    ) -> Result<Option<Duration>, ClosedPath> {
+810        let path = self
+811            .paths
+812            .get_mut(&path_id)
+813            .ok_or(ClosedPath { _private: () })?;
+814        Ok(std::mem::replace(&mut path.data.keep_alive, interval))
+815    }
+816
+817    /// Gets the [`PathData`] for a known [`PathId`].
+818    ///
+819    /// Will panic if the path_id does not reference any known path.
+820    #[track_caller]
+821    fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData {
+822        &mut self.paths.get_mut(&path_id).expect("known path").data
+823    }
+824
+825    /// Check if the remote has been validated in any active path
+826    fn is_remote_validated(&self, remote: SocketAddr) -> bool {
+827        self.paths
+828            .values()
+829            .any(|path_state| path_state.data.remote == remote && path_state.data.validated)
+830        // TODO(@divma): we might want to ensure the path has been recently active to consider the
+831        // address validated
+832    }
+833
+834    fn ensure_path(
+835        &mut self,
+836        path_id: PathId,
+837        remote: SocketAddr,
+838        now: Instant,
+839        pn: Option<u64>,
+840    ) -> &mut PathData {
+841        let validated = self.is_remote_validated(remote);
+842        let vacant_entry = match self.paths.entry(path_id) {
+843            btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
+844            btree_map::Entry::Occupied(occupied_entry) => {
+845                return &mut occupied_entry.into_mut().data;
+846            }
+847        };
+848
+849        debug!(%validated, %path_id, ?remote, "path added");
+850        let peer_max_udp_payload_size =
+851            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+852        self.path_counter = self.path_counter.wrapping_add(1);
+853        let mut data = PathData::new(
+854            remote,
+855            self.allow_mtud,
+856            Some(peer_max_udp_payload_size),
+857            self.path_counter,
+858            now,
+859            &self.config,
+860        );
+861
+862        data.validated = validated;
+863
+864        let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base();
+865        self.timers.set(
+866            Timer::PerPath(path_id, PathTimer::PathOpen),
+867            now + 3 * pto,
+868            self.qlog.with_time(now),
+869        );
+870
+871        // for the path to be opened we need to send a packet on the path. Sending a challenge
+872        // guarantees this
+873        data.send_new_challenge = true;
+874
+875        let path = vacant_entry.insert(PathState { data, prev: None });
+876
+877        let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng);
+878        if let Some(pn) = pn {
+879            pn_space.dedup.insert(pn);
+880        }
+881        self.spaces[SpaceId::Data]
+882            .number_spaces
+883            .insert(path_id, pn_space);
+884        self.qlog.emit_tuple_assigned(path_id, remote, now);
+885        &mut path.data
+886    }
+887
+888    /// Returns packets to transmit
+889    ///
+890    /// Connections should be polled for transmit after:
+891    /// - the application performed some I/O on the connection
+892    /// - a call was made to `handle_event`
+893    /// - a call was made to `handle_timeout`
+894    ///
+895    /// `max_datagrams` specifies how many datagrams can be returned inside a
+896    /// single Transmit using GSO. This must be at least 1.
+897    #[must_use]
+898    pub fn poll_transmit(
+899        &mut self,
+900        now: Instant,
+901        max_datagrams: usize,
+902        buf: &mut Vec<u8>,
+903    ) -> Option<Transmit> {
+904        if let Some(probing) = self
+905            .iroh_hp
+906            .server_side_mut()
+907            .ok()
+908            .and_then(iroh_hp::ServerState::next_probe)
+909        {
+910            let destination = probing.remote();
+911            trace!(%destination, "RAND_DATA packet");
+912            let token: u64 = self.rng.random();
+913            buf.put_u64(token);
+914            probing.finish(token);
+915            return Some(Transmit {
+916                destination,
+917                ecn: None,
+918                size: 8,
+919                segment_size: None,
+920                src_ip: None,
+921            });
+922        }
+923
+924        assert!(max_datagrams != 0);
+925        let max_datagrams = match self.config.enable_segmentation_offload {
+926            false => 1,
+927            true => max_datagrams,
+928        };
+929
+930        // Each call to poll_transmit can only send datagrams to one destination, because
+931        // all datagrams in a GSO batch are for the same destination.  Therefore only
+932        // datagrams for one Path ID are produced for each poll_transmit call.
+933
+934        // TODO(flub): this is wishful thinking and not actually implemented, but perhaps it
+935        //   should be:
+936
+937        // First, if we have to send a close, select a path for that.
+938        // Next, all paths that have a PATH_CHALLENGE or PATH_RESPONSE pending.
+939
+940        // For all, open, validated and AVAILABLE paths:
+941        // - Is the path congestion blocked or pacing blocked?
+942        // - call maybe_queue_ to ensure a tail-loss probe would be sent?
+943        // - do we need to send a close message?
+944        // - call can_send
+945        // Once there's nothing more to send on the AVAILABLE paths, do the same for BACKUP paths
+946
+947        // Check whether we need to send a close message
+948        let close = match self.state.as_type() {
+949            StateType::Drained => {
+950                self.app_limited = true;
+951                return None;
+952            }
+953            StateType::Draining | StateType::Closed => {
+954                // self.close is only reset once the associated packet had been
+955                // encoded successfully
+956                if !self.close {
+957                    self.app_limited = true;
+958                    return None;
+959                }
+960                true
+961            }
+962            _ => false,
+963        };
+964
+965        // Check whether we need to send an ACK_FREQUENCY frame
+966        if let Some(config) = &self.config.ack_frequency_config {
+967            let rtt = self
+968                .paths
+969                .values()
+970                .map(|p| p.data.rtt.get())
+971                .min()
+972                .expect("one path exists");
+973            self.spaces[SpaceId::Data].pending.ack_frequency = self
+974                .ack_frequency
+975                .should_send_ack_frequency(rtt, config, &self.peer_params)
+976                && self.highest_space == SpaceId::Data
+977                && self.peer_supports_ack_frequency();
+978        }
+979
+980        // Whether this packet can be coalesced with another one in the same datagram.
+981        let mut coalesce = true;
+982
+983        // Whether the last packet in the datagram must be padded so the datagram takes up
+984        // to at least MIN_INITIAL_SIZE, or to the maximum segment size if this is smaller.
+985        let mut pad_datagram = PadDatagram::No;
+986
+987        // Whether congestion control stopped the next packet from being sent. Further
+988        // packets could still be built, as e.g. tail-loss probes are not congestion
+989        // limited.
+990        let mut congestion_blocked = false;
+991
+992        // The packet number of the last built packet.
+993        let mut last_packet_number = None;
+994
+995        let mut path_id = *self.paths.first_key_value().expect("one path must exist").0;
+996
+997        // If there is any open, validated and available path we only want to send frames to
+998        // any backup path that must be sent on that backup path exclusively.
+999        let have_available_path = self.paths.iter().any(|(id, path)| {
+1000            path.data.validated
+1001                && path.data.local_status() == PathStatus::Available
+1002                && self.rem_cids.contains_key(id)
+1003        });
+1004
+1005        // Setup for the first path_id
+1006        let mut transmit = TransmitBuf::new(
+1007            buf,
+1008            max_datagrams,
+1009            self.path_data(path_id).current_mtu().into(),
+1010        );
+1011        if let Some(challenge) = self.send_prev_path_challenge(now, &mut transmit, path_id) {
+1012            return Some(challenge);
+1013        }
+1014        let mut space_id = match path_id {
+1015            PathId::ZERO => SpaceId::Initial,
+1016            _ => SpaceId::Data,
+1017        };
+1018
+1019        loop {
+1020            // check if there is at least one active CID to use for sending
+1021            let Some(remote_cid) = self.rem_cids.get(&path_id).map(CidQueue::active) else {
+1022                let err = PathError::RemoteCidsExhausted;
+1023                if !self.abandoned_paths.contains(&path_id) {
+1024                    debug!(?err, %path_id, "no active CID for path");
+1025                    self.events.push_back(Event::Path(PathEvent::LocallyClosed {
+1026                        id: path_id,
+1027                        error: err,
+1028                    }));
+1029                    // Locally we should have refused to open this path, the remote should
+1030                    // have given us CIDs for this path before opening it.  So we can always
+1031                    // abandon this here.
+1032                    self.close_path(
+1033                        now,
+1034                        path_id,
+1035                        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH.into(),
+1036                    )
+1037                    .ok();
+1038                    self.spaces[SpaceId::Data]
+1039                        .pending
+1040                        .path_cids_blocked
+1041                        .push(path_id);
+1042                } else {
+1043                    trace!(%path_id, "remote CIDs retired for abandoned path");
+1044                }
+1045
+1046                match self.paths.keys().find(|&&next| next > path_id) {
+1047                    Some(next_path_id) => {
+1048                        // See if this next path can send anything.
+1049                        path_id = *next_path_id;
+1050                        space_id = SpaceId::Data;
+1051
+1052                        // update per path state
+1053                        transmit.set_segment_size(self.path_data(path_id).current_mtu().into());
+1054                        if let Some(challenge) =
+1055                            self.send_prev_path_challenge(now, &mut transmit, path_id)
+1056                        {
+1057                            return Some(challenge);
+1058                        }
+1059
+1060                        continue;
+1061                    }
+1062                    None => {
+1063                        // Nothing more to send.
+1064                        trace!(
+1065                            ?space_id,
+1066                            %path_id,
+1067                            "no CIDs to send on path, no more paths"
+1068                        );
+1069                        break;
+1070                    }
+1071                }
+1072            };
+1073
+1074            // Determine if anything can be sent in this packet number space (SpaceId +
+1075            // PathId).
+1076            let max_packet_size = if transmit.datagram_remaining_mut() > 0 {
+1077                // We are trying to coalesce another packet into this datagram.
+1078                transmit.datagram_remaining_mut()
+1079            } else {
+1080                // A new datagram needs to be started.
+1081                transmit.segment_size()
+1082            };
+1083            let can_send = self.space_can_send(space_id, path_id, max_packet_size, close);
+1084            let path_should_send = {
+1085                let path_exclusive_only = space_id == SpaceId::Data
+1086                    && have_available_path
+1087                    && self.path_data(path_id).local_status() == PathStatus::Backup;
+1088                let path_should_send = if path_exclusive_only {
+1089                    can_send.path_exclusive
+1090                } else {
+1091                    !can_send.is_empty()
+1092                };
+1093                let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1094                path_should_send || needs_loss_probe || can_send.close
+1095            };
+1096
+1097            if !path_should_send && space_id < SpaceId::Data {
+1098                if self.spaces[space_id].crypto.is_some() {
+1099                    trace!(?space_id, %path_id, "nothing to send in space");
+1100                }
+1101                space_id = space_id.next();
+1102                continue;
+1103            }
+1104
+1105            let send_blocked = if path_should_send && transmit.datagram_remaining_mut() == 0 {
+1106                // Only check congestion control if a new datagram is needed.
+1107                self.path_congestion_check(space_id, path_id, &transmit, &can_send, now)
+1108            } else {
+1109                PathBlocked::No
+1110            };
+1111            if send_blocked != PathBlocked::No {
+1112                trace!(?space_id, %path_id, ?send_blocked, "congestion blocked");
+1113                congestion_blocked = true;
+1114            }
+1115            if send_blocked != PathBlocked::No && space_id < SpaceId::Data {
+1116                // Higher spaces might still have tail-loss probes to send, which are not
+1117                // congestion blocked.
+1118                space_id = space_id.next();
+1119                continue;
+1120            }
+1121            if !path_should_send || send_blocked != PathBlocked::No {
+1122                // Nothing more to send on this path, check the next path if possible.
+1123
+1124                // If there are any datagrams in the transmit, packets for another path can
+1125                // not be built.
+1126                if transmit.num_datagrams() > 0 {
+1127                    break;
+1128                }
+1129
+1130                match self.paths.keys().find(|&&next| next > path_id) {
+1131                    Some(next_path_id) => {
+1132                        // See if this next path can send anything.
+1133                        trace!(
+1134                            ?space_id,
+1135                            %path_id,
+1136                            %next_path_id,
+1137                            "nothing to send on path"
+1138                        );
+1139                        path_id = *next_path_id;
+1140                        space_id = SpaceId::Data;
+1141
+1142                        // update per path state
+1143                        transmit.set_segment_size(self.path_data(path_id).current_mtu().into());
+1144                        if let Some(challenge) =
+1145                            self.send_prev_path_challenge(now, &mut transmit, path_id)
+1146                        {
+1147                            return Some(challenge);
+1148                        }
+1149
+1150                        continue;
+1151                    }
+1152                    None => {
+1153                        // Nothing more to send.
+1154                        trace!(
+1155                            ?space_id,
+1156                            %path_id,
+1157                            next_path_id=?None::<PathId>,
+1158                            "nothing to send on path"
+1159                        );
+1160                        break;
+1161                    }
+1162                }
+1163            }
+1164
+1165            // If the datagram is full, we need to start a new one.
+1166            if transmit.datagram_remaining_mut() == 0 {
+1167                if transmit.num_datagrams() >= transmit.max_datagrams() {
+1168                    // No more datagrams allowed
+1169                    break;
+1170                }
+1171
+1172                match self.spaces[space_id].for_path(path_id).loss_probes {
+1173                    0 => transmit.start_new_datagram(),
+1174                    _ => {
+1175                        // We need something to send for a tail-loss probe.
+1176                        let request_immediate_ack =
+1177                            space_id == SpaceId::Data && self.peer_supports_ack_frequency();
+1178                        self.spaces[space_id].maybe_queue_probe(
+1179                            path_id,
+1180                            request_immediate_ack,
+1181                            &self.streams,
+1182                        );
+1183
+1184                        self.spaces[space_id].for_path(path_id).loss_probes -= 1;
+1185
+1186                        // Clamp the datagram to at most the minimum MTU to ensure that loss
+1187                        // probes can get through and enable recovery even if the path MTU
+1188                        // has shrank unexpectedly.
+1189                        transmit.start_new_datagram_with_size(std::cmp::min(
+1190                            usize::from(INITIAL_MTU),
+1191                            transmit.segment_size(),
+1192                        ));
+1193                    }
+1194                }
+1195                trace!(count = transmit.num_datagrams(), "new datagram started");
+1196                coalesce = true;
+1197                pad_datagram = PadDatagram::No;
+1198            }
+1199
+1200            // If coalescing another packet into the existing datagram, there should
+1201            // still be enough space for a whole packet.
+1202            if transmit.datagram_start_offset() < transmit.len() {
+1203                debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE);
+1204            }
+1205
+1206            //
+1207            // From here on, we've determined that a packet will definitely be sent.
+1208            //
+1209
+1210            if self.spaces[SpaceId::Initial].crypto.is_some()
+1211                && space_id == SpaceId::Handshake
+1212                && self.side.is_client()
+1213            {
+1214                // A client stops both sending and processing Initial packets when it
+1215                // sends its first Handshake packet.
+1216                self.discard_space(now, SpaceId::Initial);
+1217            }
+1218            if let Some(ref mut prev) = self.prev_crypto {
+1219                prev.update_unacked = false;
+1220            }
+1221
+1222            let mut qlog = QlogSentPacket::default();
+1223            let mut builder = PacketBuilder::new(
+1224                now,
+1225                space_id,
+1226                path_id,
+1227                remote_cid,
+1228                &mut transmit,
+1229                can_send.other,
+1230                self,
+1231                &mut qlog,
+1232            )?;
+1233            last_packet_number = Some(builder.exact_number);
+1234            coalesce = coalesce && !builder.short_header;
+1235
+1236            if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) {
+1237                // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1
+1238                pad_datagram |= PadDatagram::ToMinMtu;
+1239            }
+1240            if space_id == SpaceId::Data && self.config.pad_to_mtu {
+1241                pad_datagram |= PadDatagram::ToSegmentSize;
+1242            }
+1243
+1244            if can_send.close {
+1245                trace!("sending CONNECTION_CLOSE");
+1246                // Encode ACKs before the ConnectionClose message, to give the receiver
+1247                // a better approximate on what data has been processed. This is
+1248                // especially important with ack delay, since the peer might not
+1249                // have gotten any other ACK for the data earlier on.
+1250                let mut sent_frames = SentFrames::default();
+1251                let is_multipath_negotiated = self.is_multipath_negotiated();
+1252                for path_id in self.spaces[space_id]
+1253                    .number_spaces
+1254                    .iter()
+1255                    .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty())
+1256                    .map(|(&path_id, _)| path_id)
+1257                    .collect::<Vec<_>>()
+1258                {
+1259                    Self::populate_acks(
+1260                        now,
+1261                        self.receiving_ecn,
+1262                        &mut sent_frames,
+1263                        path_id,
+1264                        space_id,
+1265                        &mut self.spaces[space_id],
+1266                        is_multipath_negotiated,
+1267                        &mut builder.frame_space_mut(),
+1268                        &mut self.stats,
+1269                        &mut qlog,
+1270                    );
+1271                }
+1272
+1273                // Since there only 64 ACK frames there will always be enough space
+1274                // to encode the ConnectionClose frame too. However we still have the
+1275                // check here to prevent crashes if something changes.
+1276                debug_assert!(
+1277                    builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND,
+1278                    "ACKs should leave space for ConnectionClose"
+1279                );
+1280                if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() {
+1281                    let max_frame_size = builder.frame_space_remaining();
+1282                    match self.state.as_type() {
+1283                        StateType::Closed => {
+1284                            let reason: Close =
+1285                                self.state.as_closed().expect("checked").clone().into();
+1286                            if space_id == SpaceId::Data || reason.is_transport_layer() {
+1287                                reason.encode(&mut builder.frame_space_mut(), max_frame_size);
+1288                                qlog.frame(&Frame::Close(reason));
+1289                            } else {
+1290                                let frame = frame::ConnectionClose {
+1291                                    error_code: TransportErrorCode::APPLICATION_ERROR,
+1292                                    frame_type: None,
+1293                                    reason: Bytes::new(),
+1294                                };
+1295                                frame.encode(&mut builder.frame_space_mut(), max_frame_size);
+1296                                qlog.frame(&Frame::Close(frame::Close::Connection(frame)));
+1297                            }
+1298                        }
+1299                        StateType::Draining => {
+1300                            let frame = frame::ConnectionClose {
+1301                                error_code: TransportErrorCode::NO_ERROR,
+1302                                frame_type: None,
+1303                                reason: Bytes::new(),
+1304                            };
+1305                            frame.encode(&mut builder.frame_space_mut(), max_frame_size);
+1306                            qlog.frame(&Frame::Close(frame::Close::Connection(frame)));
+1307                        }
+1308                        _ => unreachable!(
+1309                            "tried to make a close packet when the connection wasn't closed"
+1310                        ),
+1311                    };
+1312                }
+1313                builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog);
+1314                if space_id == self.highest_space {
+1315                    // Don't send another close packet. Even with multipath we only send
+1316                    // CONNECTION_CLOSE on a single path since we expect our paths to work.
+1317                    self.close = false;
+1318                    // `CONNECTION_CLOSE` is the final packet
+1319                    break;
+1320                } else {
+1321                    // Send a close frame in every possible space for robustness, per
+1322                    // RFC9000 "Immediate Close during the Handshake". Don't bother trying
+1323                    // to send anything else.
+1324                    space_id = space_id.next();
+1325                    continue;
+1326                }
+1327            }
+1328
+1329            // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that
+1330            // path validation can occur while the link is saturated.
+1331            if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 {
+1332                let path = self.path_data_mut(path_id);
+1333                if let Some((token, remote)) = path.path_responses.pop_off_path(path.remote) {
+1334                    // TODO(flub): We need to use the right CID!  We shouldn't use the same
+1335                    //    CID as the current active one for the path.  Though see also
+1336                    //    https://github.com/quinn-rs/quinn/issues/2184
+1337                    let response = frame::PathResponse(token);
+1338                    trace!(%response, "(off-path)");
+1339                    builder.frame_space_mut().write(response);
+1340                    qlog.frame(&Frame::PathResponse(response));
+1341                    self.stats.frame_tx.path_response += 1;
+1342                    builder.finish_and_track(
+1343                        now,
+1344                        self,
+1345                        path_id,
+1346                        SentFrames {
+1347                            non_retransmits: true,
+1348                            ..SentFrames::default()
+1349                        },
+1350                        PadDatagram::ToMinMtu,
+1351                        qlog,
+1352                    );
+1353                    self.stats.udp_tx.on_sent(1, transmit.len());
+1354                    return Some(Transmit {
+1355                        destination: remote,
+1356                        size: transmit.len(),
+1357                        ecn: None,
+1358                        segment_size: None,
+1359                        src_ip: self.local_ip,
+1360                    });
+1361                }
+1362            }
+1363
+1364            let sent_frames = {
+1365                let path_exclusive_only = have_available_path
+1366                    && self.path_data(path_id).local_status() == PathStatus::Backup;
+1367                let pn = builder.exact_number;
+1368                self.populate_packet(
+1369                    now,
+1370                    space_id,
+1371                    path_id,
+1372                    path_exclusive_only,
+1373                    &mut builder.frame_space_mut(),
+1374                    pn,
+1375                    &mut qlog,
+1376                )
+1377            };
+1378
+1379            // ACK-only packets should only be sent when explicitly allowed. If we write them due to
+1380            // any other reason, there is a bug which leads to one component announcing write
+1381            // readiness while not writing any data. This degrades performance. The condition is
+1382            // only checked if the full MTU is available and when potentially large fixed-size
+1383            // frames aren't queued, so that lack of space in the datagram isn't the reason for just
+1384            // writing ACKs.
+1385            debug_assert!(
+1386                !(sent_frames.is_ack_only(&self.streams)
+1387                    && !can_send.acks
+1388                    && can_send.other
+1389                    && builder.buf.segment_size()
+1390                        == self.path_data(path_id).current_mtu() as usize
+1391                    && self.datagrams.outgoing.is_empty()),
+1392                "SendableFrames was {can_send:?}, but only ACKs have been written"
+1393            );
+1394            if sent_frames.requires_padding {
+1395                pad_datagram |= PadDatagram::ToMinMtu;
+1396            }
+1397
+1398            for (path_id, _pn) in sent_frames.largest_acked.iter() {
+1399                self.spaces[space_id]
+1400                    .for_path(*path_id)
+1401                    .pending_acks
+1402                    .acks_sent();
+1403                self.timers.stop(
+1404                    Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+1405                    self.qlog.with_time(now),
+1406                );
+1407            }
+1408
+1409            // Now we need to finish the packet.  Before we do so we need to know if we will
+1410            // be coalescing the next packet into this one, or will be ending the datagram
+1411            // as well.  Because if this is the last packet in the datagram more padding
+1412            // might be needed because of the packet type, or to fill the GSO segment size.
+1413
+1414            // Are we allowed to coalesce AND is there enough space for another *packet* in
+1415            // this datagram AND is there another packet to send in this or the next space?
+1416            if coalesce
+1417                && builder
+1418                    .buf
+1419                    .datagram_remaining_mut()
+1420                    .saturating_sub(builder.predict_packet_end())
+1421                    > MIN_PACKET_SPACE
+1422                && self
+1423                    .next_send_space(space_id, path_id, builder.buf, close)
+1424                    .is_some()
+1425            {
+1426                // We can append/coalesce the next packet into the current
+1427                // datagram. Finish the current packet without adding extra padding.
+1428                builder.finish_and_track(now, self, path_id, sent_frames, PadDatagram::No, qlog);
+1429            } else {
+1430                // We need a new datagram for the next packet.  Finish the current
+1431                // packet with padding.
+1432                if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) {
+1433                    // If too many padding bytes would be required to continue the
+1434                    // GSO batch after this packet, end the GSO batch here. Ensures
+1435                    // that fixed-size frames with heterogeneous sizes
+1436                    // (e.g. application datagrams) won't inadvertently waste large
+1437                    // amounts of bandwidth. The exact threshold is a bit arbitrary
+1438                    // and might benefit from further tuning, though there's no
+1439                    // universally optimal value.
+1440                    const MAX_PADDING: usize = 32;
+1441                    if builder.buf.datagram_remaining_mut()
+1442                        > builder.predict_packet_end() + MAX_PADDING
+1443                    {
+1444                        trace!(
+1445                            "GSO truncated by demand for {} padding bytes",
+1446                            builder.buf.datagram_remaining_mut() - builder.predict_packet_end()
+1447                        );
+1448                        builder.finish_and_track(
+1449                            now,
+1450                            self,
+1451                            path_id,
+1452                            sent_frames,
+1453                            PadDatagram::No,
+1454                            qlog,
+1455                        );
+1456                        break;
+1457                    }
+1458
+1459                    // Pad the current datagram to GSO segment size so it can be
+1460                    // included in the GSO batch.
+1461                    builder.finish_and_track(
+1462                        now,
+1463                        self,
+1464                        path_id,
+1465                        sent_frames,
+1466                        PadDatagram::ToSegmentSize,
+1467                        qlog,
+1468                    );
+1469                } else {
+1470                    builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog);
+1471                }
+1472                if transmit.num_datagrams() == 1 {
+1473                    transmit.clip_datagram_size();
+1474                }
+1475            }
+1476        }
+1477
+1478        if let Some(last_packet_number) = last_packet_number {
+1479            // Note that when sending in multiple packet spaces the last packet number will
+1480            // be the one from the highest packet space.
+1481            self.path_data_mut(path_id).congestion.on_sent(
+1482                now,
+1483                transmit.len() as u64,
+1484                last_packet_number,
+1485            );
+1486        }
+1487
+1488        self.qlog.emit_recovery_metrics(
+1489            path_id,
+1490            &mut self.paths.get_mut(&path_id).unwrap().data,
+1491            now,
+1492        );
+1493
+1494        self.app_limited = transmit.is_empty() && !congestion_blocked;
+1495
+1496        // Send MTU probe if necessary
+1497        if transmit.is_empty() && self.state.is_established() {
+1498            // MTU probing happens only in Data space.
+1499            let space_id = SpaceId::Data;
+1500            path_id = *self.paths.first_key_value().expect("one path must exist").0;
+1501            let probe_data = loop {
+1502                // We MTU probe all paths for which all of the following is true:
+1503                // - We have an active destination CID for the path.
+1504                // - The remote address *and* path are validated.
+1505                // - The path is not abandoned.
+1506                // - The MTU Discovery subsystem wants to probe the path.
+1507                let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active);
+1508                let eligible = self.path_data(path_id).validated
+1509                    && !self.path_data(path_id).is_validating_path()
+1510                    && !self.abandoned_paths.contains(&path_id);
+1511                let probe_size = eligible
+1512                    .then(|| {
+1513                        let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number();
+1514                        self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn)
+1515                    })
+1516                    .flatten();
+1517                match (active_cid, probe_size) {
+1518                    (Some(active_cid), Some(probe_size)) => {
+1519                        // Let's send an MTUD probe!
+1520                        break Some((active_cid, probe_size));
+1521                    }
+1522                    _ => {
+1523                        // Find the next path to check if it needs an MTUD probe.
+1524                        match self.paths.keys().find(|&&next| next > path_id) {
+1525                            Some(next) => {
+1526                                path_id = *next;
+1527                                continue;
+1528                            }
+1529                            None => break None,
+1530                        }
+1531                    }
+1532                }
+1533            };
+1534            if let Some((active_cid, probe_size)) = probe_data {
+1535                // We are definitely sending a DPLPMTUD probe.
+1536                debug_assert_eq!(transmit.num_datagrams(), 0);
+1537                transmit.start_new_datagram_with_size(probe_size as usize);
+1538
+1539                let mut qlog = QlogSentPacket::default();
+1540                let mut builder = PacketBuilder::new(
+1541                    now,
+1542                    space_id,
+1543                    path_id,
+1544                    active_cid,
+1545                    &mut transmit,
+1546                    true,
+1547                    self,
+1548                    &mut qlog,
+1549                )?;
+1550
+1551                // We implement MTU probes as ping packets padded up to the probe size
+1552                trace!(?probe_size, "writing MTUD probe");
+1553                trace!("PING");
+1554                builder.frame_space_mut().write(frame::FrameType::PING);
+1555                qlog.frame(&Frame::Ping);
+1556                self.stats.frame_tx.ping += 1;
+1557
+1558                // If supported by the peer, we want no delays to the probe's ACK
+1559                if self.peer_supports_ack_frequency() {
+1560                    trace!("IMMEDIATE_ACK");
+1561                    builder
+1562                        .frame_space_mut()
+1563                        .write(frame::FrameType::IMMEDIATE_ACK);
+1564                    self.stats.frame_tx.immediate_ack += 1;
+1565                    qlog.frame(&Frame::ImmediateAck);
+1566                }
+1567
+1568                let sent_frames = SentFrames {
+1569                    non_retransmits: true,
+1570                    ..Default::default()
+1571                };
+1572                builder.finish_and_track(
+1573                    now,
+1574                    self,
+1575                    path_id,
+1576                    sent_frames,
+1577                    PadDatagram::ToSize(probe_size),
+1578                    qlog,
+1579                );
+1580
+1581                self.path_stats
+1582                    .entry(path_id)
+1583                    .or_default()
+1584                    .sent_plpmtud_probes += 1;
+1585            }
+1586        }
+1587
+1588        if transmit.is_empty() {
+1589            return None;
+1590        }
+1591
+1592        let destination = self.path_data(path_id).remote;
+1593        trace!(
+1594            segment_size = transmit.segment_size(),
+1595            last_datagram_len = transmit.len() % transmit.segment_size(),
+1596            ?destination,
+1597            "sending {} bytes in {} datagrams",
+1598            transmit.len(),
+1599            transmit.num_datagrams()
+1600        );
+1601        self.path_data_mut(path_id)
+1602            .inc_total_sent(transmit.len() as u64);
+1603
+1604        self.stats
+1605            .udp_tx
+1606            .on_sent(transmit.num_datagrams() as u64, transmit.len());
+1607
+1608        Some(Transmit {
+1609            destination,
+1610            size: transmit.len(),
+1611            ecn: if self.path_data(path_id).sending_ecn {
+1612                Some(EcnCodepoint::Ect0)
+1613            } else {
+1614                None
+1615            },
+1616            segment_size: match transmit.num_datagrams() {
+1617                1 => None,
+1618                _ => Some(transmit.segment_size()),
+1619            },
+1620            src_ip: self.local_ip,
+1621        })
+1622    }
+1623
+1624    /// Returns the [`SpaceId`] of the next packet space which has data to send
+1625    ///
+1626    /// This takes into account the space available to frames in the next datagram.
+1627    // TODO(flub): This duplication is not nice.
+1628    fn next_send_space(
+1629        &mut self,
+1630        current_space_id: SpaceId,
+1631        path_id: PathId,
+1632        buf: &TransmitBuf<'_>,
+1633        close: bool,
+1634    ) -> Option<SpaceId> {
+1635        // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed
+1636        // to be able to send an individual frame at least this large in the next 1-RTT
+1637        // packet. This could be generalized to support every space, but it's only needed to
+1638        // handle large fixed-size frames, which only exist in 1-RTT (application
+1639        // datagrams). We don't account for coalesced packets potentially occupying space
+1640        // because frames can always spill into the next datagram.
+1641        let mut space_id = current_space_id;
+1642        loop {
+1643            let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close);
+1644            if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) {
+1645                return Some(space_id);
+1646            }
+1647            space_id = match space_id {
+1648                SpaceId::Initial => SpaceId::Handshake,
+1649                SpaceId::Handshake => SpaceId::Data,
+1650                SpaceId::Data => break,
+1651            }
+1652        }
+1653        None
+1654    }
+1655
+1656    /// Checks if creating a new datagram would be blocked by congestion control
+1657    fn path_congestion_check(
+1658        &mut self,
+1659        space_id: SpaceId,
+1660        path_id: PathId,
+1661        transmit: &TransmitBuf<'_>,
+1662        can_send: &SendableFrames,
+1663        now: Instant,
+1664    ) -> PathBlocked {
+1665        // Anti-amplification is only based on `total_sent`, which gets updated after
+1666        // the transmit is sent. Therefore we pass the amount of bytes for datagrams
+1667        // that are already created, as well as 1 byte for starting another datagram. If
+1668        // there is any anti-amplification budget left, we always allow a full MTU to be
+1669        // sent (see https://github.com/quinn-rs/quinn/issues/1082).
+1670        if self.side().is_server()
+1671            && self
+1672                .path_data(path_id)
+1673                .anti_amplification_blocked(transmit.len() as u64 + 1)
+1674        {
+1675            trace!(?space_id, %path_id, "blocked by anti-amplification");
+1676            return PathBlocked::AntiAmplification;
+1677        }
+1678
+1679        // Congestion control check.
+1680        // Tail loss probes must not be blocked by congestion, or a deadlock could arise.
+1681        let bytes_to_send = transmit.segment_size() as u64;
+1682        let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0;
+1683
+1684        if can_send.other && !need_loss_probe && !can_send.close {
+1685            let path = self.path_data(path_id);
+1686            if path.in_flight.bytes + bytes_to_send >= path.congestion.window() {
+1687                trace!(?space_id, %path_id, "blocked by congestion control");
+1688                return PathBlocked::Congestion;
+1689            }
+1690        }
+1691
+1692        // Pacing check.
+1693        if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) {
+1694            self.timers.set(
+1695                Timer::PerPath(path_id, PathTimer::Pacing),
+1696                delay,
+1697                self.qlog.with_time(now),
+1698            );
+1699            // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though
+1700            // they are not congestion controlled.
+1701            trace!(?space_id, %path_id, "blocked by pacing");
+1702            return PathBlocked::Pacing;
+1703        }
+1704
+1705        PathBlocked::No
+1706    }
+1707
+1708    /// Send PATH_CHALLENGE for a previous path if necessary
+1709    ///
+1710    /// QUIC-TRANSPORT section 9.3.3
+1711    /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding>
+1712    fn send_prev_path_challenge(
+1713        &mut self,
+1714        now: Instant,
+1715        buf: &mut TransmitBuf<'_>,
+1716        path_id: PathId,
+1717    ) -> Option<Transmit> {
+1718        let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?;
+1719        // TODO (matheus23): We could use !prev_path.is_validating() here instead to
+1720        // (possibly) also re-send challenges when they get lost.
+1721        if !prev_path.send_new_challenge {
+1722            return None;
+1723        };
+1724        prev_path.send_new_challenge = false;
+1725        let destination = prev_path.remote;
+1726        let token = self.rng.random();
+1727        let info = paths::SentChallengeInfo {
+1728            sent_instant: now,
+1729            remote: destination,
+1730        };
+1731        prev_path.challenges_sent.insert(token, info);
+1732        debug_assert_eq!(
+1733            self.highest_space,
+1734            SpaceId::Data,
+1735            "PATH_CHALLENGE queued without 1-RTT keys"
+1736        );
+1737        buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize);
+1738
+1739        // Use the previous CID to avoid linking the new path with the previous path. We
+1740        // don't bother accounting for possible retirement of that prev_cid because this is
+1741        // sent once, immediately after migration, when the CID is known to be valid. Even
+1742        // if a post-migration packet caused the CID to be retired, it's fair to pretend
+1743        // this is sent first.
+1744        debug_assert_eq!(buf.datagram_start_offset(), 0);
+1745        let mut qlog = QlogSentPacket::default();
+1746        let mut builder = PacketBuilder::new(
+1747            now,
+1748            SpaceId::Data,
+1749            path_id,
+1750            *prev_cid,
+1751            buf,
+1752            false,
+1753            self,
+1754            &mut qlog,
+1755        )?;
+1756        let challenge = frame::PathChallenge(token);
+1757        trace!(%challenge, "validating previous path");
+1758        qlog.frame(&Frame::PathChallenge(challenge));
+1759        builder.frame_space_mut().write(challenge);
+1760        self.stats.frame_tx.path_challenge += 1;
+1761
+1762        // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame
+1763        // to at least the smallest allowed maximum datagram size of 1200 bytes,
+1764        // unless the anti-amplification limit for the path does not permit
+1765        // sending a datagram of this size
+1766        builder.pad_to(MIN_INITIAL_SIZE);
+1767
+1768        builder.finish(self, now, qlog);
+1769        self.stats.udp_tx.on_sent(1, buf.len());
+1770
+1771        Some(Transmit {
+1772            destination,
+1773            size: buf.len(),
+1774            ecn: None,
+1775            segment_size: None,
+1776            src_ip: self.local_ip,
+1777        })
+1778    }
+1779
+1780    /// Indicate what types of frames are ready to send for the given space
+1781    ///
+1782    /// *packet_size* is the number of bytes available to build the next packet.  *close*
+1783    /// *indicates whether a CONNECTION_CLOSE frame needs to be sent.
+1784    fn space_can_send(
+1785        &mut self,
+1786        space_id: SpaceId,
+1787        path_id: PathId,
+1788        packet_size: usize,
+1789        close: bool,
+1790    ) -> SendableFrames {
+1791        let pn = self.spaces[SpaceId::Data]
+1792            .for_path(path_id)
+1793            .peek_tx_number();
+1794        let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id));
+1795        if self.spaces[space_id].crypto.is_none()
+1796            && (space_id != SpaceId::Data
+1797                || self.zero_rtt_crypto.is_none()
+1798                || self.side.is_server())
+1799        {
+1800            // No keys available for this space
+1801            return SendableFrames::empty();
+1802        }
+1803        let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams);
+1804        if space_id == SpaceId::Data {
+1805            can_send |= self.can_send_1rtt(path_id, frame_space_1rtt);
+1806        }
+1807
+1808        can_send.close = close && self.spaces[space_id].crypto.is_some();
+1809
+1810        can_send
+1811    }
+1812
+1813    /// Process `ConnectionEvent`s generated by the associated `Endpoint`
+1814    ///
+1815    /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals
+1816    /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be
+1817    /// extracted through the relevant methods.
+1818    pub fn handle_event(&mut self, event: ConnectionEvent) {
+1819        use ConnectionEventInner::*;
+1820        match event.0 {
+1821            Datagram(DatagramConnectionEvent {
+1822                now,
+1823                remote,
+1824                path_id,
+1825                ecn,
+1826                first_decode,
+1827                remaining,
+1828            }) => {
+1829                let span = trace_span!("pkt", %path_id);
+1830                let _guard = span.enter();
+1831                // If this packet could initiate a migration and we're a client or a server that
+1832                // forbids migration, drop the datagram. This could be relaxed to heuristically
+1833                // permit NAT-rebinding-like migration.
+1834                if let Some(known_remote) = self.path(path_id).map(|path| path.remote) {
+1835                    if remote != known_remote && !self.side.remote_may_migrate(&self.state) {
+1836                        trace!(
+1837                            %path_id,
+1838                            ?remote,
+1839                            path_remote = ?self.path(path_id).map(|p| p.remote),
+1840                            "discarding packet from unrecognized peer"
+1841                        );
+1842                        return;
+1843                    }
+1844                }
+1845
+1846                let was_anti_amplification_blocked = self
+1847                    .path(path_id)
+1848                    .map(|path| path.anti_amplification_blocked(1))
+1849                    .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated
+1850                // TODO(@divma): revisit this
+1851
+1852                self.stats.udp_rx.datagrams += 1;
+1853                self.stats.udp_rx.bytes += first_decode.len() as u64;
+1854                let data_len = first_decode.len();
+1855
+1856                self.handle_decode(now, remote, path_id, ecn, first_decode);
+1857                // The current `path` might have changed inside `handle_decode` since the packet
+1858                // could have triggered a migration. The packet might also belong to an unknown
+1859                // path and have been rejected. Make sure the data received is accounted for the
+1860                // most recent path by accessing `path` after `handle_decode`.
+1861                if let Some(path) = self.path_mut(path_id) {
+1862                    path.inc_total_recvd(data_len as u64);
+1863                }
+1864
+1865                if let Some(data) = remaining {
+1866                    self.stats.udp_rx.bytes += data.len() as u64;
+1867                    self.handle_coalesced(now, remote, path_id, ecn, data);
+1868                }
+1869
+1870                if let Some(path) = self.paths.get_mut(&path_id) {
+1871                    self.qlog
+1872                        .emit_recovery_metrics(path_id, &mut path.data, now);
+1873                }
+1874
+1875                if was_anti_amplification_blocked {
+1876                    // A prior attempt to set the loss detection timer may have failed due to
+1877                    // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if
+1878                    // the server's first flight is lost.
+1879                    self.set_loss_detection_timer(now, path_id);
+1880                }
+1881            }
+1882            NewIdentifiers(ids, now, cid_len, cid_lifetime) => {
+1883                let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default();
+1884                debug_assert!(ids.iter().all(|issued| issued.path_id == path_id));
+1885                let cid_state = self
+1886                    .local_cid_state
+1887                    .entry(path_id)
+1888                    .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0));
+1889                cid_state.new_cids(&ids, now);
+1890
+1891                ids.into_iter().rev().for_each(|frame| {
+1892                    self.spaces[SpaceId::Data].pending.new_cids.push(frame);
+1893                });
+1894                // Always update Timer::PushNewCid
+1895                self.reset_cid_retirement(now);
+1896            }
+1897        }
+1898    }
+1899
+1900    /// Process timer expirations
+1901    ///
+1902    /// Executes protocol logic, potentially preparing signals (including application `Event`s,
+1903    /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant
+1904    /// methods.
+1905    ///
+1906    /// It is most efficient to call this immediately after the system clock reaches the latest
+1907    /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply
+1908    /// no-op and therefore are safe.
+1909    pub fn handle_timeout(&mut self, now: Instant) {
+1910        while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) {
+1911            // TODO(@divma): remove `at` when the unicorn is born
+1912            trace!(?timer, at=?now, "timeout");
+1913            match timer {
+1914                Timer::Conn(timer) => match timer {
+1915                    ConnTimer::Close => {
+1916                        self.state.move_to_drained(None);
+1917                        self.endpoint_events.push_back(EndpointEventInner::Drained);
+1918                    }
+1919                    ConnTimer::Idle => {
+1920                        self.kill(ConnectionError::TimedOut);
+1921                    }
+1922                    ConnTimer::KeepAlive => {
+1923                        trace!("sending keep-alive");
+1924                        self.ping();
+1925                    }
+1926                    ConnTimer::KeyDiscard => {
+1927                        self.zero_rtt_crypto = None;
+1928                        self.prev_crypto = None;
+1929                    }
+1930                    ConnTimer::PushNewCid => {
+1931                        while let Some((path_id, when)) = self.next_cid_retirement() {
+1932                            if when > now {
+1933                                break;
+1934                            }
+1935                            match self.local_cid_state.get_mut(&path_id) {
+1936                                None => error!(%path_id, "No local CID state for path"),
+1937                                Some(cid_state) => {
+1938                                    // Update `retire_prior_to` field in NEW_CONNECTION_ID frame
+1939                                    let num_new_cid = cid_state.on_cid_timeout().into();
+1940                                    if !self.state.is_closed() {
+1941                                        trace!(
+1942                                            "push a new CID to peer RETIRE_PRIOR_TO field {}",
+1943                                            cid_state.retire_prior_to()
+1944                                        );
+1945                                        self.endpoint_events.push_back(
+1946                                            EndpointEventInner::NeedIdentifiers(
+1947                                                path_id,
+1948                                                now,
+1949                                                num_new_cid,
+1950                                            ),
+1951                                        );
+1952                                    }
+1953                                }
+1954                            }
+1955                        }
+1956                    }
+1957                },
+1958                // TODO: add path_id as span somehow
+1959                Timer::PerPath(path_id, timer) => {
+1960                    let span = trace_span!("per-path timer fired", %path_id, ?timer);
+1961                    let _guard = span.enter();
+1962                    match timer {
+1963                        PathTimer::PathIdle => {
+1964                            self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into())
+1965                                .ok();
+1966                        }
+1967
+1968                        PathTimer::PathKeepAlive => {
+1969                            trace!("sending keep-alive on path");
+1970                            self.ping_path(path_id).ok();
+1971                        }
+1972                        PathTimer::LossDetection => {
+1973                            self.on_loss_detection_timeout(now, path_id);
+1974                            self.qlog.emit_recovery_metrics(
+1975                                path_id,
+1976                                &mut self.paths.get_mut(&path_id).unwrap().data,
+1977                                now,
+1978                            );
+1979                        }
+1980                        PathTimer::PathValidation => {
+1981                            let Some(path) = self.paths.get_mut(&path_id) else {
+1982                                continue;
+1983                            };
+1984                            self.timers.stop(
+1985                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+1986                                self.qlog.with_time(now),
+1987                            );
+1988                            debug!("path validation failed");
+1989                            if let Some((_, prev)) = path.prev.take() {
+1990                                path.data = prev;
+1991                            }
+1992                            path.data.challenges_sent.clear();
+1993                            path.data.send_new_challenge = false;
+1994                        }
+1995                        PathTimer::PathChallengeLost => {
+1996                            let Some(path) = self.paths.get_mut(&path_id) else {
+1997                                continue;
+1998                            };
+1999                            trace!("path challenge deemed lost");
+2000                            path.data.send_new_challenge = true;
+2001                        }
+2002                        PathTimer::PathOpen => {
+2003                            let Some(path) = self.path_mut(path_id) else {
+2004                                continue;
+2005                            };
+2006                            path.challenges_sent.clear();
+2007                            path.send_new_challenge = false;
+2008                            debug!("new path validation failed");
+2009                            if let Err(err) = self.close_path(
+2010                                now,
+2011                                path_id,
+2012                                TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(),
+2013                            ) {
+2014                                warn!(?err, "failed closing path");
+2015                            }
+2016
+2017                            self.events.push_back(Event::Path(PathEvent::LocallyClosed {
+2018                                id: path_id,
+2019                                error: PathError::ValidationFailed,
+2020                            }));
+2021                        }
+2022                        PathTimer::Pacing => trace!("pacing timer expired"),
+2023                        PathTimer::MaxAckDelay => {
+2024                            trace!("max ack delay reached");
+2025                            // This timer is only armed in the Data space
+2026                            self.spaces[SpaceId::Data]
+2027                                .for_path(path_id)
+2028                                .pending_acks
+2029                                .on_max_ack_delay_timeout()
+2030                        }
+2031                        PathTimer::DiscardPath => {
+2032                            // The path was abandoned and 3*PTO has expired since.  Clean up all
+2033                            // remaining state and install stateless reset token.
+2034                            self.timers.stop_per_path(path_id, self.qlog.with_time(now));
+2035                            if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) {
+2036                                let (min_seq, max_seq) = loc_cid_state.active_seq();
+2037                                for seq in min_seq..=max_seq {
+2038                                    self.endpoint_events.push_back(
+2039                                        EndpointEventInner::RetireConnectionId(
+2040                                            now, path_id, seq, false,
+2041                                        ),
+2042                                    );
+2043                                }
+2044                            }
+2045                            self.discard_path(path_id, now);
+2046                        }
+2047                    }
+2048                }
+2049            }
+2050        }
+2051    }
+2052
+2053    /// Close a connection immediately
+2054    ///
+2055    /// This does not ensure delivery of outstanding data. It is the application's responsibility to
+2056    /// call this only when all important communications have been completed, e.g. by calling
+2057    /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding
+2058    /// [`StreamEvent::Finished`] event.
+2059    ///
+2060    /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been
+2061    /// delivered. There may still be data from the peer that has not been received.
+2062    ///
+2063    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+2064    pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) {
+2065        self.close_inner(
+2066            now,
+2067            Close::Application(frame::ApplicationClose { error_code, reason }),
+2068        )
+2069    }
+2070
+2071    fn close_inner(&mut self, now: Instant, reason: Close) {
+2072        let was_closed = self.state.is_closed();
+2073        if !was_closed {
+2074            self.close_common();
+2075            self.set_close_timer(now);
+2076            self.close = true;
+2077            self.state.move_to_closed_local(reason);
+2078        }
+2079    }
+2080
+2081    /// Control datagrams
+2082    pub fn datagrams(&mut self) -> Datagrams<'_> {
+2083        Datagrams { conn: self }
+2084    }
+2085
+2086    /// Returns connection statistics
+2087    pub fn stats(&mut self) -> ConnectionStats {
+2088        self.stats.clone()
+2089    }
+2090
+2091    /// Returns path statistics
+2092    pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> {
+2093        let path = self.paths.get(&path_id)?;
+2094        let stats = self.path_stats.entry(path_id).or_default();
+2095        stats.rtt = path.data.rtt.get();
+2096        stats.cwnd = path.data.congestion.window();
+2097        stats.current_mtu = path.data.mtud.current_mtu();
+2098        Some(*stats)
+2099    }
+2100
+2101    /// Ping the remote endpoint
+2102    ///
+2103    /// Causes an ACK-eliciting packet to be transmitted on the connection.
+2104    pub fn ping(&mut self) {
+2105        // TODO(flub): This is very brute-force: it pings *all* the paths.  Instead it would
+2106        //    be nice if we could only send a single packet for this.
+2107        for path_data in self.spaces[self.highest_space].number_spaces.values_mut() {
+2108            path_data.ping_pending = true;
+2109        }
+2110    }
+2111
+2112    /// Ping the remote endpoint over a specific path
+2113    ///
+2114    /// Causes an ACK-eliciting packet to be transmitted on the path.
+2115    pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> {
+2116        let path_data = self.spaces[self.highest_space]
+2117            .number_spaces
+2118            .get_mut(&path)
+2119            .ok_or(ClosedPath { _private: () })?;
+2120        path_data.ping_pending = true;
+2121        Ok(())
+2122    }
+2123
+2124    /// Update traffic keys spontaneously
+2125    ///
+2126    /// This can be useful for testing key updates, as they otherwise only happen infrequently.
+2127    pub fn force_key_update(&mut self) {
+2128        if !self.state.is_established() {
+2129            debug!("ignoring forced key update in illegal state");
+2130            return;
+2131        }
+2132        if self.prev_crypto.is_some() {
+2133            // We already just updated, or are currently updating, the keys. Concurrent key updates
+2134            // are illegal.
+2135            debug!("ignoring redundant forced key update");
+2136            return;
+2137        }
+2138        self.update_keys(None, false);
+2139    }
+2140
+2141    // Compatibility wrapper for quinn < 0.11.7. Remove for 0.12.
+2142    #[doc(hidden)]
+2143    #[deprecated]
+2144    pub fn initiate_key_update(&mut self) {
+2145        self.force_key_update();
+2146    }
+2147
+2148    /// Get a session reference
+2149    pub fn crypto_session(&self) -> &dyn crypto::Session {
+2150        &*self.crypto
+2151    }
+2152
+2153    /// Whether the connection is in the process of being established
+2154    ///
+2155    /// If this returns `false`, the connection may be either established or closed, signaled by the
+2156    /// emission of a `Connected` or `ConnectionLost` message respectively.
+2157    pub fn is_handshaking(&self) -> bool {
+2158        self.state.is_handshake()
+2159    }
+2160
+2161    /// Whether the connection is closed
+2162    ///
+2163    /// Closed connections cannot transport any further data. A connection becomes closed when
+2164    /// either peer application intentionally closes it, or when either transport layer detects an
+2165    /// error such as a time-out or certificate validation failure.
+2166    ///
+2167    /// A `ConnectionLost` event is emitted with details when the connection becomes closed.
+2168    pub fn is_closed(&self) -> bool {
+2169        self.state.is_closed()
+2170    }
+2171
+2172    /// Whether there is no longer any need to keep the connection around
+2173    ///
+2174    /// Closed connections become drained after a brief timeout to absorb any remaining in-flight
+2175    /// packets from the peer. All drained connections have been closed.
+2176    pub fn is_drained(&self) -> bool {
+2177        self.state.is_drained()
+2178    }
+2179
+2180    /// For clients, if the peer accepted the 0-RTT data packets
+2181    ///
+2182    /// The value is meaningless until after the handshake completes.
+2183    pub fn accepted_0rtt(&self) -> bool {
+2184        self.accepted_0rtt
+2185    }
+2186
+2187    /// Whether 0-RTT is/was possible during the handshake
+2188    pub fn has_0rtt(&self) -> bool {
+2189        self.zero_rtt_enabled
+2190    }
+2191
+2192    /// Whether there are any pending retransmits
+2193    pub fn has_pending_retransmits(&self) -> bool {
+2194        !self.spaces[SpaceId::Data].pending.is_empty(&self.streams)
+2195    }
+2196
+2197    /// Look up whether we're the client or server of this Connection
+2198    pub fn side(&self) -> Side {
+2199        self.side.side()
+2200    }
+2201
+2202    /// Get the address observed by the remote over the given path
+2203    pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> {
+2204        self.path(path_id)
+2205            .map(|path_data| {
+2206                path_data
+2207                    .last_observed_addr_report
+2208                    .as_ref()
+2209                    .map(|observed| observed.socket_addr())
+2210            })
+2211            .ok_or(ClosedPath { _private: () })
+2212    }
+2213
+2214    /// The local IP address which was used when the peer established
+2215    /// the connection
+2216    ///
+2217    /// This can be different from the address the endpoint is bound to, in case
+2218    /// the endpoint is bound to a wildcard address like `0.0.0.0` or `::`.
+2219    ///
+2220    /// This will return `None` for clients, or when no `local_ip` was passed to
+2221    /// [`Endpoint::handle()`](crate::Endpoint::handle) for the datagrams establishing this
+2222    /// connection.
+2223    pub fn local_ip(&self) -> Option<IpAddr> {
+2224        self.local_ip
+2225    }
+2226
+2227    /// Current best estimate of this connection's latency (round-trip-time)
+2228    pub fn rtt(&self, path_id: PathId) -> Option<Duration> {
+2229        self.path(path_id).map(|d| d.rtt.get())
+2230    }
+2231
+2232    /// Current state of this connection's congestion controller, for debugging purposes
+2233    pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> {
+2234        self.path(path_id).map(|d| d.congestion.as_ref())
+2235    }
+2236
+2237    /// Modify the number of remotely initiated streams that may be concurrently open
+2238    ///
+2239    /// No streams may be opened by the peer unless fewer than `count` are already open. Large
+2240    /// `count`s increase both minimum and worst-case memory consumption.
+2241    pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) {
+2242        self.streams.set_max_concurrent(dir, count);
+2243        // If the limit was reduced, then a flow control update previously deemed insignificant may
+2244        // now be significant.
+2245        let pending = &mut self.spaces[SpaceId::Data].pending;
+2246        self.streams.queue_max_stream_id(pending);
+2247    }
+2248
+2249    /// Modify the number of open paths allowed when multipath is enabled
+2250    ///
+2251    /// When reducing the number of concurrent paths this will only affect delaying sending
+2252    /// new MAX_PATH_ID frames until fewer than this number of paths are possible.  To
+2253    /// actively reduce paths they must be closed using [`Connection::close_path`], which
+2254    /// can also be used to close not-yet-opened paths.
+2255    ///
+2256    /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable
+2257    /// multipath and will fail.
+2258    pub fn set_max_concurrent_paths(
+2259        &mut self,
+2260        now: Instant,
+2261        count: NonZeroU32,
+2262    ) -> Result<(), MultipathNotNegotiated> {
+2263        if !self.is_multipath_negotiated() {
+2264            return Err(MultipathNotNegotiated { _private: () });
+2265        }
+2266        self.max_concurrent_paths = count;
+2267
+2268        let in_use_count = self
+2269            .local_max_path_id
+2270            .next()
+2271            .saturating_sub(self.abandoned_paths.len() as u32)
+2272            .as_u32();
+2273        let extra_needed = count.get().saturating_sub(in_use_count);
+2274        let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed);
+2275
+2276        self.set_max_path_id(now, new_max_path_id);
+2277
+2278        Ok(())
+2279    }
+2280
+2281    /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths
+2282    fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) {
+2283        if max_path_id <= self.local_max_path_id {
+2284            return;
+2285        }
+2286
+2287        self.local_max_path_id = max_path_id;
+2288        self.spaces[SpaceId::Data].pending.max_path_id = true;
+2289
+2290        self.issue_first_path_cids(now);
+2291    }
+2292
+2293    /// Current number of remotely initiated streams that may be concurrently open
+2294    ///
+2295    /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams),
+2296    /// it will not change immediately, even if fewer streams are open. Instead, it will
+2297    /// decrement by one for each time a remotely initiated stream of matching directionality is closed.
+2298    pub fn max_concurrent_streams(&self, dir: Dir) -> u64 {
+2299        self.streams.max_concurrent(dir)
+2300    }
+2301
+2302    /// See [`TransportConfig::send_window()`]
+2303    pub fn set_send_window(&mut self, send_window: u64) {
+2304        self.streams.set_send_window(send_window);
+2305    }
+2306
+2307    /// See [`TransportConfig::receive_window()`]
+2308    pub fn set_receive_window(&mut self, receive_window: VarInt) {
+2309        if self.streams.set_receive_window(receive_window) {
+2310            self.spaces[SpaceId::Data].pending.max_data = true;
+2311        }
+2312    }
+2313
+2314    /// Whether the Multipath for QUIC extension is enabled.
+2315    ///
+2316    /// Multipath is only enabled after the handshake is completed and if it was enabled by both
+2317    /// peers.
+2318    pub fn is_multipath_negotiated(&self) -> bool {
+2319        !self.is_handshaking()
+2320            && self.config.max_concurrent_multipath_paths.is_some()
+2321            && self.peer_params.initial_max_path_id.is_some()
+2322    }
+2323
+2324    fn on_ack_received(
+2325        &mut self,
+2326        now: Instant,
+2327        space: SpaceId,
+2328        ack: frame::Ack,
+2329    ) -> Result<(), TransportError> {
+2330        // All ACKs are referencing path 0
+2331        let path = PathId::ZERO;
+2332        self.inner_on_ack_received(now, space, path, ack)
+2333    }
+2334
+2335    fn on_path_ack_received(
+2336        &mut self,
+2337        now: Instant,
+2338        space: SpaceId,
+2339        path_ack: frame::PathAck,
+2340    ) -> Result<(), TransportError> {
+2341        let (ack, path) = path_ack.into_ack();
+2342        self.inner_on_ack_received(now, space, path, ack)
+2343    }
+2344
+2345    /// Handles an ACK frame acknowledging packets sent on *path*.
+2346    fn inner_on_ack_received(
+2347        &mut self,
+2348        now: Instant,
+2349        space: SpaceId,
+2350        path: PathId,
+2351        ack: frame::Ack,
+2352    ) -> Result<(), TransportError> {
+2353        if self.abandoned_paths.contains(&path) {
+2354            // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3
+2355            // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4.
+2356            trace!("silently ignoring PATH_ACK on abandoned path");
+2357            return Ok(());
+2358        }
+2359        if ack.largest >= self.spaces[space].for_path(path).next_packet_number {
+2360            return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+2361        }
+2362        let new_largest = {
+2363            let space = &mut self.spaces[space].for_path(path);
+2364            if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) {
+2365                space.largest_acked_packet = Some(ack.largest);
+2366                if let Some(info) = space.sent_packets.get(ack.largest) {
+2367                    // This should always succeed, but a misbehaving peer might ACK a packet we
+2368                    // haven't sent. At worst, that will result in us spuriously reducing the
+2369                    // congestion window.
+2370                    space.largest_acked_packet_sent = info.time_sent;
+2371                }
+2372                true
+2373            } else {
+2374                false
+2375            }
+2376        };
+2377
+2378        if self.detect_spurious_loss(&ack, space, path) {
+2379            self.path_data_mut(path)
+2380                .congestion
+2381                .on_spurious_congestion_event();
+2382        }
+2383
+2384        // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks.
+2385        let mut newly_acked = ArrayRangeSet::new();
+2386        for range in ack.iter() {
+2387            self.spaces[space].for_path(path).check_ack(range.clone())?;
+2388            for (pn, _) in self.spaces[space]
+2389                .for_path(path)
+2390                .sent_packets
+2391                .iter_range(range)
+2392            {
+2393                newly_acked.insert_one(pn);
+2394            }
+2395        }
+2396
+2397        if newly_acked.is_empty() {
+2398            return Ok(());
+2399        }
+2400
+2401        let mut ack_eliciting_acked = false;
+2402        for packet in newly_acked.elts() {
+2403            if let Some(info) = self.spaces[space].for_path(path).take(packet) {
+2404                for (acked_path_id, acked_pn) in info.largest_acked.iter() {
+2405                    // Assume ACKs for all packets below the largest acknowledged in
+2406                    // `packet` have been received. This can cause the peer to spuriously
+2407                    // retransmit if some of our earlier ACKs were lost, but allows for
+2408                    // simpler state tracking. See discussion at
+2409                    // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking
+2410                    if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) {
+2411                        pns.pending_acks.subtract_below(*acked_pn);
+2412                    }
+2413                }
+2414                ack_eliciting_acked |= info.ack_eliciting;
+2415
+2416                // Notify MTU discovery that a packet was acked, because it might be an MTU probe
+2417                let path_data = self.path_data_mut(path);
+2418                let mtu_updated = path_data.mtud.on_acked(space, packet, info.size);
+2419                if mtu_updated {
+2420                    path_data
+2421                        .congestion
+2422                        .on_mtu_update(path_data.mtud.current_mtu());
+2423                }
+2424
+2425                // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame
+2426                self.ack_frequency.on_acked(path, packet);
+2427
+2428                self.on_packet_acked(now, path, info);
+2429            }
+2430        }
+2431
+2432        let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet;
+2433        let app_limited = self.app_limited;
+2434        let path_data = self.path_data_mut(path);
+2435        let in_flight = path_data.in_flight.bytes;
+2436
+2437        path_data
+2438            .congestion
+2439            .on_end_acks(now, in_flight, app_limited, largest_ackd);
+2440
+2441        if new_largest && ack_eliciting_acked {
+2442            let ack_delay = if space != SpaceId::Data {
+2443                Duration::from_micros(0)
+2444            } else {
+2445                cmp::min(
+2446                    self.ack_frequency.peer_max_ack_delay,
+2447                    Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0),
+2448                )
+2449            };
+2450            let rtt = now.saturating_duration_since(
+2451                self.spaces[space].for_path(path).largest_acked_packet_sent,
+2452            );
+2453
+2454            let next_pn = self.spaces[space].for_path(path).next_packet_number;
+2455            let path_data = self.path_data_mut(path);
+2456            // TODO(@divma): should be a method of path, should be contained in a single place
+2457            path_data.rtt.update(ack_delay, rtt);
+2458            if path_data.first_packet_after_rtt_sample.is_none() {
+2459                path_data.first_packet_after_rtt_sample = Some((space, next_pn));
+2460            }
+2461        }
+2462
+2463        // Must be called before crypto/pto_count are clobbered
+2464        self.detect_lost_packets(now, space, path, true);
+2465
+2466        if self.peer_completed_address_validation(path) {
+2467            self.path_data_mut(path).pto_count = 0;
+2468        }
+2469
+2470        // Explicit congestion notification
+2471        // TODO(@divma): this code is a good example of logic that should be contained in a single
+2472        // place but it's split between the path data and the packet number space data, we should
+2473        // find a way to make this work without two lookups
+2474        if self.path_data(path).sending_ecn {
+2475            if let Some(ecn) = ack.ecn {
+2476                // We only examine ECN counters from ACKs that we are certain we received in transmit
+2477                // order, allowing us to compute an increase in ECN counts to compare against the number
+2478                // of newly acked packets that remains well-defined in the presence of arbitrary packet
+2479                // reordering.
+2480                if new_largest {
+2481                    let sent = self.spaces[space].for_path(path).largest_acked_packet_sent;
+2482                    self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent);
+2483                }
+2484            } else {
+2485                // We always start out sending ECN, so any ack that doesn't acknowledge it disables it.
+2486                debug!("ECN not acknowledged by peer");
+2487                self.path_data_mut(path).sending_ecn = false;
+2488            }
+2489        }
+2490
+2491        self.set_loss_detection_timer(now, path);
+2492        Ok(())
+2493    }
+2494
+2495    fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool {
+2496        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2497
+2498        if lost_packets.is_empty() {
+2499            return false;
+2500        }
+2501
+2502        for range in ack.iter() {
+2503            let spurious_losses: Vec<u64> = lost_packets
+2504                .iter_range(range.clone())
+2505                .map(|(pn, _info)| pn)
+2506                .collect();
+2507
+2508            for pn in spurious_losses {
+2509                lost_packets.remove(pn);
+2510            }
+2511        }
+2512
+2513        // If this ACK frame acknowledged all deemed lost packets,
+2514        // then we have raised a spurious congestion event in the past.
+2515        // We cannot conclude when there are remaining packets,
+2516        // but future ACK frames might indicate a spurious loss detection.
+2517        lost_packets.is_empty()
+2518    }
+2519
+2520    /// Drain lost packets that we reasonably think will never arrive
+2521    ///
+2522    /// The current criterion is copied from `msquic`:
+2523    /// discard packets that were sent earlier than 2 probe timeouts ago.
+2524    fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) {
+2525        let two_pto = 2 * self.path_data(path).rtt.pto_base();
+2526
+2527        let lost_packets = &mut self.spaces[space].for_path(path).lost_packets;
+2528        lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto);
+2529    }
+2530
+2531    /// Process a new ECN block from an in-order ACK
+2532    fn process_ecn(
+2533        &mut self,
+2534        now: Instant,
+2535        space: SpaceId,
+2536        path: PathId,
+2537        newly_acked: u64,
+2538        ecn: frame::EcnCounts,
+2539        largest_sent_time: Instant,
+2540    ) {
+2541        match self.spaces[space]
+2542            .for_path(path)
+2543            .detect_ecn(newly_acked, ecn)
+2544        {
+2545            Err(e) => {
+2546                debug!("halting ECN due to verification failure: {}", e);
+2547
+2548                self.path_data_mut(path).sending_ecn = false;
+2549                // Wipe out the existing value because it might be garbage and could interfere with
+2550                // future attempts to use ECN on new paths.
+2551                self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO;
+2552            }
+2553            Ok(false) => {}
+2554            Ok(true) => {
+2555                self.path_stats.entry(path).or_default().congestion_events += 1;
+2556                self.path_data_mut(path).congestion.on_congestion_event(
+2557                    now,
+2558                    largest_sent_time,
+2559                    false,
+2560                    true,
+2561                    0,
+2562                );
+2563            }
+2564        }
+2565    }
+2566
+2567    // Not timing-aware, so it's safe to call this for inferred acks, such as arise from
+2568    // high-latency handshakes
+2569    fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) {
+2570        self.paths
+2571            .get_mut(&path_id)
+2572            .expect("known path")
+2573            .remove_in_flight(&info);
+2574        let app_limited = self.app_limited;
+2575        let path = self.path_data_mut(path_id);
+2576        if info.ack_eliciting && !path.is_validating_path() {
+2577            // Only pass ACKs to the congestion controller if we are not validating the current
+2578            // path, so as to ignore any ACKs from older paths still coming in.
+2579            let rtt = path.rtt;
+2580            path.congestion
+2581                .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt);
+2582        }
+2583
+2584        // Update state for confirmed delivery of frames
+2585        if let Some(retransmits) = info.retransmits.get() {
+2586            for (id, _) in retransmits.reset_stream.iter() {
+2587                self.streams.reset_acked(*id);
+2588            }
+2589        }
+2590
+2591        for frame in info.stream_frames {
+2592            self.streams.received_ack_of(frame);
+2593        }
+2594    }
+2595
+2596    fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) {
+2597        let start = if self.zero_rtt_crypto.is_some() {
+2598            now
+2599        } else {
+2600            self.prev_crypto
+2601                .as_ref()
+2602                .expect("no previous keys")
+2603                .end_packet
+2604                .as_ref()
+2605                .expect("update not acknowledged yet")
+2606                .1
+2607        };
+2608
+2609        // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths.
+2610        self.timers.set(
+2611            Timer::Conn(ConnTimer::KeyDiscard),
+2612            start + self.pto_max_path(space) * 3,
+2613            self.qlog.with_time(now),
+2614        );
+2615    }
+2616
+2617    /// Handle a [`PathTimer::LossDetection`] timeout.
+2618    ///
+2619    /// This timer expires for two reasons:
+2620    /// - An ACK-eliciting packet we sent should be considered lost.
+2621    /// - The PTO may have expired and a tail-loss probe needs to be scheduled.
+2622    ///
+2623    /// The former needs us to schedule re-transmission of the lost data.
+2624    ///
+2625    /// The latter means we have not received an ACK for an ack-eliciting packet we sent
+2626    /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting
+2627    /// packet, to try and elicit new acknowledgements. These new acknowledgements will
+2628    /// indicate whether the previously sent packets were lost or not.
+2629    fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) {
+2630        if let Some((_, pn_space)) = self.loss_time_and_space(path_id) {
+2631            // Time threshold loss Detection
+2632            self.detect_lost_packets(now, pn_space, path_id, false);
+2633            self.set_loss_detection_timer(now, path_id);
+2634            return;
+2635        }
+2636
+2637        let (_, space) = match self.pto_time_and_space(now, path_id) {
+2638            Some(x) => x,
+2639            None => {
+2640                error!(%path_id, "PTO expired while unset");
+2641                return;
+2642            }
+2643        };
+2644        trace!(
+2645            in_flight = self.path_data(path_id).in_flight.bytes,
+2646            count = self.path_data(path_id).pto_count,
+2647            ?space,
+2648            %path_id,
+2649            "PTO fired"
+2650        );
+2651
+2652        let count = match self.path_data(path_id).in_flight.ack_eliciting {
+2653            // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification
+2654            // deadlock preventions
+2655            0 => {
+2656                debug_assert!(!self.peer_completed_address_validation(path_id));
+2657                1
+2658            }
+2659            // Conventional loss probe
+2660            _ => 2,
+2661        };
+2662        let pns = self.spaces[space].for_path(path_id);
+2663        pns.loss_probes = pns.loss_probes.saturating_add(count);
+2664        let path_data = self.path_data_mut(path_id);
+2665        path_data.pto_count = path_data.pto_count.saturating_add(1);
+2666        self.set_loss_detection_timer(now, path_id);
+2667    }
+2668
+2669    /// Detect any lost packets
+2670    ///
+2671    /// There are two cases in which we detects lost packets:
+2672    ///
+2673    /// - We received an ACK packet.
+2674    /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet
+2675    ///   that was followed by an acknowledged packet. The loss timer for this
+2676    ///   un-acknowledged packet expired and we need to detect that packet as lost.
+2677    ///
+2678    /// Packets are lost if they are both (See RFC9002 §6.1):
+2679    ///
+2680    /// - Unacknowledged, in flight and sent prior to an acknowledged packet.
+2681    /// - Old enough by either:
+2682    ///   - Having a packet number [`TransportConfig::packet_threshold`] lower then the last
+2683    ///     acknowledged packet.
+2684    ///   - Being sent [`TransportConfig::time_threshold`] * RTT in the past.
+2685    fn detect_lost_packets(
+2686        &mut self,
+2687        now: Instant,
+2688        pn_space: SpaceId,
+2689        path_id: PathId,
+2690        due_to_ack: bool,
+2691    ) {
+2692        let mut lost_packets = Vec::<u64>::new();
+2693        let mut lost_mtu_probe = None;
+2694        let mut in_persistent_congestion = false;
+2695        let mut size_of_lost_packets = 0u64;
+2696        self.spaces[pn_space].for_path(path_id).loss_time = None;
+2697
+2698        // Find all the lost packets, populating all variables initialised above.
+2699
+2700        let path = self.path_data(path_id);
+2701        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+2702        let loss_delay = path
+2703            .rtt
+2704            .conservative()
+2705            .mul_f32(self.config.time_threshold)
+2706            .max(TIMER_GRANULARITY);
+2707        let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample;
+2708
+2709        let largest_acked_packet = self.spaces[pn_space]
+2710            .for_path(path_id)
+2711            .largest_acked_packet
+2712            .expect("detect_lost_packets only to be called if path received at least one ACK");
+2713        let packet_threshold = self.config.packet_threshold as u64;
+2714
+2715        // InPersistentCongestion: Determine if all packets in the time period before the newest
+2716        // lost packet, including the edges, are marked lost. PTO computation must always
+2717        // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1).
+2718        let congestion_period = self
+2719            .pto(SpaceId::Data, path_id)
+2720            .saturating_mul(self.config.persistent_congestion_threshold);
+2721        let mut persistent_congestion_start: Option<Instant> = None;
+2722        let mut prev_packet = None;
+2723        let space = self.spaces[pn_space].for_path(path_id);
+2724
+2725        for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) {
+2726            if prev_packet != Some(packet.wrapping_sub(1)) {
+2727                // An intervening packet was acknowledged
+2728                persistent_congestion_start = None;
+2729            }
+2730
+2731            // Packets sent before now - loss_delay are deemed lost.
+2732            // However, we avoid subtraction as it can panic and there's no
+2733            // saturating equivalent of this subtraction operation with a Duration.
+2734            let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay;
+2735            if packet_too_old || largest_acked_packet >= packet + packet_threshold {
+2736                // The packet should be declared lost.
+2737                if Some(packet) == in_flight_mtu_probe {
+2738                    // Lost MTU probes are not included in `lost_packets`, because they
+2739                    // should not trigger a congestion control response
+2740                    lost_mtu_probe = in_flight_mtu_probe;
+2741                } else {
+2742                    lost_packets.push(packet);
+2743                    size_of_lost_packets += info.size as u64;
+2744                    if info.ack_eliciting && due_to_ack {
+2745                        match persistent_congestion_start {
+2746                            // Two ACK-eliciting packets lost more than
+2747                            // congestion_period apart, with no ACKed packets in between
+2748                            Some(start) if info.time_sent - start > congestion_period => {
+2749                                in_persistent_congestion = true;
+2750                            }
+2751                            // Persistent congestion must start after the first RTT sample
+2752                            None if first_packet_after_rtt_sample
+2753                                .is_some_and(|x| x < (pn_space, packet)) =>
+2754                            {
+2755                                persistent_congestion_start = Some(info.time_sent);
+2756                            }
+2757                            _ => {}
+2758                        }
+2759                    }
+2760                }
+2761            } else {
+2762                // The packet should not yet be declared lost.
+2763                if space.loss_time.is_none() {
+2764                    // Since we iterate in order the lowest packet number's loss time will
+2765                    // always be the earliest.
+2766                    space.loss_time = Some(info.time_sent + loss_delay);
+2767                }
+2768                persistent_congestion_start = None;
+2769            }
+2770
+2771            prev_packet = Some(packet);
+2772        }
+2773
+2774        self.handle_lost_packets(
+2775            pn_space,
+2776            path_id,
+2777            now,
+2778            lost_packets,
+2779            lost_mtu_probe,
+2780            loss_delay,
+2781            in_persistent_congestion,
+2782            size_of_lost_packets,
+2783        );
+2784    }
+2785
+2786    /// Drops the path state, declaring any remaining in-flight packets as lost
+2787    fn discard_path(&mut self, path_id: PathId, now: Instant) {
+2788        trace!(%path_id, "dropping path state");
+2789        let path = self.path_data(path_id);
+2790        let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe();
+2791
+2792        let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes;
+2793        let lost_pns: Vec<_> = self.spaces[SpaceId::Data]
+2794            .for_path(path_id)
+2795            .sent_packets
+2796            .iter()
+2797            .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe)
+2798            .map(|(pn, info)| {
+2799                size_of_lost_packets += info.size as u64;
+2800                pn
+2801            })
+2802            .collect();
+2803
+2804        if !lost_pns.is_empty() {
+2805            trace!(
+2806                %path_id,
+2807                count = lost_pns.len(),
+2808                lost_bytes = size_of_lost_packets,
+2809                "packets lost on path abandon"
+2810            );
+2811            self.handle_lost_packets(
+2812                SpaceId::Data,
+2813                path_id,
+2814                now,
+2815                lost_pns,
+2816                in_flight_mtu_probe,
+2817                Duration::ZERO,
+2818                false,
+2819                size_of_lost_packets,
+2820            );
+2821        }
+2822        self.paths.remove(&path_id);
+2823        self.spaces[SpaceId::Data].number_spaces.remove(&path_id);
+2824
+2825        let path_stats = self.path_stats.remove(&path_id).unwrap_or_default();
+2826        self.events.push_back(
+2827            PathEvent::Abandoned {
+2828                id: path_id,
+2829                path_stats,
+2830            }
+2831            .into(),
+2832        );
+2833    }
+2834
+2835    fn handle_lost_packets(
+2836        &mut self,
+2837        pn_space: SpaceId,
+2838        path_id: PathId,
+2839        now: Instant,
+2840        lost_packets: Vec<u64>,
+2841        lost_mtu_probe: Option<u64>,
+2842        loss_delay: Duration,
+2843        in_persistent_congestion: bool,
+2844        size_of_lost_packets: u64,
+2845    ) {
+2846        debug_assert!(
+2847            {
+2848                let mut sorted = lost_packets.clone();
+2849                sorted.sort();
+2850                sorted == lost_packets
+2851            },
+2852            "lost_packets must be sorted"
+2853        );
+2854
+2855        self.drain_lost_packets(now, pn_space, path_id);
+2856
+2857        // OnPacketsLost
+2858        if let Some(largest_lost) = lost_packets.last().cloned() {
+2859            let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes;
+2860            let largest_lost_sent = self.spaces[pn_space]
+2861                .for_path(path_id)
+2862                .sent_packets
+2863                .get(largest_lost)
+2864                .unwrap()
+2865                .time_sent;
+2866            let path_stats = self.path_stats.entry(path_id).or_default();
+2867            path_stats.lost_packets += lost_packets.len() as u64;
+2868            path_stats.lost_bytes += size_of_lost_packets;
+2869            trace!(
+2870                %path_id,
+2871                count = lost_packets.len(),
+2872                lost_bytes = size_of_lost_packets,
+2873                "packets lost",
+2874            );
+2875
+2876            for &packet in &lost_packets {
+2877                let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else {
+2878                    continue;
+2879                };
+2880                self.qlog
+2881                    .emit_packet_lost(packet, &info, loss_delay, pn_space, now);
+2882                self.paths
+2883                    .get_mut(&path_id)
+2884                    .unwrap()
+2885                    .remove_in_flight(&info);
+2886
+2887                for frame in info.stream_frames {
+2888                    self.streams.retransmit(frame);
+2889                }
+2890                self.spaces[pn_space].pending |= info.retransmits;
+2891                self.path_data_mut(path_id)
+2892                    .mtud
+2893                    .on_non_probe_lost(packet, info.size);
+2894
+2895                self.spaces[pn_space].for_path(path_id).lost_packets.insert(
+2896                    packet,
+2897                    LostPacket {
+2898                        time_sent: info.time_sent,
+2899                    },
+2900                );
+2901            }
+2902
+2903            let path = self.path_data_mut(path_id);
+2904            if path.mtud.black_hole_detected(now) {
+2905                path.congestion.on_mtu_update(path.mtud.current_mtu());
+2906                if let Some(max_datagram_size) = self.datagrams().max_size() {
+2907                    self.datagrams.drop_oversized(max_datagram_size);
+2908                }
+2909                self.path_stats
+2910                    .entry(path_id)
+2911                    .or_default()
+2912                    .black_holes_detected += 1;
+2913            }
+2914
+2915            // Don't apply congestion penalty for lost ack-only packets
+2916            let lost_ack_eliciting =
+2917                old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes;
+2918
+2919            if lost_ack_eliciting {
+2920                self.path_stats
+2921                    .entry(path_id)
+2922                    .or_default()
+2923                    .congestion_events += 1;
+2924                self.path_data_mut(path_id).congestion.on_congestion_event(
+2925                    now,
+2926                    largest_lost_sent,
+2927                    in_persistent_congestion,
+2928                    false,
+2929                    size_of_lost_packets,
+2930                );
+2931            }
+2932        }
+2933
+2934        // Handle a lost MTU probe
+2935        if let Some(packet) = lost_mtu_probe {
+2936            let info = self.spaces[SpaceId::Data]
+2937                .for_path(path_id)
+2938                .take(packet)
+2939                .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and
+2940            // therefore must not have been removed yet
+2941            self.paths
+2942                .get_mut(&path_id)
+2943                .unwrap()
+2944                .remove_in_flight(&info);
+2945            self.path_data_mut(path_id).mtud.on_probe_lost();
+2946            self.path_stats
+2947                .entry(path_id)
+2948                .or_default()
+2949                .lost_plpmtud_probes += 1;
+2950        }
+2951    }
+2952
+2953    /// Returns the earliest time packets should be declared lost for all spaces on a path.
+2954    ///
+2955    /// If a path has an acknowledged packet with any prior un-acknowledged packets, the
+2956    /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed.
+2957    /// The time returned is when this packet should be declared lost.
+2958    fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> {
+2959        SpaceId::iter()
+2960            .filter_map(|id| {
+2961                self.spaces[id]
+2962                    .number_spaces
+2963                    .get(&path_id)
+2964                    .and_then(|pns| pns.loss_time)
+2965                    .map(|time| (time, id))
+2966            })
+2967            .min_by_key(|&(time, _)| time)
+2968    }
+2969
+2970    /// Returns the earliest next PTO should fire for all spaces on a path.
+2971    fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> {
+2972        let path = self.path(path_id)?;
+2973        let pto_count = path.pto_count;
+2974        let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT));
+2975        let mut duration = path.rtt.pto_base() * backoff;
+2976
+2977        if path_id == PathId::ZERO
+2978            && path.in_flight.ack_eliciting == 0
+2979            && !self.peer_completed_address_validation(PathId::ZERO)
+2980        {
+2981            // Address Validation during Connection Establishment:
+2982            // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a
+2983            // deadlock if an Initial or Handshake packet from the server is lost and the
+2984            // server can not send more due to its anti-amplification limit the client must
+2985            // send another packet on PTO.
+2986            let space = match self.highest_space {
+2987                SpaceId::Handshake => SpaceId::Handshake,
+2988                _ => SpaceId::Initial,
+2989            };
+2990
+2991            return Some((now + duration, space));
+2992        }
+2993
+2994        let mut result = None;
+2995        for space in SpaceId::iter() {
+2996            let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else {
+2997                continue;
+2998            };
+2999
+3000            if !pns.has_in_flight() {
+3001                continue;
+3002            }
+3003            if space == SpaceId::Data {
+3004                // Skip ApplicationData until handshake completes.
+3005                if self.is_handshaking() {
+3006                    return result;
+3007                }
+3008                // Include max_ack_delay and backoff for ApplicationData.
+3009                duration += self.ack_frequency.max_ack_delay_for_pto() * backoff;
+3010            }
+3011            let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else {
+3012                continue;
+3013            };
+3014            let pto = last_ack_eliciting + duration;
+3015            if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) {
+3016                if path.anti_amplification_blocked(1) {
+3017                    // Nothing would be able to be sent.
+3018                    continue;
+3019                }
+3020                if path.in_flight.ack_eliciting == 0 {
+3021                    // Nothing ack-eliciting, no PTO to arm/fire.
+3022                    continue;
+3023                }
+3024                result = Some((pto, space));
+3025            }
+3026        }
+3027        result
+3028    }
+3029
+3030    fn peer_completed_address_validation(&self, path: PathId) -> bool {
+3031        // TODO(flub): This logic needs updating for multipath
+3032        if self.side.is_server() || self.state.is_closed() {
+3033            return true;
+3034        }
+3035        // The server is guaranteed to have validated our address if any of our handshake or 1-RTT
+3036        // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys.
+3037        self.spaces[SpaceId::Handshake]
+3038            .path_space(PathId::ZERO)
+3039            .and_then(|pns| pns.largest_acked_packet)
+3040            .is_some()
+3041            || self.spaces[SpaceId::Data]
+3042                .path_space(path)
+3043                .and_then(|pns| pns.largest_acked_packet)
+3044                .is_some()
+3045            || (self.spaces[SpaceId::Data].crypto.is_some()
+3046                && self.spaces[SpaceId::Handshake].crypto.is_none())
+3047    }
+3048
+3049    /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed
+3050    ///
+3051    /// The timer must fire if either:
+3052    /// - An ack-eliciting packet we sent needs to be declared lost.
+3053    /// - A tail-loss probe needs to be sent.
+3054    ///
+3055    /// See [`Connection::on_loss_detection_timeout`] for details.
+3056    fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) {
+3057        if self.state.is_closed() {
+3058            // No loss detection takes place on closed connections, and `close_common` already
+3059            // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a
+3060            // reordered packet being handled by state-insensitive code.
+3061            return;
+3062        }
+3063
+3064        if let Some((loss_time, _)) = self.loss_time_and_space(path_id) {
+3065            // Time threshold loss detection.
+3066            self.timers.set(
+3067                Timer::PerPath(path_id, PathTimer::LossDetection),
+3068                loss_time,
+3069                self.qlog.with_time(now),
+3070            );
+3071            return;
+3072        }
+3073
+3074        // Determine which PN space to arm PTO for.
+3075        // Calculate PTO duration
+3076        if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) {
+3077            self.timers.set(
+3078                Timer::PerPath(path_id, PathTimer::LossDetection),
+3079                timeout,
+3080                self.qlog.with_time(now),
+3081            );
+3082        } else {
+3083            self.timers.stop(
+3084                Timer::PerPath(path_id, PathTimer::LossDetection),
+3085                self.qlog.with_time(now),
+3086            );
+3087        }
+3088    }
+3089
+3090    /// The maximum probe timeout across all paths
+3091    ///
+3092    /// See [`Connection::pto`]
+3093    fn pto_max_path(&self, space: SpaceId) -> Duration {
+3094        match space {
+3095            SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO),
+3096            SpaceId::Data => self
+3097                .paths
+3098                .keys()
+3099                .map(|path_id| self.pto(space, *path_id))
+3100                .max()
+3101                .expect("there should be one at least path"),
+3102        }
+3103    }
+3104
+3105    /// Probe Timeout
+3106    ///
+3107    /// The PTO is logically the time in which you'd expect to receive an acknowledgement
+3108    /// for a packet. So approximately RTT + max_ack_delay.
+3109    fn pto(&self, space: SpaceId, path_id: PathId) -> Duration {
+3110        let max_ack_delay = match space {
+3111            SpaceId::Initial | SpaceId::Handshake => Duration::ZERO,
+3112            SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(),
+3113        };
+3114        self.path_data(path_id).rtt.pto_base() + max_ack_delay
+3115    }
+3116
+3117    fn on_packet_authenticated(
+3118        &mut self,
+3119        now: Instant,
+3120        space_id: SpaceId,
+3121        path_id: PathId,
+3122        ecn: Option<EcnCodepoint>,
+3123        packet: Option<u64>,
+3124        spin: bool,
+3125        is_1rtt: bool,
+3126    ) {
+3127        self.total_authed_packets += 1;
+3128        if let Some(last_allowed_receive) = self
+3129            .paths
+3130            .get(&path_id)
+3131            .and_then(|path| path.data.last_allowed_receive)
+3132        {
+3133            if now > last_allowed_receive {
+3134                warn!("received data on path which we abandoned more than 3 * PTO ago");
+3135                // The peer failed to respond with a PATH_ABANDON in time.
+3136                if !self.state.is_closed() {
+3137                    // TODO(flub): What should the error code be?
+3138                    self.state.move_to_closed(TransportError::NO_ERROR(
+3139                        "peer failed to respond with PATH_ABANDON in time",
+3140                    ));
+3141                    self.close_common();
+3142                    self.set_close_timer(now);
+3143                    self.close = true;
+3144                }
+3145                return;
+3146            }
+3147        }
+3148
+3149        self.reset_keep_alive(path_id, now);
+3150        self.reset_idle_timeout(now, space_id, path_id);
+3151        self.permit_idle_reset = true;
+3152        self.receiving_ecn |= ecn.is_some();
+3153        if let Some(x) = ecn {
+3154            let space = &mut self.spaces[space_id];
+3155            space.for_path(path_id).ecn_counters += x;
+3156
+3157            if x.is_ce() {
+3158                space
+3159                    .for_path(path_id)
+3160                    .pending_acks
+3161                    .set_immediate_ack_required();
+3162            }
+3163        }
+3164
+3165        let packet = match packet {
+3166            Some(x) => x,
+3167            None => return,
+3168        };
+3169        match &self.side {
+3170            ConnectionSide::Client { .. } => {
+3171                // If we received a handshake packet that authenticated, then we're talking to
+3172                // the real server.  From now on we should no longer allow the server to migrate
+3173                // its address.
+3174                if space_id == SpaceId::Handshake {
+3175                    if let Some(hs) = self.state.as_handshake_mut() {
+3176                        hs.allow_server_migration = false;
+3177                    }
+3178                }
+3179            }
+3180            ConnectionSide::Server { .. } => {
+3181                if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake
+3182                {
+3183                    // A server stops sending and processing Initial packets when it receives its first Handshake packet.
+3184                    self.discard_space(now, SpaceId::Initial);
+3185                }
+3186                if self.zero_rtt_crypto.is_some() && is_1rtt {
+3187                    // Discard 0-RTT keys soon after receiving a 1-RTT packet
+3188                    self.set_key_discard_timer(now, space_id)
+3189                }
+3190            }
+3191        }
+3192        let space = self.spaces[space_id].for_path(path_id);
+3193        space.pending_acks.insert_one(packet, now);
+3194        if packet >= space.rx_packet.unwrap_or_default() {
+3195            space.rx_packet = Some(packet);
+3196            // Update outgoing spin bit, inverting iff we're the client
+3197            self.spin = self.side.is_client() ^ spin;
+3198        }
+3199    }
+3200
+3201    /// Resets the idle timeout timers
+3202    ///
+3203    /// Without multipath there is only the connection-wide idle timeout. When multipath is
+3204    /// enabled there is an additional per-path idle timeout.
+3205    fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) {
+3206        // First reset the global idle timeout.
+3207        if let Some(timeout) = self.idle_timeout {
+3208            if self.state.is_closed() {
+3209                self.timers
+3210                    .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now));
+3211            } else {
+3212                let dt = cmp::max(timeout, 3 * self.pto_max_path(space));
+3213                self.timers.set(
+3214                    Timer::Conn(ConnTimer::Idle),
+3215                    now + dt,
+3216                    self.qlog.with_time(now),
+3217                );
+3218            }
+3219        }
+3220
+3221        // Now handle the per-path state
+3222        if let Some(timeout) = self.path_data(path_id).idle_timeout {
+3223            if self.state.is_closed() {
+3224                self.timers.stop(
+3225                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3226                    self.qlog.with_time(now),
+3227                );
+3228            } else {
+3229                let dt = cmp::max(timeout, 3 * self.pto(space, path_id));
+3230                self.timers.set(
+3231                    Timer::PerPath(path_id, PathTimer::PathIdle),
+3232                    now + dt,
+3233                    self.qlog.with_time(now),
+3234                );
+3235            }
+3236        }
+3237    }
+3238
+3239    /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers
+3240    fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) {
+3241        if !self.state.is_established() {
+3242            return;
+3243        }
+3244
+3245        if let Some(interval) = self.config.keep_alive_interval {
+3246            self.timers.set(
+3247                Timer::Conn(ConnTimer::KeepAlive),
+3248                now + interval,
+3249                self.qlog.with_time(now),
+3250            );
+3251        }
+3252
+3253        if let Some(interval) = self.path_data(path_id).keep_alive {
+3254            self.timers.set(
+3255                Timer::PerPath(path_id, PathTimer::PathKeepAlive),
+3256                now + interval,
+3257                self.qlog.with_time(now),
+3258            );
+3259        }
+3260    }
+3261
+3262    /// Sets the timer for when a previously issued CID should be retired next
+3263    fn reset_cid_retirement(&mut self, now: Instant) {
+3264        if let Some((_path, t)) = self.next_cid_retirement() {
+3265            self.timers.set(
+3266                Timer::Conn(ConnTimer::PushNewCid),
+3267                t,
+3268                self.qlog.with_time(now),
+3269            );
+3270        }
+3271    }
+3272
+3273    /// The next time when a previously issued CID should be retired
+3274    fn next_cid_retirement(&self) -> Option<(PathId, Instant)> {
+3275        self.local_cid_state
+3276            .iter()
+3277            .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t)))
+3278            .min_by_key(|(_path_id, timeout)| *timeout)
+3279    }
+3280
+3281    /// Handle the already-decrypted first packet from the client
+3282    ///
+3283    /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more
+3284    /// efficient.
+3285    pub(crate) fn handle_first_packet(
+3286        &mut self,
+3287        now: Instant,
+3288        remote: SocketAddr,
+3289        ecn: Option<EcnCodepoint>,
+3290        packet_number: u64,
+3291        packet: InitialPacket,
+3292        remaining: Option<BytesMut>,
+3293    ) -> Result<(), ConnectionError> {
+3294        let span = trace_span!("first recv");
+3295        let _guard = span.enter();
+3296        debug_assert!(self.side.is_server());
+3297        let len = packet.header_data.len() + packet.payload.len();
+3298        let path_id = PathId::ZERO;
+3299        self.path_data_mut(path_id).total_recvd = len as u64;
+3300
+3301        if let Some(hs) = self.state.as_handshake_mut() {
+3302            hs.expected_token = packet.header.token.clone();
+3303        } else {
+3304            unreachable!("first packet must be delivered in Handshake state");
+3305        }
+3306
+3307        // The first packet is always on PathId::ZERO
+3308        self.on_packet_authenticated(
+3309            now,
+3310            SpaceId::Initial,
+3311            path_id,
+3312            ecn,
+3313            Some(packet_number),
+3314            false,
+3315            false,
+3316        );
+3317
+3318        let packet: Packet = packet.into();
+3319
+3320        let mut qlog = QlogRecvPacket::new(len);
+3321        qlog.header(&packet.header, Some(packet_number), path_id);
+3322
+3323        self.process_decrypted_packet(
+3324            now,
+3325            remote,
+3326            path_id,
+3327            Some(packet_number),
+3328            packet,
+3329            &mut qlog,
+3330        )?;
+3331        self.qlog.emit_packet_received(qlog, now);
+3332        if let Some(data) = remaining {
+3333            self.handle_coalesced(now, remote, path_id, ecn, data);
+3334        }
+3335
+3336        self.qlog.emit_recovery_metrics(
+3337            path_id,
+3338            &mut self.paths.get_mut(&path_id).unwrap().data,
+3339            now,
+3340        );
+3341
+3342        Ok(())
+3343    }
+3344
+3345    fn init_0rtt(&mut self, now: Instant) {
+3346        let (header, packet) = match self.crypto.early_crypto() {
+3347            Some(x) => x,
+3348            None => return,
+3349        };
+3350        if self.side.is_client() {
+3351            match self.crypto.transport_parameters() {
+3352                Ok(params) => {
+3353                    let params = params
+3354                        .expect("crypto layer didn't supply transport parameters with ticket");
+3355                    // Certain values must not be cached
+3356                    let params = TransportParameters {
+3357                        initial_src_cid: None,
+3358                        original_dst_cid: None,
+3359                        preferred_address: None,
+3360                        retry_src_cid: None,
+3361                        stateless_reset_token: None,
+3362                        min_ack_delay: None,
+3363                        ack_delay_exponent: TransportParameters::default().ack_delay_exponent,
+3364                        max_ack_delay: TransportParameters::default().max_ack_delay,
+3365                        initial_max_path_id: None,
+3366                        ..params
+3367                    };
+3368                    self.set_peer_params(params);
+3369                    self.qlog.emit_peer_transport_params_restored(self, now);
+3370                }
+3371                Err(e) => {
+3372                    error!("session ticket has malformed transport parameters: {}", e);
+3373                    return;
+3374                }
+3375            }
+3376        }
+3377        trace!("0-RTT enabled");
+3378        self.zero_rtt_enabled = true;
+3379        self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet });
+3380    }
+3381
+3382    fn read_crypto(
+3383        &mut self,
+3384        space: SpaceId,
+3385        crypto: &frame::Crypto,
+3386        payload_len: usize,
+3387    ) -> Result<(), TransportError> {
+3388        let expected = if !self.state.is_handshake() {
+3389            SpaceId::Data
+3390        } else if self.highest_space == SpaceId::Initial {
+3391            SpaceId::Initial
+3392        } else {
+3393            // On the server, self.highest_space can be Data after receiving the client's first
+3394            // flight, but we expect Handshake CRYPTO until the handshake is complete.
+3395            SpaceId::Handshake
+3396        };
+3397        // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT
+3398        // packets are illegal, and we don't process 1-RTT packets until the handshake is
+3399        // complete. Therefore, we will never see CRYPTO data from a later-than-expected space.
+3400        debug_assert!(space <= expected, "received out-of-order CRYPTO data");
+3401
+3402        let end = crypto.offset + crypto.data.len() as u64;
+3403        if space < expected && end > self.spaces[space].crypto_stream.bytes_read() {
+3404            warn!(
+3405                "received new {:?} CRYPTO data when expecting {:?}",
+3406                space, expected
+3407            );
+3408            return Err(TransportError::PROTOCOL_VIOLATION(
+3409                "new data at unexpected encryption level",
+3410            ));
+3411        }
+3412
+3413        let space = &mut self.spaces[space];
+3414        let max = end.saturating_sub(space.crypto_stream.bytes_read());
+3415        if max > self.config.crypto_buffer_size as u64 {
+3416            return Err(TransportError::CRYPTO_BUFFER_EXCEEDED(""));
+3417        }
+3418
+3419        space
+3420            .crypto_stream
+3421            .insert(crypto.offset, crypto.data.clone(), payload_len);
+3422        while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) {
+3423            trace!("consumed {} CRYPTO bytes", chunk.bytes.len());
+3424            if self.crypto.read_handshake(&chunk.bytes)? {
+3425                self.events.push_back(Event::HandshakeDataReady);
+3426            }
+3427        }
+3428
+3429        Ok(())
+3430    }
+3431
+3432    fn write_crypto(&mut self) {
+3433        loop {
+3434            let space = self.highest_space;
+3435            let mut outgoing = Vec::new();
+3436            if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) {
+3437                match space {
+3438                    SpaceId::Initial => {
+3439                        self.upgrade_crypto(SpaceId::Handshake, crypto);
+3440                    }
+3441                    SpaceId::Handshake => {
+3442                        self.upgrade_crypto(SpaceId::Data, crypto);
+3443                    }
+3444                    _ => unreachable!("got updated secrets during 1-RTT"),
+3445                }
+3446            }
+3447            if outgoing.is_empty() {
+3448                if space == self.highest_space {
+3449                    break;
+3450                } else {
+3451                    // Keys updated, check for more data to send
+3452                    continue;
+3453                }
+3454            }
+3455            let offset = self.spaces[space].crypto_offset;
+3456            let outgoing = Bytes::from(outgoing);
+3457            if let Some(hs) = self.state.as_handshake_mut() {
+3458                if space == SpaceId::Initial && offset == 0 && self.side.is_client() {
+3459                    hs.client_hello = Some(outgoing.clone());
+3460                }
+3461            }
+3462            self.spaces[space].crypto_offset += outgoing.len() as u64;
+3463            trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space);
+3464            self.spaces[space].pending.crypto.push_back(frame::Crypto {
+3465                offset,
+3466                data: outgoing,
+3467            });
+3468        }
+3469    }
+3470
+3471    /// Switch to stronger cryptography during handshake
+3472    fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) {
+3473        debug_assert!(
+3474            self.spaces[space].crypto.is_none(),
+3475            "already reached packet space {space:?}"
+3476        );
+3477        trace!("{:?} keys ready", space);
+3478        if space == SpaceId::Data {
+3479            // Precompute the first key update
+3480            self.next_crypto = Some(
+3481                self.crypto
+3482                    .next_1rtt_keys()
+3483                    .expect("handshake should be complete"),
+3484            );
+3485        }
+3486
+3487        self.spaces[space].crypto = Some(crypto);
+3488        debug_assert!(space as usize > self.highest_space as usize);
+3489        self.highest_space = space;
+3490        if space == SpaceId::Data && self.side.is_client() {
+3491            // Discard 0-RTT keys because 1-RTT keys are available.
+3492            self.zero_rtt_crypto = None;
+3493        }
+3494    }
+3495
+3496    fn discard_space(&mut self, now: Instant, space_id: SpaceId) {
+3497        debug_assert!(space_id != SpaceId::Data);
+3498        trace!("discarding {:?} keys", space_id);
+3499        if space_id == SpaceId::Initial {
+3500            // No longer needed
+3501            if let ConnectionSide::Client { token, .. } = &mut self.side {
+3502                *token = Bytes::new();
+3503            }
+3504        }
+3505        let space = &mut self.spaces[space_id];
+3506        space.crypto = None;
+3507        let pns = space.for_path(PathId::ZERO);
+3508        pns.time_of_last_ack_eliciting_packet = None;
+3509        pns.loss_time = None;
+3510        pns.loss_probes = 0;
+3511        let sent_packets = mem::take(&mut pns.sent_packets);
+3512        let path = self.paths.get_mut(&PathId::ZERO).unwrap();
+3513        for (_, packet) in sent_packets.into_iter() {
+3514            path.data.remove_in_flight(&packet);
+3515        }
+3516
+3517        self.set_loss_detection_timer(now, PathId::ZERO)
+3518    }
+3519
+3520    fn handle_coalesced(
+3521        &mut self,
+3522        now: Instant,
+3523        remote: SocketAddr,
+3524        path_id: PathId,
+3525        ecn: Option<EcnCodepoint>,
+3526        data: BytesMut,
+3527    ) {
+3528        self.path_data_mut(path_id)
+3529            .inc_total_recvd(data.len() as u64);
+3530        let mut remaining = Some(data);
+3531        let cid_len = self
+3532            .local_cid_state
+3533            .values()
+3534            .map(|cid_state| cid_state.cid_len())
+3535            .next()
+3536            .expect("one cid_state must exist");
+3537        while let Some(data) = remaining {
+3538            match PartialDecode::new(
+3539                data,
+3540                &FixedLengthConnectionIdParser::new(cid_len),
+3541                &[self.version],
+3542                self.endpoint_config.grease_quic_bit,
+3543            ) {
+3544                Ok((partial_decode, rest)) => {
+3545                    remaining = rest;
+3546                    self.handle_decode(now, remote, path_id, ecn, partial_decode);
+3547                }
+3548                Err(e) => {
+3549                    trace!("malformed header: {}", e);
+3550                    return;
+3551                }
+3552            }
+3553        }
+3554    }
+3555
+3556    fn handle_decode(
+3557        &mut self,
+3558        now: Instant,
+3559        remote: SocketAddr,
+3560        path_id: PathId,
+3561        ecn: Option<EcnCodepoint>,
+3562        partial_decode: PartialDecode,
+3563    ) {
+3564        let qlog = QlogRecvPacket::new(partial_decode.len());
+3565        if let Some(decoded) = packet_crypto::unprotect_header(
+3566            partial_decode,
+3567            &self.spaces,
+3568            self.zero_rtt_crypto.as_ref(),
+3569            self.peer_params.stateless_reset_token,
+3570        ) {
+3571            self.handle_packet(
+3572                now,
+3573                remote,
+3574                path_id,
+3575                ecn,
+3576                decoded.packet,
+3577                decoded.stateless_reset,
+3578                qlog,
+3579            );
+3580        }
+3581    }
+3582
+3583    fn handle_packet(
+3584        &mut self,
+3585        now: Instant,
+3586        remote: SocketAddr,
+3587        path_id: PathId,
+3588        ecn: Option<EcnCodepoint>,
+3589        packet: Option<Packet>,
+3590        stateless_reset: bool,
+3591        mut qlog: QlogRecvPacket,
+3592    ) {
+3593        self.stats.udp_rx.ios += 1;
+3594        if let Some(ref packet) = packet {
+3595            trace!(
+3596                "got {:?} packet ({} bytes) from {} using id {}",
+3597                packet.header.space(),
+3598                packet.payload.len() + packet.header_data.len(),
+3599                remote,
+3600                packet.header.dst_cid(),
+3601            );
+3602        }
+3603
+3604        if self.is_handshaking() {
+3605            if path_id != PathId::ZERO {
+3606                debug!(%remote, %path_id, "discarding multipath packet during handshake");
+3607                return;
+3608            }
+3609            if remote != self.path_data_mut(path_id).remote {
+3610                if let Some(hs) = self.state.as_handshake() {
+3611                    if hs.allow_server_migration {
+3612                        trace!(?remote, prev = ?self.path_data(path_id).remote, "server migrated to new remote");
+3613                        self.path_data_mut(path_id).remote = remote;
+3614                        self.qlog.emit_tuple_assigned(path_id, remote, now);
+3615                    } else {
+3616                        debug!("discarding packet with unexpected remote during handshake");
+3617                        return;
+3618                    }
+3619                } else {
+3620                    debug!("discarding packet with unexpected remote during handshake");
+3621                    return;
+3622                }
+3623            }
+3624        }
+3625
+3626        let was_closed = self.state.is_closed();
+3627        let was_drained = self.state.is_drained();
+3628
+3629        let decrypted = match packet {
+3630            None => Err(None),
+3631            Some(mut packet) => self
+3632                .decrypt_packet(now, path_id, &mut packet)
+3633                .map(move |number| (packet, number)),
+3634        };
+3635        let result = match decrypted {
+3636            _ if stateless_reset => {
+3637                debug!("got stateless reset");
+3638                Err(ConnectionError::Reset)
+3639            }
+3640            Err(Some(e)) => {
+3641                warn!("illegal packet: {}", e);
+3642                Err(e.into())
+3643            }
+3644            Err(None) => {
+3645                debug!("failed to authenticate packet");
+3646                self.authentication_failures += 1;
+3647                let integrity_limit = self.spaces[self.highest_space]
+3648                    .crypto
+3649                    .as_ref()
+3650                    .unwrap()
+3651                    .packet
+3652                    .local
+3653                    .integrity_limit();
+3654                if self.authentication_failures > integrity_limit {
+3655                    Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into())
+3656                } else {
+3657                    return;
+3658                }
+3659            }
+3660            Ok((packet, number)) => {
+3661                qlog.header(&packet.header, number, path_id);
+3662                let span = match number {
+3663                    Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
+3664                    None => trace_span!("recv", space = ?packet.header.space()),
+3665                };
+3666                let _guard = span.enter();
+3667
+3668                let dedup = self.spaces[packet.header.space()]
+3669                    .path_space_mut(path_id)
+3670                    .map(|pns| &mut pns.dedup);
+3671                if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) {
+3672                    debug!("discarding possible duplicate packet");
+3673                    self.qlog.emit_packet_received(qlog, now);
+3674                    return;
+3675                } else if self.state.is_handshake() && packet.header.is_short() {
+3676                    // TODO: SHOULD buffer these to improve reordering tolerance.
+3677                    trace!("dropping short packet during handshake");
+3678                    self.qlog.emit_packet_received(qlog, now);
+3679                    return;
+3680                } else {
+3681                    if let Header::Initial(InitialHeader { ref token, .. }) = packet.header {
+3682                        if let Some(hs) = self.state.as_handshake() {
+3683                            if self.side.is_server() && token != &hs.expected_token {
+3684                                // Clients must send the same retry token in every Initial. Initial
+3685                                // packets can be spoofed, so we discard rather than killing the
+3686                                // connection.
+3687                                warn!("discarding Initial with invalid retry token");
+3688                                self.qlog.emit_packet_received(qlog, now);
+3689                                return;
+3690                            }
+3691                        }
+3692                    }
+3693
+3694                    if !self.state.is_closed() {
+3695                        let spin = match packet.header {
+3696                            Header::Short { spin, .. } => spin,
+3697                            _ => false,
+3698                        };
+3699
+3700                        if self.side().is_server() && !self.abandoned_paths.contains(&path_id) {
+3701                            // Only the client is allowed to open paths
+3702                            self.ensure_path(path_id, remote, now, number);
+3703                        }
+3704                        if self.paths.contains_key(&path_id) {
+3705                            self.on_packet_authenticated(
+3706                                now,
+3707                                packet.header.space(),
+3708                                path_id,
+3709                                ecn,
+3710                                number,
+3711                                spin,
+3712                                packet.header.is_1rtt(),
+3713                            );
+3714                        }
+3715                    }
+3716
+3717                    let res = self
+3718                        .process_decrypted_packet(now, remote, path_id, number, packet, &mut qlog);
+3719
+3720                    self.qlog.emit_packet_received(qlog, now);
+3721                    res
+3722                }
+3723            }
+3724        };
+3725
+3726        // State transitions for error cases
+3727        if let Err(conn_err) = result {
+3728            match conn_err {
+3729                ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason),
+3730                ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason),
+3731                ConnectionError::Reset
+3732                | ConnectionError::TransportError(TransportError {
+3733                    code: TransportErrorCode::AEAD_LIMIT_REACHED,
+3734                    ..
+3735                }) => {
+3736                    self.state.move_to_drained(Some(conn_err));
+3737                }
+3738                ConnectionError::TimedOut => {
+3739                    unreachable!("timeouts aren't generated by packet processing");
+3740                }
+3741                ConnectionError::TransportError(err) => {
+3742                    debug!("closing connection due to transport error: {}", err);
+3743                    self.state.move_to_closed(err);
+3744                }
+3745                ConnectionError::VersionMismatch => {
+3746                    self.state.move_to_draining(Some(conn_err));
+3747                }
+3748                ConnectionError::LocallyClosed => {
+3749                    unreachable!("LocallyClosed isn't generated by packet processing");
+3750                }
+3751                ConnectionError::CidsExhausted => {
+3752                    unreachable!("CidsExhausted isn't generated by packet processing");
+3753                }
+3754            };
+3755        }
+3756
+3757        if !was_closed && self.state.is_closed() {
+3758            self.close_common();
+3759            if !self.state.is_drained() {
+3760                self.set_close_timer(now);
+3761            }
+3762        }
+3763        if !was_drained && self.state.is_drained() {
+3764            self.endpoint_events.push_back(EndpointEventInner::Drained);
+3765            // Close timer may have been started previously, e.g. if we sent a close and got a
+3766            // stateless reset in response
+3767            self.timers
+3768                .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now));
+3769        }
+3770
+3771        // Transmit CONNECTION_CLOSE if necessary
+3772        if matches!(self.state.as_type(), StateType::Closed) {
+3773            // If there is no PathData for this PathId the packet was for a brand new
+3774            // path. It was a valid packet however, so the remote is valid and we want to
+3775            // send CONNECTION_CLOSE.
+3776            let path_remote = self
+3777                .paths
+3778                .get(&path_id)
+3779                .map(|p| p.data.remote)
+3780                .unwrap_or(remote);
+3781            self.close = remote == path_remote;
+3782        }
+3783    }
+3784
+3785    fn process_decrypted_packet(
+3786        &mut self,
+3787        now: Instant,
+3788        remote: SocketAddr,
+3789        path_id: PathId,
+3790        number: Option<u64>,
+3791        packet: Packet,
+3792        qlog: &mut QlogRecvPacket,
+3793    ) -> Result<(), ConnectionError> {
+3794        if !self.paths.contains_key(&path_id) {
+3795            // There is a chance this is a server side, first (for this path) packet, which would
+3796            // be a protocol violation. It's more likely, however, that this is a packet of a
+3797            // pruned path
+3798            trace!(%path_id, ?number, "discarding packet for unknown path");
+3799            return Ok(());
+3800        }
+3801        let state = match self.state.as_type() {
+3802            StateType::Established => {
+3803                match packet.header.space() {
+3804                    SpaceId::Data => {
+3805                        self.process_payload(now, remote, path_id, number.unwrap(), packet, qlog)?
+3806                    }
+3807                    _ if packet.header.has_frames() => {
+3808                        self.process_early_payload(now, path_id, packet, qlog)?
+3809                    }
+3810                    _ => {
+3811                        trace!("discarding unexpected pre-handshake packet");
+3812                    }
+3813                }
+3814                return Ok(());
+3815            }
+3816            StateType::Closed => {
+3817                for result in frame::Iter::new(packet.payload.freeze())? {
+3818                    let frame = match result {
+3819                        Ok(frame) => frame,
+3820                        Err(err) => {
+3821                            debug!("frame decoding error: {err:?}");
+3822                            continue;
+3823                        }
+3824                    };
+3825                    qlog.frame(&frame);
+3826
+3827                    if let Frame::Padding = frame {
+3828                        continue;
+3829                    };
+3830
+3831                    self.stats.frame_rx.record(&frame);
+3832
+3833                    if let Frame::Close(_error) = frame {
+3834                        trace!("draining");
+3835                        self.state.move_to_draining(None);
+3836                        break;
+3837                    }
+3838                }
+3839                return Ok(());
+3840            }
+3841            StateType::Draining | StateType::Drained => return Ok(()),
+3842            StateType::Handshake => self.state.as_handshake_mut().expect("checked"),
+3843        };
+3844
+3845        match packet.header {
+3846            Header::Retry {
+3847                src_cid: rem_cid, ..
+3848            } => {
+3849                debug_assert_eq!(path_id, PathId::ZERO);
+3850                if self.side.is_server() {
+3851                    return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into());
+3852                }
+3853
+3854                let is_valid_retry = self
+3855                    .rem_cids
+3856                    .get(&path_id)
+3857                    .map(|cids| cids.active())
+3858                    .map(|orig_dst_cid| {
+3859                        self.crypto.is_valid_retry(
+3860                            orig_dst_cid,
+3861                            &packet.header_data,
+3862                            &packet.payload,
+3863                        )
+3864                    })
+3865                    .unwrap_or_default();
+3866                if self.total_authed_packets > 1
+3867                            || packet.payload.len() <= 16 // token + 16 byte tag
+3868                            || !is_valid_retry
+3869                {
+3870                    trace!("discarding invalid Retry");
+3871                    // - After the client has received and processed an Initial or Retry
+3872                    //   packet from the server, it MUST discard any subsequent Retry
+3873                    //   packets that it receives.
+3874                    // - A client MUST discard a Retry packet with a zero-length Retry Token
+3875                    //   field.
+3876                    // - Clients MUST discard Retry packets that have a Retry Integrity Tag
+3877                    //   that cannot be validated
+3878                    return Ok(());
+3879                }
+3880
+3881                trace!("retrying with CID {}", rem_cid);
+3882                let client_hello = state.client_hello.take().unwrap();
+3883                self.retry_src_cid = Some(rem_cid);
+3884                self.rem_cids
+3885                    .get_mut(&path_id)
+3886                    .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false")
+3887                    .update_initial_cid(rem_cid);
+3888                self.rem_handshake_cid = rem_cid;
+3889
+3890                let space = &mut self.spaces[SpaceId::Initial];
+3891                if let Some(info) = space.for_path(PathId::ZERO).take(0) {
+3892                    self.on_packet_acked(now, PathId::ZERO, info);
+3893                };
+3894
+3895                self.discard_space(now, SpaceId::Initial); // Make sure we clean up after
+3896                // any retransmitted Initials
+3897                self.spaces[SpaceId::Initial] = {
+3898                    let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng);
+3899                    space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side()));
+3900                    space.crypto_offset = client_hello.len() as u64;
+3901                    space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial]
+3902                        .for_path(path_id)
+3903                        .next_packet_number;
+3904                    space.pending.crypto.push_back(frame::Crypto {
+3905                        offset: 0,
+3906                        data: client_hello,
+3907                    });
+3908                    space
+3909                };
+3910
+3911                // Retransmit all 0-RTT data
+3912                let zero_rtt = mem::take(
+3913                    &mut self.spaces[SpaceId::Data]
+3914                        .for_path(PathId::ZERO)
+3915                        .sent_packets,
+3916                );
+3917                for (_, info) in zero_rtt.into_iter() {
+3918                    self.paths
+3919                        .get_mut(&PathId::ZERO)
+3920                        .unwrap()
+3921                        .remove_in_flight(&info);
+3922                    self.spaces[SpaceId::Data].pending |= info.retransmits;
+3923                }
+3924                self.streams.retransmit_all_for_0rtt();
+3925
+3926                let token_len = packet.payload.len() - 16;
+3927                let ConnectionSide::Client { ref mut token, .. } = self.side else {
+3928                    unreachable!("we already short-circuited if we're server");
+3929                };
+3930                *token = packet.payload.freeze().split_to(token_len);
+3931
+3932                self.state = State::handshake(state::Handshake {
+3933                    expected_token: Bytes::new(),
+3934                    rem_cid_set: false,
+3935                    client_hello: None,
+3936                    allow_server_migration: true,
+3937                });
+3938                Ok(())
+3939            }
+3940            Header::Long {
+3941                ty: LongType::Handshake,
+3942                src_cid: rem_cid,
+3943                dst_cid: loc_cid,
+3944                ..
+3945            } => {
+3946                debug_assert_eq!(path_id, PathId::ZERO);
+3947                if rem_cid != self.rem_handshake_cid {
+3948                    debug!(
+3949                        "discarding packet with mismatched remote CID: {} != {}",
+3950                        self.rem_handshake_cid, rem_cid
+3951                    );
+3952                    return Ok(());
+3953                }
+3954                self.on_path_validated(path_id);
+3955
+3956                self.process_early_payload(now, path_id, packet, qlog)?;
+3957                if self.state.is_closed() {
+3958                    return Ok(());
+3959                }
+3960
+3961                if self.crypto.is_handshaking() {
+3962                    trace!("handshake ongoing");
+3963                    return Ok(());
+3964                }
+3965
+3966                if self.side.is_client() {
+3967                    // Client-only because server params were set from the client's Initial
+3968                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+3969                        TransportError::new(
+3970                            TransportErrorCode::crypto(0x6d),
+3971                            "transport parameters missing".to_owned(),
+3972                        )
+3973                    })?;
+3974
+3975                    if self.has_0rtt() {
+3976                        if !self.crypto.early_data_accepted().unwrap() {
+3977                            debug_assert!(self.side.is_client());
+3978                            debug!("0-RTT rejected");
+3979                            self.accepted_0rtt = false;
+3980                            self.streams.zero_rtt_rejected();
+3981
+3982                            // Discard already-queued frames
+3983                            self.spaces[SpaceId::Data].pending = Retransmits::default();
+3984
+3985                            // Discard 0-RTT packets
+3986                            let sent_packets = mem::take(
+3987                                &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets,
+3988                            );
+3989                            for (_, packet) in sent_packets.into_iter() {
+3990                                self.paths
+3991                                    .get_mut(&path_id)
+3992                                    .unwrap()
+3993                                    .remove_in_flight(&packet);
+3994                            }
+3995                        } else {
+3996                            self.accepted_0rtt = true;
+3997                            params.validate_resumption_from(&self.peer_params)?;
+3998                        }
+3999                    }
+4000                    if let Some(token) = params.stateless_reset_token {
+4001                        let remote = self.path_data(path_id).remote;
+4002                        self.endpoint_events
+4003                            .push_back(EndpointEventInner::ResetToken(path_id, remote, token));
+4004                    }
+4005                    self.handle_peer_params(params, loc_cid, rem_cid, now)?;
+4006                    self.issue_first_cids(now);
+4007                } else {
+4008                    // Server-only
+4009                    self.spaces[SpaceId::Data].pending.handshake_done = true;
+4010                    self.discard_space(now, SpaceId::Handshake);
+4011                    self.events.push_back(Event::HandshakeConfirmed);
+4012                    trace!("handshake confirmed");
+4013                }
+4014
+4015                self.events.push_back(Event::Connected);
+4016                self.state.move_to_established();
+4017                trace!("established");
+4018
+4019                // Multipath can only be enabled after the state has reached Established.
+4020                // So this can not happen any earlier.
+4021                self.issue_first_path_cids(now);
+4022                Ok(())
+4023            }
+4024            Header::Initial(InitialHeader {
+4025                src_cid: rem_cid,
+4026                dst_cid: loc_cid,
+4027                ..
+4028            }) => {
+4029                debug_assert_eq!(path_id, PathId::ZERO);
+4030                if !state.rem_cid_set {
+4031                    trace!("switching remote CID to {}", rem_cid);
+4032                    let mut state = state.clone();
+4033                    self.rem_cids
+4034                        .get_mut(&path_id)
+4035                        .expect("PathId::ZERO not yet abandoned")
+4036                        .update_initial_cid(rem_cid);
+4037                    self.rem_handshake_cid = rem_cid;
+4038                    self.orig_rem_cid = rem_cid;
+4039                    state.rem_cid_set = true;
+4040                    self.state.move_to_handshake(state);
+4041                } else if rem_cid != self.rem_handshake_cid {
+4042                    debug!(
+4043                        "discarding packet with mismatched remote CID: {} != {}",
+4044                        self.rem_handshake_cid, rem_cid
+4045                    );
+4046                    return Ok(());
+4047                }
+4048
+4049                let starting_space = self.highest_space;
+4050                self.process_early_payload(now, path_id, packet, qlog)?;
+4051
+4052                if self.side.is_server()
+4053                    && starting_space == SpaceId::Initial
+4054                    && self.highest_space != SpaceId::Initial
+4055                {
+4056                    let params = self.crypto.transport_parameters()?.ok_or_else(|| {
+4057                        TransportError::new(
+4058                            TransportErrorCode::crypto(0x6d),
+4059                            "transport parameters missing".to_owned(),
+4060                        )
+4061                    })?;
+4062                    self.handle_peer_params(params, loc_cid, rem_cid, now)?;
+4063                    self.issue_first_cids(now);
+4064                    self.init_0rtt(now);
+4065                }
+4066                Ok(())
+4067            }
+4068            Header::Long {
+4069                ty: LongType::ZeroRtt,
+4070                ..
+4071            } => {
+4072                self.process_payload(now, remote, path_id, number.unwrap(), packet, qlog)?;
+4073                Ok(())
+4074            }
+4075            Header::VersionNegotiate { .. } => {
+4076                if self.total_authed_packets > 1 {
+4077                    return Ok(());
+4078                }
+4079                let supported = packet
+4080                    .payload
+4081                    .chunks(4)
+4082                    .any(|x| match <[u8; 4]>::try_from(x) {
+4083                        Ok(version) => self.version == u32::from_be_bytes(version),
+4084                        Err(_) => false,
+4085                    });
+4086                if supported {
+4087                    return Ok(());
+4088                }
+4089                debug!("remote doesn't support our version");
+4090                Err(ConnectionError::VersionMismatch)
+4091            }
+4092            Header::Short { .. } => unreachable!(
+4093                "short packets received during handshake are discarded in handle_packet"
+4094            ),
+4095        }
+4096    }
+4097
+4098    /// Process an Initial or Handshake packet payload
+4099    fn process_early_payload(
+4100        &mut self,
+4101        now: Instant,
+4102        path_id: PathId,
+4103        packet: Packet,
+4104        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4105    ) -> Result<(), TransportError> {
+4106        debug_assert_ne!(packet.header.space(), SpaceId::Data);
+4107        debug_assert_eq!(path_id, PathId::ZERO);
+4108        let payload_len = packet.payload.len();
+4109        let mut ack_eliciting = false;
+4110        for result in frame::Iter::new(packet.payload.freeze())? {
+4111            let frame = result?;
+4112            qlog.frame(&frame);
+4113            let span = match frame {
+4114                Frame::Padding => continue,
+4115                _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)),
+4116            };
+4117
+4118            self.stats.frame_rx.record(&frame);
+4119
+4120            let _guard = span.as_ref().map(|x| x.enter());
+4121            ack_eliciting |= frame.is_ack_eliciting();
+4122
+4123            // Process frames
+4124            if frame.is_1rtt() && packet.header.space() != SpaceId::Data {
+4125                return Err(TransportError::PROTOCOL_VIOLATION(
+4126                    "illegal frame type in handshake",
+4127                ));
+4128            }
+4129
+4130            match frame {
+4131                Frame::Padding | Frame::Ping => {}
+4132                Frame::Crypto(frame) => {
+4133                    self.read_crypto(packet.header.space(), &frame, payload_len)?;
+4134                }
+4135                Frame::Ack(ack) => {
+4136                    self.on_ack_received(now, packet.header.space(), ack)?;
+4137                }
+4138                Frame::PathAck(ack) => {
+4139                    span.as_ref()
+4140                        .map(|span| span.record("path", tracing::field::debug(&ack.path_id)));
+4141                    self.on_path_ack_received(now, packet.header.space(), ack)?;
+4142                }
+4143                Frame::Close(reason) => {
+4144                    self.state.move_to_draining(Some(reason.into()));
+4145                    return Ok(());
+4146                }
+4147                _ => {
+4148                    let mut err =
+4149                        TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake");
+4150                    err.frame = Some(frame.ty());
+4151                    return Err(err);
+4152                }
+4153            }
+4154        }
+4155
+4156        if ack_eliciting {
+4157            // In the initial and handshake spaces, ACKs must be sent immediately
+4158            self.spaces[packet.header.space()]
+4159                .for_path(path_id)
+4160                .pending_acks
+4161                .set_immediate_ack_required();
+4162        }
+4163
+4164        self.write_crypto();
+4165        Ok(())
+4166    }
+4167
+4168    /// Processes the packet payload, always in the data space.
+4169    fn process_payload(
+4170        &mut self,
+4171        now: Instant,
+4172        remote: SocketAddr,
+4173        path_id: PathId,
+4174        number: u64,
+4175        packet: Packet,
+4176        #[allow(unused)] qlog: &mut QlogRecvPacket,
+4177    ) -> Result<(), TransportError> {
+4178        let payload = packet.payload.freeze();
+4179        let mut is_probing_packet = true;
+4180        let mut close = None;
+4181        let payload_len = payload.len();
+4182        let mut ack_eliciting = false;
+4183        // if this packet triggers a path migration and includes a observed address frame, it's
+4184        // stored here
+4185        let mut migration_observed_addr = None;
+4186        for result in frame::Iter::new(payload)? {
+4187            let frame = result?;
+4188            qlog.frame(&frame);
+4189            let span = match frame {
+4190                Frame::Padding => continue,
+4191                _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty),
+4192            };
+4193
+4194            self.stats.frame_rx.record(&frame);
+4195            // Crypto, Stream and Datagram frames are special cased in order no pollute
+4196            // the log with payload data
+4197            match &frame {
+4198                Frame::Crypto(f) => {
+4199                    trace!(offset = f.offset, len = f.data.len(), "got crypto frame");
+4200                }
+4201                Frame::Stream(f) => {
+4202                    trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame");
+4203                }
+4204                Frame::Datagram(f) => {
+4205                    trace!(len = f.data.len(), "got datagram frame");
+4206                }
+4207                f => {
+4208                    trace!("got frame {:?}", f);
+4209                }
+4210            }
+4211
+4212            let _guard = span.enter();
+4213            if packet.header.is_0rtt() {
+4214                match frame {
+4215                    Frame::Crypto(_) | Frame::Close(Close::Application(_)) => {
+4216                        return Err(TransportError::PROTOCOL_VIOLATION(
+4217                            "illegal frame type in 0-RTT",
+4218                        ));
+4219                    }
+4220                    _ => {
+4221                        if frame.is_1rtt() {
+4222                            return Err(TransportError::PROTOCOL_VIOLATION(
+4223                                "illegal frame type in 0-RTT",
+4224                            ));
+4225                        }
+4226                    }
+4227                }
+4228            }
+4229            ack_eliciting |= frame.is_ack_eliciting();
+4230
+4231            // Check whether this could be a probing packet
+4232            match frame {
+4233                Frame::Padding
+4234                | Frame::PathChallenge(_)
+4235                | Frame::PathResponse(_)
+4236                | Frame::NewConnectionId(_)
+4237                | Frame::ObservedAddr(_) => {}
+4238                _ => {
+4239                    is_probing_packet = false;
+4240                }
+4241            }
+4242
+4243            match frame {
+4244                Frame::Crypto(frame) => {
+4245                    self.read_crypto(SpaceId::Data, &frame, payload_len)?;
+4246                }
+4247                Frame::Stream(frame) => {
+4248                    if self.streams.received(frame, payload_len)?.should_transmit() {
+4249                        self.spaces[SpaceId::Data].pending.max_data = true;
+4250                    }
+4251                }
+4252                Frame::Ack(ack) => {
+4253                    self.on_ack_received(now, SpaceId::Data, ack)?;
+4254                }
+4255                Frame::PathAck(ack) => {
+4256                    span.record("path", tracing::field::debug(&ack.path_id));
+4257                    self.on_path_ack_received(now, SpaceId::Data, ack)?;
+4258                }
+4259                Frame::Padding | Frame::Ping => {}
+4260                Frame::Close(reason) => {
+4261                    close = Some(reason);
+4262                }
+4263                Frame::PathChallenge(challenge) => {
+4264                    let path = &mut self
+4265                        .path_mut(path_id)
+4266                        .expect("payload is processed only after the path becomes known");
+4267                    path.path_responses.push(number, challenge.0, remote);
+4268                    if remote == path.remote {
+4269                        // PATH_CHALLENGE on active path, possible off-path packet forwarding
+4270                        // attack. Send a non-probing packet to recover the active path.
+4271                        // TODO(flub): No longer true! We now path_challege also to validate
+4272                        //    the path if the path is new, without an RFC9000-style
+4273                        //    migration involved. This means we add in an extra
+4274                        //    IMMEDIATE_ACK on some challenges. It isn't really wrong to do
+4275                        //    so, but it still is something untidy. We should instead
+4276                        //    suppress this when we know the remote is still validating the
+4277                        //    path.
+4278                        match self.peer_supports_ack_frequency() {
+4279                            true => self.immediate_ack(path_id),
+4280                            false => {
+4281                                self.ping_path(path_id).ok();
+4282                            }
+4283                        }
+4284                    }
+4285                }
+4286                Frame::PathResponse(response) => {
+4287                    let path = self
+4288                        .paths
+4289                        .get_mut(&path_id)
+4290                        .expect("payload is processed only after the path becomes known");
+4291
+4292                    match path.data.challenges_sent.get(&response.0) {
+4293                        // Response to an on-path PathChallenge
+4294                        Some(info) if info.remote == remote && path.data.remote == remote => {
+4295                            let sent_instant = info.sent_instant;
+4296                            // TODO(@divma): reset timers using the remaining off-path challenges
+4297                            self.timers.stop(
+4298                                Timer::PerPath(path_id, PathTimer::PathValidation),
+4299                                self.qlog.with_time(now),
+4300                            );
+4301                            self.timers.stop(
+4302                                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+4303                                self.qlog.with_time(now),
+4304                            );
+4305                            if !path.data.validated {
+4306                                trace!("new path validated");
+4307                            }
+4308                            self.timers.stop(
+4309                                Timer::PerPath(path_id, PathTimer::PathOpen),
+4310                                self.qlog.with_time(now),
+4311                            );
+4312                            // Clear any other on-path sent challenge.
+4313                            path.data
+4314                                .challenges_sent
+4315                                .retain(|_token, info| info.remote != remote);
+4316                            path.data.send_new_challenge = false;
+4317                            path.data.validated = true;
+4318
+4319                            // This RTT can only be used for the initial RTT, not as a normal
+4320                            // sample: https://www.rfc-editor.org/rfc/rfc9002#section-6.2.2-2.
+4321                            let rtt = now.saturating_duration_since(sent_instant);
+4322                            path.data.rtt.reset_initial_rtt(rtt);
+4323
+4324                            self.events
+4325                                .push_back(Event::Path(PathEvent::Opened { id: path_id }));
+4326                            // mark the path as open from the application perspective now that Opened
+4327                            // event has been queued
+4328                            if !std::mem::replace(&mut path.data.open, true) {
+4329                                trace!("path opened");
+4330                                if let Some(observed) = path.data.last_observed_addr_report.as_ref()
+4331                                {
+4332                                    self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4333                                        id: path_id,
+4334                                        addr: observed.socket_addr(),
+4335                                    }));
+4336                                }
+4337                            }
+4338                            if let Some((_, ref mut prev)) = path.prev {
+4339                                prev.challenges_sent.clear();
+4340                                prev.send_new_challenge = false;
+4341                            }
+4342                        }
+4343                        // Response to an off-path PathChallenge
+4344                        Some(info) if info.remote == remote => {
+4345                            debug!("Response to off-path PathChallenge!");
+4346                            path.data
+4347                                .challenges_sent
+4348                                .retain(|_token, info| info.remote != remote);
+4349                        }
+4350                        // Response to a PathChallenge we recognize, but from an invalid remote
+4351                        Some(info) => {
+4352                            debug!(%response, from=%remote, expected=%info.remote, "ignoring invalid PATH_RESPONSE")
+4353                        }
+4354                        // Response to an unknown PathChallenge
+4355                        None => debug!(%response, "ignoring invalid PATH_RESPONSE"),
+4356                    }
+4357                }
+4358                Frame::MaxData(bytes) => {
+4359                    self.streams.received_max_data(bytes);
+4360                }
+4361                Frame::MaxStreamData { id, offset } => {
+4362                    self.streams.received_max_stream_data(id, offset)?;
+4363                }
+4364                Frame::MaxStreams { dir, count } => {
+4365                    self.streams.received_max_streams(dir, count)?;
+4366                }
+4367                Frame::ResetStream(frame) => {
+4368                    if self.streams.received_reset(frame)?.should_transmit() {
+4369                        self.spaces[SpaceId::Data].pending.max_data = true;
+4370                    }
+4371                }
+4372                Frame::DataBlocked { offset } => {
+4373                    debug!(offset, "peer claims to be blocked at connection level");
+4374                }
+4375                Frame::StreamDataBlocked { id, offset } => {
+4376                    if id.initiator() == self.side.side() && id.dir() == Dir::Uni {
+4377                        debug!("got STREAM_DATA_BLOCKED on send-only {}", id);
+4378                        return Err(TransportError::STREAM_STATE_ERROR(
+4379                            "STREAM_DATA_BLOCKED on send-only stream",
+4380                        ));
+4381                    }
+4382                    debug!(
+4383                        stream = %id,
+4384                        offset, "peer claims to be blocked at stream level"
+4385                    );
+4386                }
+4387                Frame::StreamsBlocked { dir, limit } => {
+4388                    if limit > MAX_STREAM_COUNT {
+4389                        return Err(TransportError::FRAME_ENCODING_ERROR(
+4390                            "unrepresentable stream limit",
+4391                        ));
+4392                    }
+4393                    debug!(
+4394                        "peer claims to be blocked opening more than {} {} streams",
+4395                        limit, dir
+4396                    );
+4397                }
+4398                Frame::StopSending(frame::StopSending { id, error_code }) => {
+4399                    if id.initiator() != self.side.side() {
+4400                        if id.dir() == Dir::Uni {
+4401                            debug!("got STOP_SENDING on recv-only {}", id);
+4402                            return Err(TransportError::STREAM_STATE_ERROR(
+4403                                "STOP_SENDING on recv-only stream",
+4404                            ));
+4405                        }
+4406                    } else if self.streams.is_local_unopened(id) {
+4407                        return Err(TransportError::STREAM_STATE_ERROR(
+4408                            "STOP_SENDING on unopened stream",
+4409                        ));
+4410                    }
+4411                    self.streams.received_stop_sending(id, error_code);
+4412                }
+4413                Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => {
+4414                    if let Some(ref path_id) = path_id {
+4415                        span.record("path", tracing::field::debug(&path_id));
+4416                    }
+4417                    let path_id = path_id.unwrap_or_default();
+4418                    match self.local_cid_state.get_mut(&path_id) {
+4419                        None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"),
+4420                        Some(cid_state) => {
+4421                            let allow_more_cids = cid_state
+4422                                .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?;
+4423
+4424                            // If the path has closed, we do not issue more CIDs for this path
+4425                            // For details see  https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2
+4426                            // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames.
+4427                            let has_path = !self.abandoned_paths.contains(&path_id);
+4428                            let allow_more_cids = allow_more_cids && has_path;
+4429
+4430                            self.endpoint_events
+4431                                .push_back(EndpointEventInner::RetireConnectionId(
+4432                                    now,
+4433                                    path_id,
+4434                                    sequence,
+4435                                    allow_more_cids,
+4436                                ));
+4437                        }
+4438                    }
+4439                }
+4440                Frame::NewConnectionId(frame) => {
+4441                    let path_id = if let Some(path_id) = frame.path_id {
+4442                        if !self.is_multipath_negotiated() {
+4443                            return Err(TransportError::PROTOCOL_VIOLATION(
+4444                                "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated",
+4445                            ));
+4446                        }
+4447                        if path_id > self.local_max_path_id {
+4448                            return Err(TransportError::PROTOCOL_VIOLATION(
+4449                                "PATH_NEW_CONNECTION_ID contains path_id exceeding current max",
+4450                            ));
+4451                        }
+4452                        path_id
+4453                    } else {
+4454                        PathId::ZERO
+4455                    };
+4456
+4457                    if self.abandoned_paths.contains(&path_id) {
+4458                        trace!("ignoring issued CID for abandoned path");
+4459                        continue;
+4460                    }
+4461                    if let Some(ref path_id) = frame.path_id {
+4462                        span.record("path", tracing::field::debug(&path_id));
+4463                    }
+4464                    let rem_cids = self
+4465                        .rem_cids
+4466                        .entry(path_id)
+4467                        .or_insert_with(|| CidQueue::new(frame.id));
+4468                    if rem_cids.active().is_empty() {
+4469                        // TODO(@divma): is the entry removed later? (rem_cids.entry)
+4470                        return Err(TransportError::PROTOCOL_VIOLATION(
+4471                            "NEW_CONNECTION_ID when CIDs aren't in use",
+4472                        ));
+4473                    }
+4474                    if frame.retire_prior_to > frame.sequence {
+4475                        return Err(TransportError::PROTOCOL_VIOLATION(
+4476                            "NEW_CONNECTION_ID retiring unissued CIDs",
+4477                        ));
+4478                    }
+4479
+4480                    use crate::cid_queue::InsertError;
+4481                    match rem_cids.insert(frame) {
+4482                        Ok(None) => {}
+4483                        Ok(Some((retired, reset_token))) => {
+4484                            let pending_retired =
+4485                                &mut self.spaces[SpaceId::Data].pending.retire_cids;
+4486                            /// Ensure `pending_retired` cannot grow without bound. Limit is
+4487                            /// somewhat arbitrary but very permissive.
+4488                            const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10;
+4489                            // We don't bother counting in-flight frames because those are bounded
+4490                            // by congestion control.
+4491                            if (pending_retired.len() as u64)
+4492                                .saturating_add(retired.end.saturating_sub(retired.start))
+4493                                > MAX_PENDING_RETIRED_CIDS
+4494                            {
+4495                                return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(
+4496                                    "queued too many retired CIDs",
+4497                                ));
+4498                            }
+4499                            pending_retired.extend(retired.map(|seq| (path_id, seq)));
+4500                            self.set_reset_token(path_id, remote, reset_token);
+4501                        }
+4502                        Err(InsertError::ExceedsLimit) => {
+4503                            return Err(TransportError::CONNECTION_ID_LIMIT_ERROR(""));
+4504                        }
+4505                        Err(InsertError::Retired) => {
+4506                            trace!("discarding already-retired");
+4507                            // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a
+4508                            // range of connection IDs larger than the active connection ID limit
+4509                            // was retired all at once via retire_prior_to.
+4510                            self.spaces[SpaceId::Data]
+4511                                .pending
+4512                                .retire_cids
+4513                                .push((path_id, frame.sequence));
+4514                            continue;
+4515                        }
+4516                    };
+4517
+4518                    if self.side.is_server()
+4519                        && path_id == PathId::ZERO
+4520                        && self
+4521                            .rem_cids
+4522                            .get(&PathId::ZERO)
+4523                            .map(|cids| cids.active_seq() == 0)
+4524                            .unwrap_or_default()
+4525                    {
+4526                        // We're a server still using the initial remote CID for the client, so
+4527                        // let's switch immediately to enable clientside stateless resets.
+4528                        self.update_rem_cid(PathId::ZERO);
+4529                    }
+4530                }
+4531                Frame::NewToken(NewToken { token }) => {
+4532                    let ConnectionSide::Client {
+4533                        token_store,
+4534                        server_name,
+4535                        ..
+4536                    } = &self.side
+4537                    else {
+4538                        return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN"));
+4539                    };
+4540                    if token.is_empty() {
+4541                        return Err(TransportError::FRAME_ENCODING_ERROR("empty token"));
+4542                    }
+4543                    trace!("got new token");
+4544                    token_store.insert(server_name, token);
+4545                }
+4546                Frame::Datagram(datagram) => {
+4547                    if self
+4548                        .datagrams
+4549                        .received(datagram, &self.config.datagram_receive_buffer_size)?
+4550                    {
+4551                        self.events.push_back(Event::DatagramReceived);
+4552                    }
+4553                }
+4554                Frame::AckFrequency(ack_frequency) => {
+4555                    // This frame can only be sent in the Data space
+4556
+4557                    if !self.ack_frequency.ack_frequency_received(&ack_frequency)? {
+4558                        // The AckFrequency frame is stale (we have already received a more
+4559                        // recent one)
+4560                        continue;
+4561                    }
+4562
+4563                    // Update the params for all of our paths
+4564                    for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() {
+4565                        space.pending_acks.set_ack_frequency_params(&ack_frequency);
+4566
+4567                        // Our `max_ack_delay` has been updated, so we may need to adjust
+4568                        // its associated timeout
+4569                        if let Some(timeout) = space
+4570                            .pending_acks
+4571                            .max_ack_delay_timeout(self.ack_frequency.max_ack_delay)
+4572                        {
+4573                            self.timers.set(
+4574                                Timer::PerPath(*path_id, PathTimer::MaxAckDelay),
+4575                                timeout,
+4576                                self.qlog.with_time(now),
+4577                            );
+4578                        }
+4579                    }
+4580                }
+4581                Frame::ImmediateAck => {
+4582                    // This frame can only be sent in the Data space
+4583                    for pns in self.spaces[SpaceId::Data].iter_paths_mut() {
+4584                        pns.pending_acks.set_immediate_ack_required();
+4585                    }
+4586                }
+4587                Frame::HandshakeDone => {
+4588                    if self.side.is_server() {
+4589                        return Err(TransportError::PROTOCOL_VIOLATION(
+4590                            "client sent HANDSHAKE_DONE",
+4591                        ));
+4592                    }
+4593                    if self.spaces[SpaceId::Handshake].crypto.is_some() {
+4594                        self.discard_space(now, SpaceId::Handshake);
+4595                    }
+4596                    self.events.push_back(Event::HandshakeConfirmed);
+4597                    trace!("handshake confirmed");
+4598                }
+4599                Frame::ObservedAddr(observed) => {
+4600                    // check if params allows the peer to send report and this node to receive it
+4601                    trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port);
+4602                    if !self
+4603                        .peer_params
+4604                        .address_discovery_role
+4605                        .should_report(&self.config.address_discovery_role)
+4606                    {
+4607                        return Err(TransportError::PROTOCOL_VIOLATION(
+4608                            "received OBSERVED_ADDRESS frame when not negotiated",
+4609                        ));
+4610                    }
+4611                    // must only be sent in data space
+4612                    if packet.header.space() != SpaceId::Data {
+4613                        return Err(TransportError::PROTOCOL_VIOLATION(
+4614                            "OBSERVED_ADDRESS frame outside data space",
+4615                        ));
+4616                    }
+4617
+4618                    let path = self.path_data_mut(path_id);
+4619                    if remote == path.remote {
+4620                        if let Some(updated) = path.update_observed_addr_report(observed) {
+4621                            if path.open {
+4622                                self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4623                                    id: path_id,
+4624                                    addr: updated,
+4625                                }));
+4626                            }
+4627                            // otherwise the event is reported when the path is deemed open
+4628                        }
+4629                    } else {
+4630                        // include in migration
+4631                        migration_observed_addr = Some(observed)
+4632                    }
+4633                }
+4634                Frame::PathAbandon(frame::PathAbandon {
+4635                    path_id,
+4636                    error_code,
+4637                }) => {
+4638                    span.record("path", tracing::field::debug(&path_id));
+4639                    // TODO(flub): don't really know which error code to use here.
+4640                    let already_abandoned = match self.close_path(now, path_id, error_code.into()) {
+4641                        Ok(()) => {
+4642                            trace!("peer abandoned path");
+4643                            false
+4644                        }
+4645                        Err(ClosePathError::LastOpenPath) => {
+4646                            trace!("peer abandoned last path, closing connection");
+4647                            // TODO(flub): which error code?
+4648                            return Err(TransportError::NO_ERROR("last path abandoned by peer"));
+4649                        }
+4650                        Err(ClosePathError::ClosedPath) => {
+4651                            trace!("peer abandoned already closed path");
+4652                            true
+4653                        }
+4654                    };
+4655                    // If we receive a retransmit of PATH_ABANDON then we may already have
+4656                    // abandoned this path locally.  In that case the DiscardPath timer
+4657                    // may already have fired and we no longer have any state for this path.
+4658                    // Only set this timer if we still have path state.
+4659                    if self.path(path_id).is_some() && !already_abandoned {
+4660                        // TODO(flub): Checking is_some() here followed by a number of calls
+4661                        //    that would panic if it was None is really ugly.  If only we
+4662                        //    could do something like PathData::pto().  One day we'll have
+4663                        //    unified SpaceId and PathId and this will be possible.
+4664                        let delay = self.pto(SpaceId::Data, path_id) * 3;
+4665                        self.timers.set(
+4666                            Timer::PerPath(path_id, PathTimer::DiscardPath),
+4667                            now + delay,
+4668                            self.qlog.with_time(now),
+4669                        );
+4670                    }
+4671                }
+4672                Frame::PathStatusAvailable(info) => {
+4673                    span.record("path", tracing::field::debug(&info.path_id));
+4674                    if self.is_multipath_negotiated() {
+4675                        self.on_path_status(
+4676                            info.path_id,
+4677                            PathStatus::Available,
+4678                            info.status_seq_no,
+4679                        );
+4680                    } else {
+4681                        return Err(TransportError::PROTOCOL_VIOLATION(
+4682                            "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated",
+4683                        ));
+4684                    }
+4685                }
+4686                Frame::PathStatusBackup(info) => {
+4687                    span.record("path", tracing::field::debug(&info.path_id));
+4688                    if self.is_multipath_negotiated() {
+4689                        self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no);
+4690                    } else {
+4691                        return Err(TransportError::PROTOCOL_VIOLATION(
+4692                            "received PATH_STATUS_BACKUP frame when multipath was not negotiated",
+4693                        ));
+4694                    }
+4695                }
+4696                Frame::MaxPathId(frame::MaxPathId(path_id)) => {
+4697                    span.record("path", tracing::field::debug(&path_id));
+4698                    if !self.is_multipath_negotiated() {
+4699                        return Err(TransportError::PROTOCOL_VIOLATION(
+4700                            "received MAX_PATH_ID frame when multipath was not negotiated",
+4701                        ));
+4702                    }
+4703                    // frames that do not increase the path id are ignored
+4704                    if path_id > self.remote_max_path_id {
+4705                        self.remote_max_path_id = path_id;
+4706                        self.issue_first_path_cids(now);
+4707                    }
+4708                }
+4709                Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => {
+4710                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+4711                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+4712                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+4713                    if self.is_multipath_negotiated() {
+4714                        if self.local_max_path_id > max_path_id {
+4715                            return Err(TransportError::PROTOCOL_VIOLATION(
+4716                                "PATHS_BLOCKED maximum path identifier was larger than local maximum",
+4717                            ));
+4718                        }
+4719                        debug!("received PATHS_BLOCKED({:?})", max_path_id);
+4720                        // TODO(@divma): ensure max concurrent paths
+4721                    } else {
+4722                        return Err(TransportError::PROTOCOL_VIOLATION(
+4723                            "received PATHS_BLOCKED frame when not multipath was not negotiated",
+4724                        ));
+4725                    }
+4726                }
+4727                Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => {
+4728                    // Nothing to do.  This is recorded in the frame stats, but otherwise we
+4729                    // always issue all CIDs we're allowed to issue, so either this is an
+4730                    // impatient peer or a bug on our side.
+4731
+4732                    // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST
+4733                    // be treated as a connection error of type PROTOCOL_VIOLATION.
+4734                    // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids>
+4735                    if self.is_multipath_negotiated() {
+4736                        if path_id > self.local_max_path_id {
+4737                            return Err(TransportError::PROTOCOL_VIOLATION(
+4738                                "PATH_CIDS_BLOCKED path identifier was larger than local maximum",
+4739                            ));
+4740                        }
+4741                        if next_seq.0
+4742                            > self
+4743                                .local_cid_state
+4744                                .get(&path_id)
+4745                                .map(|cid_state| cid_state.active_seq().1 + 1)
+4746                                .unwrap_or_default()
+4747                        {
+4748                            return Err(TransportError::PROTOCOL_VIOLATION(
+4749                                "PATH_CIDS_BLOCKED next sequence number larger than in local state",
+4750                            ));
+4751                        }
+4752                        debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED");
+4753                    } else {
+4754                        return Err(TransportError::PROTOCOL_VIOLATION(
+4755                            "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated",
+4756                        ));
+4757                    }
+4758                }
+4759                Frame::AddAddress(addr) => {
+4760                    let client_state = match self.iroh_hp.client_side_mut() {
+4761                        Ok(state) => state,
+4762                        Err(err) => {
+4763                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4764                                "Nat traversal(ADD_ADDRESS): {err}"
+4765                            )));
+4766                        }
+4767                    };
+4768
+4769                    if !client_state.check_remote_address(&addr) {
+4770                        // if the address is not valid we flag it, but update anyway
+4771                        warn!(?addr, "server sent illegal ADD_ADDRESS frame");
+4772                    }
+4773
+4774                    match client_state.add_remote_address(addr) {
+4775                        Ok(maybe_added) => {
+4776                            if let Some(added) = maybe_added {
+4777                                self.events.push_back(Event::NatTraversal(
+4778                                    iroh_hp::Event::AddressAdded(added),
+4779                                ));
+4780                            }
+4781                        }
+4782                        Err(e) => {
+4783                            warn!(%e, "failed to add remote address")
+4784                        }
+4785                    }
+4786                }
+4787                Frame::RemoveAddress(addr) => {
+4788                    let client_state = match self.iroh_hp.client_side_mut() {
+4789                        Ok(state) => state,
+4790                        Err(err) => {
+4791                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4792                                "Nat traversal(REMOVE_ADDRESS): {err}"
+4793                            )));
+4794                        }
+4795                    };
+4796                    if let Some(removed_addr) = client_state.remove_remote_address(addr) {
+4797                        self.events
+4798                            .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved(
+4799                                removed_addr,
+4800                            )));
+4801                    }
+4802                }
+4803                Frame::ReachOut(reach_out) => {
+4804                    let server_state = match self.iroh_hp.server_side_mut() {
+4805                        Ok(state) => state,
+4806                        Err(err) => {
+4807                            return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4808                                "Nat traversal(REACH_OUT): {err}"
+4809                            )));
+4810                        }
+4811                    };
+4812
+4813                    if let Err(err) = server_state.handle_reach_out(reach_out) {
+4814                        return Err(TransportError::PROTOCOL_VIOLATION(format!(
+4815                            "Nat traversal(REACH_OUT): {err}"
+4816                        )));
+4817                    }
+4818                }
+4819            }
+4820        }
+4821
+4822        let space = self.spaces[SpaceId::Data].for_path(path_id);
+4823        if space
+4824            .pending_acks
+4825            .packet_received(now, number, ack_eliciting, &space.dedup)
+4826        {
+4827            if self.abandoned_paths.contains(&path_id) {
+4828                // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from
+4829                // abandoned paths.
+4830                space.pending_acks.set_immediate_ack_required();
+4831            } else {
+4832                self.timers.set(
+4833                    Timer::PerPath(path_id, PathTimer::MaxAckDelay),
+4834                    now + self.ack_frequency.max_ack_delay,
+4835                    self.qlog.with_time(now),
+4836                );
+4837            }
+4838        }
+4839
+4840        // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets
+4841        // on stopped streams. Incoming finishes/resets on open streams are not handled here as they
+4842        // are only freed, and hence only issue credit, once the application has been notified
+4843        // during a read on the stream.
+4844        let pending = &mut self.spaces[SpaceId::Data].pending;
+4845        self.streams.queue_max_stream_id(pending);
+4846
+4847        if let Some(reason) = close {
+4848            self.state.move_to_draining(Some(reason.into()));
+4849            self.close = true;
+4850        }
+4851
+4852        if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet
+4853            && !is_probing_packet
+4854            && remote != self.path_data(path_id).remote
+4855        {
+4856            let ConnectionSide::Server { ref server_config } = self.side else {
+4857                panic!("packets from unknown remote should be dropped by clients");
+4858            };
+4859            debug_assert!(
+4860                server_config.migration,
+4861                "migration-initiating packets should have been dropped immediately"
+4862            );
+4863            self.migrate(path_id, now, remote, migration_observed_addr);
+4864            // Break linkability, if possible
+4865            self.update_rem_cid(path_id);
+4866            self.spin = false;
+4867        }
+4868
+4869        Ok(())
+4870    }
+4871
+4872    fn migrate(
+4873        &mut self,
+4874        path_id: PathId,
+4875        now: Instant,
+4876        remote: SocketAddr,
+4877        observed_addr: Option<ObservedAddr>,
+4878    ) {
+4879        trace!(%remote, %path_id, "migration initiated");
+4880        self.path_counter = self.path_counter.wrapping_add(1);
+4881        // TODO(@divma): conditions for path migration in multipath are very specific, check them
+4882        // again to prevent path migrations that should actually create a new path
+4883
+4884        // Reset rtt/congestion state for new path unless it looks like a NAT rebinding.
+4885        // Note that the congestion window will not grow until validation terminates. Helps mitigate
+4886        // amplification attacks performed by spoofing source addresses.
+4887        let prev_pto = self.pto(SpaceId::Data, path_id);
+4888        let known_path = self.paths.get_mut(&path_id).expect("known path");
+4889        let path = &mut known_path.data;
+4890        let mut new_path = if remote.is_ipv4() && remote.ip() == path.remote.ip() {
+4891            PathData::from_previous(remote, path, self.path_counter, now)
+4892        } else {
+4893            let peer_max_udp_payload_size =
+4894                u16::try_from(self.peer_params.max_udp_payload_size.into_inner())
+4895                    .unwrap_or(u16::MAX);
+4896            PathData::new(
+4897                remote,
+4898                self.allow_mtud,
+4899                Some(peer_max_udp_payload_size),
+4900                self.path_counter,
+4901                now,
+4902                &self.config,
+4903            )
+4904        };
+4905        new_path.last_observed_addr_report = path.last_observed_addr_report.clone();
+4906        if let Some(report) = observed_addr {
+4907            if let Some(updated) = new_path.update_observed_addr_report(report) {
+4908                tracing::info!("adding observed addr event from migration");
+4909                self.events.push_back(Event::Path(PathEvent::ObservedAddr {
+4910                    id: path_id,
+4911                    addr: updated,
+4912                }));
+4913            }
+4914        }
+4915        new_path.send_new_challenge = true;
+4916
+4917        let mut prev = mem::replace(path, new_path);
+4918        // Don't clobber the original path if the previous one hasn't been validated yet
+4919        if !prev.is_validating_path() {
+4920            prev.send_new_challenge = true;
+4921            // We haven't updated the remote CID yet, this captures the remote CID we were using on
+4922            // the previous path.
+4923
+4924            known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev));
+4925        }
+4926
+4927        // We need to re-assign the correct remote to this path in qlog
+4928        self.qlog.emit_tuple_assigned(path_id, remote, now);
+4929
+4930        self.timers.set(
+4931            Timer::PerPath(path_id, PathTimer::PathValidation),
+4932            now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto),
+4933            self.qlog.with_time(now),
+4934        );
+4935    }
+4936
+4937    /// Handle a change in the local address, i.e. an active migration
+4938    pub fn local_address_changed(&mut self) {
+4939        // TODO(flub): if multipath is enabled this needs to create a new path entirely.
+4940        self.update_rem_cid(PathId::ZERO);
+4941        self.ping();
+4942    }
+4943
+4944    /// Switch to a previously unused remote connection ID, if possible
+4945    fn update_rem_cid(&mut self, path_id: PathId) {
+4946        let Some((reset_token, retired)) =
+4947            self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next())
+4948        else {
+4949            return;
+4950        };
+4951
+4952        // Retire the current remote CID and any CIDs we had to skip.
+4953        self.spaces[SpaceId::Data]
+4954            .pending
+4955            .retire_cids
+4956            .extend(retired.map(|seq| (path_id, seq)));
+4957        let remote = self.path_data(path_id).remote;
+4958        self.set_reset_token(path_id, remote, reset_token);
+4959    }
+4960
+4961    /// Sends this reset token to the endpoint
+4962    ///
+4963    /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer
+4964    /// sends a reset token it knows to route it to this connection. See RFC 9000 section
+4965    /// 10.3. Stateless Reset.
+4966    ///
+4967    /// Reset tokens are different for each path, the endpoint identifies paths by peer
+4968    /// socket address however, not by path ID.
+4969    fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) {
+4970        self.endpoint_events
+4971            .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token));
+4972
+4973        // During the handshake the server sends a reset token in the transport
+4974        // parameters. When we are the client and we receive the reset token during the
+4975        // handshake we want this to affect our peer transport parameters.
+4976        // TODO(flub): Pretty sure this is pointless, the entire params is overwritten
+4977        //    shortly after this was called.  And then the params don't have this anymore.
+4978        if path_id == PathId::ZERO {
+4979            self.peer_params.stateless_reset_token = Some(reset_token);
+4980        }
+4981    }
+4982
+4983    /// Issue an initial set of connection IDs to the peer upon connection
+4984    fn issue_first_cids(&mut self, now: Instant) {
+4985        if self
+4986            .local_cid_state
+4987            .get(&PathId::ZERO)
+4988            .expect("PathId::ZERO exists when the connection is created")
+4989            .cid_len()
+4990            == 0
+4991        {
+4992            return;
+4993        }
+4994
+4995        // Subtract 1 to account for the CID we supplied while handshaking
+4996        let mut n = self.peer_params.issue_cids_limit() - 1;
+4997        if let ConnectionSide::Server { server_config } = &self.side {
+4998            if server_config.has_preferred_address() {
+4999                // We also sent a CID in the transport parameters
+5000                n -= 1;
+5001            }
+5002        }
+5003        self.endpoint_events
+5004            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+5005    }
+5006
+5007    /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued
+5008    ///
+5009    /// Later CIDs are issued when CIDs expire or are retired by the peer.
+5010    fn issue_first_path_cids(&mut self, now: Instant) {
+5011        if let Some(max_path_id) = self.max_path_id() {
+5012            let mut path_id = self.max_path_id_with_cids.next();
+5013            while path_id <= max_path_id {
+5014                self.endpoint_events
+5015                    .push_back(EndpointEventInner::NeedIdentifiers(
+5016                        path_id,
+5017                        now,
+5018                        self.peer_params.issue_cids_limit(),
+5019                    ));
+5020                path_id = path_id.next();
+5021            }
+5022            self.max_path_id_with_cids = max_path_id;
+5023        }
+5024    }
+5025
+5026    /// Populates a packet with frames
+5027    ///
+5028    /// This tries to fit as many frames as possible into the packet.
+5029    ///
+5030    /// *path_exclusive_only* means to only build frames which can only be sent on this
+5031    /// *path.  This is used in multipath for backup paths while there is still an active
+5032    /// *path.
+5033    fn populate_packet(
+5034        &mut self,
+5035        now: Instant,
+5036        space_id: SpaceId,
+5037        path_id: PathId,
+5038        path_exclusive_only: bool,
+5039        buf: &mut impl BufMut,
+5040        pn: u64,
+5041        #[allow(unused)] qlog: &mut QlogSentPacket,
+5042    ) -> SentFrames {
+5043        let mut sent = SentFrames::default();
+5044        let is_multipath_negotiated = self.is_multipath_negotiated();
+5045        let space = &mut self.spaces[space_id];
+5046        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5047        let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none();
+5048        space
+5049            .for_path(path_id)
+5050            .pending_acks
+5051            .maybe_ack_non_eliciting();
+5052
+5053        // HANDSHAKE_DONE
+5054        if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) {
+5055            trace!("HANDSHAKE_DONE");
+5056            buf.write(frame::FrameType::HANDSHAKE_DONE);
+5057            qlog.frame(&Frame::HandshakeDone);
+5058            sent.retransmits.get_or_create().handshake_done = true;
+5059            // This is just a u8 counter and the frame is typically just sent once
+5060            self.stats.frame_tx.handshake_done =
+5061                self.stats.frame_tx.handshake_done.saturating_add(1);
+5062        }
+5063
+5064        // REACH_OUT
+5065        // TODO(@divma): path explusive considerations
+5066        if let Some((round, addresses)) = space.pending.reach_out.as_mut() {
+5067            while let Some(local_addr) = addresses.pop() {
+5068                let reach_out = frame::ReachOut::new(*round, local_addr);
+5069                if buf.remaining_mut() > reach_out.size() {
+5070                    trace!(%round, ?local_addr, "REACH_OUT");
+5071                    reach_out.write(buf);
+5072                    let sent_reachouts = sent
+5073                        .retransmits
+5074                        .get_or_create()
+5075                        .reach_out
+5076                        .get_or_insert_with(|| (*round, Default::default()));
+5077                    sent_reachouts.1.push(local_addr);
+5078                    self.stats.frame_tx.reach_out = self.stats.frame_tx.reach_out.saturating_add(1);
+5079                    qlog.frame(&Frame::ReachOut(reach_out));
+5080                } else {
+5081                    addresses.push(local_addr);
+5082                    break;
+5083                }
+5084            }
+5085            if addresses.is_empty() {
+5086                space.pending.reach_out = None;
+5087            }
+5088        }
+5089
+5090        // OBSERVED_ADDR
+5091        if !path_exclusive_only
+5092            && space_id == SpaceId::Data
+5093            && self
+5094                .config
+5095                .address_discovery_role
+5096                .should_report(&self.peer_params.address_discovery_role)
+5097            && (!path.observed_addr_sent || space.pending.observed_addr)
+5098        {
+5099            let frame = frame::ObservedAddr::new(path.remote, self.next_observed_addr_seq_no);
+5100            if buf.remaining_mut() > frame.size() {
+5101                trace!(seq = %frame.seq_no, ip = %frame.ip, port = frame.port, "OBSERVED_ADDRESS");
+5102                frame.write(buf);
+5103
+5104                self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8);
+5105                path.observed_addr_sent = true;
+5106
+5107                self.stats.frame_tx.observed_addr += 1;
+5108                sent.retransmits.get_or_create().observed_addr = true;
+5109                space.pending.observed_addr = false;
+5110                qlog.frame(&Frame::ObservedAddr(frame));
+5111            }
+5112        }
+5113
+5114        // PING
+5115        if mem::replace(&mut space.for_path(path_id).ping_pending, false) {
+5116            trace!("PING");
+5117            buf.write(frame::FrameType::PING);
+5118            sent.non_retransmits = true;
+5119            self.stats.frame_tx.ping += 1;
+5120            qlog.frame(&Frame::Ping);
+5121        }
+5122
+5123        // IMMEDIATE_ACK
+5124        if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) {
+5125            debug_assert_eq!(
+5126                space_id,
+5127                SpaceId::Data,
+5128                "immediate acks must be sent in the data space"
+5129            );
+5130            trace!("IMMEDIATE_ACK");
+5131            buf.write(frame::FrameType::IMMEDIATE_ACK);
+5132            sent.non_retransmits = true;
+5133            self.stats.frame_tx.immediate_ack += 1;
+5134            qlog.frame(&Frame::ImmediateAck);
+5135        }
+5136
+5137        // ACK
+5138        // TODO(flub): Should this send acks for this path anyway?
+5139
+5140        if !path_exclusive_only {
+5141            for path_id in space
+5142                .number_spaces
+5143                .iter_mut()
+5144                .filter(|(_, pns)| pns.pending_acks.can_send())
+5145                .map(|(&path_id, _)| path_id)
+5146                .collect::<Vec<_>>()
+5147            {
+5148                Self::populate_acks(
+5149                    now,
+5150                    self.receiving_ecn,
+5151                    &mut sent,
+5152                    path_id,
+5153                    space_id,
+5154                    space,
+5155                    is_multipath_negotiated,
+5156                    buf,
+5157                    &mut self.stats,
+5158                    qlog,
+5159                );
+5160            }
+5161        }
+5162
+5163        // ACK_FREQUENCY
+5164        if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) {
+5165            let sequence_number = self.ack_frequency.next_sequence_number();
+5166
+5167            // Safe to unwrap because this is always provided when ACK frequency is enabled
+5168            let config = self.config.ack_frequency_config.as_ref().unwrap();
+5169
+5170            // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error
+5171            let max_ack_delay = self.ack_frequency.candidate_max_ack_delay(
+5172                path.rtt.get(),
+5173                config,
+5174                &self.peer_params,
+5175            );
+5176
+5177            trace!(?max_ack_delay, "ACK_FREQUENCY");
+5178
+5179            let frame = frame::AckFrequency {
+5180                sequence: sequence_number,
+5181                ack_eliciting_threshold: config.ack_eliciting_threshold,
+5182                request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX),
+5183                reordering_threshold: config.reordering_threshold,
+5184            };
+5185            frame.encode(buf);
+5186            qlog.frame(&Frame::AckFrequency(frame));
+5187
+5188            sent.retransmits.get_or_create().ack_frequency = true;
+5189
+5190            self.ack_frequency
+5191                .ack_frequency_sent(path_id, pn, max_ack_delay);
+5192            self.stats.frame_tx.ack_frequency += 1;
+5193        }
+5194
+5195        // PATH_CHALLENGE
+5196        if buf.remaining_mut() > frame::PathChallenge::SIZE_BOUND
+5197            && space_id == SpaceId::Data
+5198            && path.send_new_challenge
+5199        {
+5200            path.send_new_challenge = false;
+5201
+5202            // Generate a new challenge every time we send a new PATH_CHALLENGE
+5203            let token = self.rng.random();
+5204            let info = paths::SentChallengeInfo {
+5205                sent_instant: now,
+5206                remote: path.remote,
+5207            };
+5208            path.challenges_sent.insert(token, info);
+5209            sent.non_retransmits = true;
+5210            sent.requires_padding = true;
+5211            let challenge = frame::PathChallenge(token);
+5212            trace!(%challenge, "sending new challenge");
+5213            buf.write(challenge);
+5214            qlog.frame(&Frame::PathChallenge(challenge));
+5215            self.stats.frame_tx.path_challenge += 1;
+5216            let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base();
+5217            self.timers.set(
+5218                Timer::PerPath(path_id, PathTimer::PathChallengeLost),
+5219                now + pto,
+5220                self.qlog.with_time(now),
+5221            );
+5222
+5223            if is_multipath_negotiated && !path.validated && path.send_new_challenge {
+5224                // queue informing the path status along with the challenge
+5225                space.pending.path_status.insert(path_id);
+5226            }
+5227
+5228            // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless
+5229            // of whether one has already been sent on this path.
+5230            if space_id == SpaceId::Data
+5231                && self
+5232                    .config
+5233                    .address_discovery_role
+5234                    .should_report(&self.peer_params.address_discovery_role)
+5235            {
+5236                let frame = frame::ObservedAddr::new(path.remote, self.next_observed_addr_seq_no);
+5237                if buf.remaining_mut() > frame.size() {
+5238                    frame.write(buf);
+5239                    qlog.frame(&Frame::ObservedAddr(frame));
+5240
+5241                    self.next_observed_addr_seq_no =
+5242                        self.next_observed_addr_seq_no.saturating_add(1u8);
+5243                    path.observed_addr_sent = true;
+5244
+5245                    self.stats.frame_tx.observed_addr += 1;
+5246                    sent.retransmits.get_or_create().observed_addr = true;
+5247                    space.pending.observed_addr = false;
+5248                }
+5249            }
+5250        }
+5251
+5252        // PATH_RESPONSE
+5253        if buf.remaining_mut() > frame::PathResponse::SIZE_BOUND && space_id == SpaceId::Data {
+5254            if let Some(token) = path.path_responses.pop_on_path(path.remote) {
+5255                sent.non_retransmits = true;
+5256                sent.requires_padding = true;
+5257                let response = frame::PathResponse(token);
+5258                trace!(%response, "sending response");
+5259                buf.write(response);
+5260                qlog.frame(&Frame::PathResponse(response));
+5261                self.stats.frame_tx.path_response += 1;
+5262
+5263                // NOTE: this is technically not required but might be useful to ride the
+5264                // request/response nature of path challenges to refresh an observation
+5265                // Since PATH_RESPONSE is a probing frame, this is allowed by the spec.
+5266                if space_id == SpaceId::Data
+5267                    && self
+5268                        .config
+5269                        .address_discovery_role
+5270                        .should_report(&self.peer_params.address_discovery_role)
+5271                {
+5272                    let frame =
+5273                        frame::ObservedAddr::new(path.remote, self.next_observed_addr_seq_no);
+5274                    if buf.remaining_mut() > frame.size() {
+5275                        frame.write(buf);
+5276                        qlog.frame(&Frame::ObservedAddr(frame));
+5277
+5278                        self.next_observed_addr_seq_no =
+5279                            self.next_observed_addr_seq_no.saturating_add(1u8);
+5280                        path.observed_addr_sent = true;
+5281
+5282                        self.stats.frame_tx.observed_addr += 1;
+5283                        sent.retransmits.get_or_create().observed_addr = true;
+5284                        space.pending.observed_addr = false;
+5285                    }
+5286                }
+5287            }
+5288        }
+5289
+5290        // CRYPTO
+5291        while !path_exclusive_only && buf.remaining_mut() > frame::Crypto::SIZE_BOUND && !is_0rtt {
+5292            let mut frame = match space.pending.crypto.pop_front() {
+5293                Some(x) => x,
+5294                None => break,
+5295            };
+5296
+5297            // Calculate the maximum amount of crypto data we can store in the buffer.
+5298            // Since the offset is known, we can reserve the exact size required to encode it.
+5299            // For length we reserve 2bytes which allows to encode up to 2^14,
+5300            // which is more than what fits into normally sized QUIC frames.
+5301            let max_crypto_data_size = buf.remaining_mut()
+5302                - 1 // Frame Type
+5303                - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) })
+5304                - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes
+5305
+5306            let len = frame
+5307                .data
+5308                .len()
+5309                .min(2usize.pow(14) - 1)
+5310                .min(max_crypto_data_size);
+5311
+5312            let data = frame.data.split_to(len);
+5313            let truncated = frame::Crypto {
+5314                offset: frame.offset,
+5315                data,
+5316            };
+5317            trace!(
+5318                "CRYPTO: off {} len {}",
+5319                truncated.offset,
+5320                truncated.data.len()
+5321            );
+5322            truncated.encode(buf);
+5323            self.stats.frame_tx.crypto += 1;
+5324
+5325            // The clone is cheap but we still cfg it out if qlog is disabled.
+5326            #[cfg(feature = "qlog")]
+5327            qlog.frame(&Frame::Crypto(truncated.clone()));
+5328            sent.retransmits.get_or_create().crypto.push_back(truncated);
+5329            if !frame.data.is_empty() {
+5330                frame.offset += len as u64;
+5331                space.pending.crypto.push_front(frame);
+5332            }
+5333        }
+5334
+5335        // TODO(flub): maybe this is much higher priority?
+5336        // PATH_ABANDON
+5337        while !path_exclusive_only
+5338            && space_id == SpaceId::Data
+5339            && frame::PathAbandon::SIZE_BOUND <= buf.remaining_mut()
+5340        {
+5341            let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else {
+5342                break;
+5343            };
+5344            let frame = frame::PathAbandon {
+5345                path_id,
+5346                error_code,
+5347            };
+5348            frame.encode(buf);
+5349            qlog.frame(&Frame::PathAbandon(frame));
+5350            self.stats.frame_tx.path_abandon += 1;
+5351            trace!(%path_id, "PATH_ABANDON");
+5352            sent.retransmits
+5353                .get_or_create()
+5354                .path_abandon
+5355                .entry(path_id)
+5356                .or_insert(error_code);
+5357        }
+5358
+5359        // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP
+5360        while !path_exclusive_only
+5361            && space_id == SpaceId::Data
+5362            && frame::PathStatusAvailable::SIZE_BOUND <= buf.remaining_mut()
+5363        {
+5364            let Some(path_id) = space.pending.path_status.pop_first() else {
+5365                break;
+5366            };
+5367            let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else {
+5368                trace!(%path_id, "discarding queued path status for unknown path");
+5369                continue;
+5370            };
+5371
+5372            let seq = path.status.seq();
+5373            sent.retransmits.get_or_create().path_status.insert(path_id);
+5374            match path.local_status() {
+5375                PathStatus::Available => {
+5376                    let frame = frame::PathStatusAvailable {
+5377                        path_id,
+5378                        status_seq_no: seq,
+5379                    };
+5380                    frame.encode(buf);
+5381                    qlog.frame(&Frame::PathStatusAvailable(frame));
+5382                    self.stats.frame_tx.path_status_available += 1;
+5383                    trace!(%path_id, %seq, "PATH_STATUS_AVAILABLE")
+5384                }
+5385                PathStatus::Backup => {
+5386                    let frame = frame::PathStatusBackup {
+5387                        path_id,
+5388                        status_seq_no: seq,
+5389                    };
+5390                    frame.encode(buf);
+5391                    qlog.frame(&Frame::PathStatusBackup(frame));
+5392                    self.stats.frame_tx.path_status_backup += 1;
+5393                    trace!(%path_id, %seq, "PATH_STATUS_BACKUP")
+5394                }
+5395            }
+5396        }
+5397
+5398        // MAX_PATH_ID
+5399        if space_id == SpaceId::Data
+5400            && space.pending.max_path_id
+5401            && frame::MaxPathId::SIZE_BOUND <= buf.remaining_mut()
+5402        {
+5403            let frame = frame::MaxPathId(self.local_max_path_id);
+5404            frame.encode(buf);
+5405            qlog.frame(&Frame::MaxPathId(frame));
+5406            space.pending.max_path_id = false;
+5407            sent.retransmits.get_or_create().max_path_id = true;
+5408            trace!(val = %self.local_max_path_id, "MAX_PATH_ID");
+5409            self.stats.frame_tx.max_path_id += 1;
+5410        }
+5411
+5412        // PATHS_BLOCKED
+5413        if space_id == SpaceId::Data
+5414            && space.pending.paths_blocked
+5415            && frame::PathsBlocked::SIZE_BOUND <= buf.remaining_mut()
+5416        {
+5417            let frame = frame::PathsBlocked(self.remote_max_path_id);
+5418            frame.encode(buf);
+5419            qlog.frame(&Frame::PathsBlocked(frame));
+5420            space.pending.paths_blocked = false;
+5421            sent.retransmits.get_or_create().paths_blocked = true;
+5422            trace!(max_path_id = ?self.remote_max_path_id, "PATHS_BLOCKED");
+5423            self.stats.frame_tx.paths_blocked += 1;
+5424        }
+5425
+5426        // PATH_CIDS_BLOCKED
+5427        while space_id == SpaceId::Data && frame::PathCidsBlocked::SIZE_BOUND <= buf.remaining_mut()
+5428        {
+5429            let Some(path_id) = space.pending.path_cids_blocked.pop() else {
+5430                break;
+5431            };
+5432            let next_seq = match self.rem_cids.get(&path_id) {
+5433                Some(cid_queue) => cid_queue.active_seq() + 1,
+5434                None => 0,
+5435            };
+5436            let frame = frame::PathCidsBlocked {
+5437                path_id,
+5438                next_seq: VarInt(next_seq),
+5439            };
+5440            frame.encode(buf);
+5441            qlog.frame(&Frame::PathCidsBlocked(frame));
+5442            sent.retransmits
+5443                .get_or_create()
+5444                .path_cids_blocked
+5445                .push(path_id);
+5446            trace!(%path_id, next_seq, "PATH_CIDS_BLOCKED");
+5447            self.stats.frame_tx.path_cids_blocked += 1;
+5448        }
+5449
+5450        // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS
+5451        if space_id == SpaceId::Data {
+5452            self.streams.write_control_frames(
+5453                buf,
+5454                &mut space.pending,
+5455                &mut sent.retransmits,
+5456                &mut self.stats.frame_tx,
+5457                qlog,
+5458            );
+5459        }
+5460
+5461        // NEW_CONNECTION_ID
+5462        let cid_len = self
+5463            .local_cid_state
+5464            .values()
+5465            .map(|cid_state| cid_state.cid_len())
+5466            .max()
+5467            .expect("some local CID state must exist");
+5468        let new_cid_size_bound =
+5469            frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len);
+5470        while !path_exclusive_only && buf.remaining_mut() > new_cid_size_bound {
+5471            let issued = match space.pending.new_cids.pop() {
+5472                Some(x) => x,
+5473                None => break,
+5474            };
+5475            let retire_prior_to = self
+5476                .local_cid_state
+5477                .get(&issued.path_id)
+5478                .map(|cid_state| cid_state.retire_prior_to())
+5479                .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id));
+5480
+5481            let cid_path_id = match is_multipath_negotiated {
+5482                true => {
+5483                    trace!(
+5484                        path_id = ?issued.path_id,
+5485                        sequence = issued.sequence,
+5486                        id = %issued.id,
+5487                        "PATH_NEW_CONNECTION_ID",
+5488                    );
+5489                    self.stats.frame_tx.path_new_connection_id += 1;
+5490                    Some(issued.path_id)
+5491                }
+5492                false => {
+5493                    trace!(
+5494                        sequence = issued.sequence,
+5495                        id = %issued.id,
+5496                        "NEW_CONNECTION_ID"
+5497                    );
+5498                    debug_assert_eq!(issued.path_id, PathId::ZERO);
+5499                    self.stats.frame_tx.new_connection_id += 1;
+5500                    None
+5501                }
+5502            };
+5503            let frame = frame::NewConnectionId {
+5504                path_id: cid_path_id,
+5505                sequence: issued.sequence,
+5506                retire_prior_to,
+5507                id: issued.id,
+5508                reset_token: issued.reset_token,
+5509            };
+5510            frame.encode(buf);
+5511            sent.retransmits.get_or_create().new_cids.push(issued);
+5512            qlog.frame(&Frame::NewConnectionId(frame));
+5513        }
+5514
+5515        // RETIRE_CONNECTION_ID
+5516        let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated);
+5517        while !path_exclusive_only && buf.remaining_mut() > retire_cid_bound {
+5518            let (path_id, sequence) = match space.pending.retire_cids.pop() {
+5519                Some((PathId::ZERO, seq)) if !is_multipath_negotiated => {
+5520                    trace!(sequence = seq, "RETIRE_CONNECTION_ID");
+5521                    self.stats.frame_tx.retire_connection_id += 1;
+5522                    (None, seq)
+5523                }
+5524                Some((path_id, seq)) => {
+5525                    trace!(%path_id, sequence = seq, "PATH_RETIRE_CONNECTION_ID");
+5526                    self.stats.frame_tx.path_retire_connection_id += 1;
+5527                    (Some(path_id), seq)
+5528                }
+5529                None => break,
+5530            };
+5531            let frame = frame::RetireConnectionId { path_id, sequence };
+5532            frame.encode(buf);
+5533            qlog.frame(&Frame::RetireConnectionId(frame));
+5534            sent.retransmits
+5535                .get_or_create()
+5536                .retire_cids
+5537                .push((path_id.unwrap_or_default(), sequence));
+5538        }
+5539
+5540        // DATAGRAM
+5541        let mut sent_datagrams = false;
+5542        while !path_exclusive_only
+5543            && buf.remaining_mut() > Datagram::SIZE_BOUND
+5544            && space_id == SpaceId::Data
+5545        {
+5546            let prev_remaining = buf.remaining_mut();
+5547            match self.datagrams.write(buf) {
+5548                true => {
+5549                    sent_datagrams = true;
+5550                    sent.non_retransmits = true;
+5551                    self.stats.frame_tx.datagram += 1;
+5552                    qlog.frame_datagram((prev_remaining - buf.remaining_mut()) as u64);
+5553                }
+5554                false => break,
+5555            }
+5556        }
+5557        if self.datagrams.send_blocked && sent_datagrams {
+5558            self.events.push_back(Event::DatagramsUnblocked);
+5559            self.datagrams.send_blocked = false;
+5560        }
+5561
+5562        let path = &mut self.paths.get_mut(&path_id).expect("known path").data;
+5563
+5564        // NEW_TOKEN
+5565        while let Some(remote_addr) = space.pending.new_tokens.pop() {
+5566            if path_exclusive_only {
+5567                break;
+5568            }
+5569            debug_assert_eq!(space_id, SpaceId::Data);
+5570            let ConnectionSide::Server { server_config } = &self.side else {
+5571                panic!("NEW_TOKEN frames should not be enqueued by clients");
+5572            };
+5573
+5574            if remote_addr != path.remote {
+5575                // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only
+5576                // useful if used from the same IP address.  Thus, we abandon enqueued NEW_TOKEN
+5577                // frames upon an path change. Instead, when the new path becomes validated,
+5578                // NEW_TOKEN frames may be enqueued for the new path instead.
+5579                continue;
+5580            }
+5581
+5582            let token = Token::new(
+5583                TokenPayload::Validation {
+5584                    ip: remote_addr.ip(),
+5585                    issued: server_config.time_source.now(),
+5586                },
+5587                &mut self.rng,
+5588            );
+5589            let new_token = NewToken {
+5590                token: token.encode(&*server_config.token_key).into(),
+5591            };
+5592
+5593            if buf.remaining_mut() < new_token.size() {
+5594                space.pending.new_tokens.push(remote_addr);
+5595                break;
+5596            }
+5597
+5598            trace!("NEW_TOKEN");
+5599            new_token.encode(buf);
+5600            qlog.frame(&Frame::NewToken(new_token));
+5601            sent.retransmits
+5602                .get_or_create()
+5603                .new_tokens
+5604                .push(remote_addr);
+5605            self.stats.frame_tx.new_token += 1;
+5606        }
+5607
+5608        // STREAM
+5609        if !path_exclusive_only && space_id == SpaceId::Data {
+5610            sent.stream_frames =
+5611                self.streams
+5612                    .write_stream_frames(buf, self.config.send_fairness, qlog);
+5613            self.stats.frame_tx.stream += sent.stream_frames.len() as u64;
+5614        }
+5615
+5616        // ADD_ADDRESS
+5617        // TODO(@divma): check if we need to do path exclusive filters
+5618        while space_id == SpaceId::Data && frame::AddAddress::SIZE_BOUND <= buf.remaining_mut() {
+5619            if let Some(added_address) = space.pending.add_address.pop_last() {
+5620                trace!(
+5621                    seq = %added_address.seq_no,
+5622                    ip = ?added_address.ip,
+5623                    port = added_address.port,
+5624                    "ADD_ADDRESS",
+5625                );
+5626                added_address.write(buf);
+5627                sent.retransmits
+5628                    .get_or_create()
+5629                    .add_address
+5630                    .insert(added_address);
+5631                self.stats.frame_tx.add_address = self.stats.frame_tx.add_address.saturating_add(1);
+5632                qlog.frame(&Frame::AddAddress(added_address));
+5633            } else {
+5634                break;
+5635            }
+5636        }
+5637
+5638        // REMOVE_ADDRESS
+5639        while space_id == SpaceId::Data && frame::RemoveAddress::SIZE_BOUND <= buf.remaining_mut() {
+5640            if let Some(removed_address) = space.pending.remove_address.pop_last() {
+5641                trace!(seq = %removed_address.seq_no, "REMOVE_ADDRESS");
+5642                removed_address.write(buf);
+5643                sent.retransmits
+5644                    .get_or_create()
+5645                    .remove_address
+5646                    .insert(removed_address);
+5647                self.stats.frame_tx.remove_address =
+5648                    self.stats.frame_tx.remove_address.saturating_add(1);
+5649                qlog.frame(&Frame::RemoveAddress(removed_address));
+5650            } else {
+5651                break;
+5652            }
+5653        }
+5654
+5655        sent
+5656    }
+5657
+5658    /// Write pending ACKs into a buffer
+5659    fn populate_acks(
+5660        now: Instant,
+5661        receiving_ecn: bool,
+5662        sent: &mut SentFrames,
+5663        path_id: PathId,
+5664        space_id: SpaceId,
+5665        space: &mut PacketSpace,
+5666        is_multipath_negotiated: bool,
+5667        buf: &mut impl BufMut,
+5668        stats: &mut ConnectionStats,
+5669        #[allow(unused)] qlog: &mut QlogSentPacket,
+5670    ) {
+5671        // 0-RTT packets must never carry acks (which would have to be of handshake packets)
+5672        debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT");
+5673
+5674        debug_assert!(
+5675            is_multipath_negotiated || path_id == PathId::ZERO,
+5676            "Only PathId::ZERO allowed without multipath (have {path_id:?})"
+5677        );
+5678        if is_multipath_negotiated {
+5679            debug_assert!(
+5680                space_id == SpaceId::Data || path_id == PathId::ZERO,
+5681                "path acks must be sent in 1RTT space (have {space_id:?})"
+5682            );
+5683        }
+5684
+5685        let pns = space.for_path(path_id);
+5686        let ranges = pns.pending_acks.ranges();
+5687        debug_assert!(!ranges.is_empty(), "can not send empty ACK range");
+5688        let ecn = if receiving_ecn {
+5689            Some(&pns.ecn_counters)
+5690        } else {
+5691            None
+5692        };
+5693        if let Some(max) = ranges.max() {
+5694            sent.largest_acked.insert(path_id, max);
+5695        }
+5696
+5697        let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64;
+5698        // TODO: This should come from `TransportConfig` if that gets configurable.
+5699        let ack_delay_exp = TransportParameters::default().ack_delay_exponent;
+5700        let delay = delay_micros >> ack_delay_exp.into_inner();
+5701
+5702        if is_multipath_negotiated && space_id == SpaceId::Data {
+5703            if !ranges.is_empty() {
+5704                trace!("PATH_ACK {path_id:?} {ranges:?}, Delay = {delay_micros}us");
+5705                frame::PathAck::encode(path_id, delay as _, ranges, ecn, buf);
+5706                qlog.frame_path_ack(path_id, delay as _, ranges, ecn);
+5707                stats.frame_tx.path_acks += 1;
+5708            }
+5709        } else {
+5710            trace!("ACK {ranges:?}, Delay = {delay_micros}us");
+5711            frame::Ack::encode(delay as _, ranges, ecn, buf);
+5712            stats.frame_tx.acks += 1;
+5713            qlog.frame_ack(delay, ranges, ecn);
+5714        }
+5715    }
+5716
+5717    fn close_common(&mut self) {
+5718        trace!("connection closed");
+5719        self.timers.reset();
+5720    }
+5721
+5722    fn set_close_timer(&mut self, now: Instant) {
+5723        // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of
+5724        // the PTO for all paths.
+5725        self.timers.set(
+5726            Timer::Conn(ConnTimer::Close),
+5727            now + 3 * self.pto_max_path(self.highest_space),
+5728            self.qlog.with_time(now),
+5729        );
+5730    }
+5731
+5732    /// Handle transport parameters received from the peer
+5733    ///
+5734    /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the
+5735    /// *packet into which the transport parameters arrived.
+5736    fn handle_peer_params(
+5737        &mut self,
+5738        params: TransportParameters,
+5739        loc_cid: ConnectionId,
+5740        rem_cid: ConnectionId,
+5741        now: Instant,
+5742    ) -> Result<(), TransportError> {
+5743        if Some(self.orig_rem_cid) != params.initial_src_cid
+5744            || (self.side.is_client()
+5745                && (Some(self.initial_dst_cid) != params.original_dst_cid
+5746                    || self.retry_src_cid != params.retry_src_cid))
+5747        {
+5748            return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
+5749                "CID authentication failure",
+5750            ));
+5751        }
+5752        if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) {
+5753            return Err(TransportError::PROTOCOL_VIOLATION(
+5754                "multipath must not use zero-length CIDs",
+5755            ));
+5756        }
+5757
+5758        self.set_peer_params(params);
+5759        self.qlog.emit_peer_transport_params_received(self, now);
+5760
+5761        Ok(())
+5762    }
+5763
+5764    fn set_peer_params(&mut self, params: TransportParameters) {
+5765        self.streams.set_params(&params);
+5766        self.idle_timeout =
+5767            negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout));
+5768        trace!("negotiated max idle timeout {:?}", self.idle_timeout);
+5769
+5770        if let Some(ref info) = params.preferred_address {
+5771            // During the handshake PathId::ZERO exists.
+5772            self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId {
+5773                path_id: None,
+5774                sequence: 1,
+5775                id: info.connection_id,
+5776                reset_token: info.stateless_reset_token,
+5777                retire_prior_to: 0,
+5778            })
+5779            .expect(
+5780                "preferred address CID is the first received, and hence is guaranteed to be legal",
+5781            );
+5782            let remote = self.path_data(PathId::ZERO).remote;
+5783            self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token);
+5784        }
+5785        self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params);
+5786
+5787        let mut multipath_enabled = None;
+5788        if let (Some(local_max_path_id), Some(remote_max_path_id)) = (
+5789            self.config.get_initial_max_path_id(),
+5790            params.initial_max_path_id,
+5791        ) {
+5792            // multipath is enabled, register the local and remote maximums
+5793            self.local_max_path_id = local_max_path_id;
+5794            self.remote_max_path_id = remote_max_path_id;
+5795            let initial_max_path_id = local_max_path_id.min(remote_max_path_id);
+5796            debug!(%initial_max_path_id, "multipath negotiated");
+5797            multipath_enabled = Some(initial_max_path_id);
+5798        }
+5799
+5800        if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) =
+5801            self.config
+5802                .max_remote_nat_traversal_addresses
+5803                .zip(params.max_remote_nat_traversal_addresses)
+5804        {
+5805            if let Some(max_initial_paths) =
+5806                multipath_enabled.map(|path_id| path_id.saturating_add(1u8))
+5807            {
+5808                let max_local_addresses = max_remotely_allowed_remote_addresses.get();
+5809                let max_remote_addresses = max_locally_allowed_remote_addresses.get();
+5810                self.iroh_hp =
+5811                    iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side());
+5812                debug!(
+5813                    %max_remote_addresses, %max_local_addresses,
+5814                    "iroh hole punching negotiated"
+5815                );
+5816
+5817                match self.side() {
+5818                    Side::Client => {
+5819                        if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 {
+5820                            // in this case the client might try to open `max_remote_addresses` new
+5821                            // paths, but the current multipath configuration will not allow it
+5822                            warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues")
+5823                        } else if max_local_addresses as u64
+5824                            > params.active_connection_id_limit.into_inner()
+5825                        {
+5826                            // the server allows us to send at most `params.active_connection_id_limit`
+5827                            // but they might need at least `max_local_addresses` to effectively send
+5828                            // `PATH_CHALLENGE` frames to each advertised local address
+5829                            warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues")
+5830                        }
+5831                    }
+5832                    Side::Server => {
+5833                        if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT {
+5834                            warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues")
+5835                        }
+5836                    }
+5837                }
+5838            } else {
+5839                debug!("iroh nat traversal enabled for both endpoints, but multipath is missing")
+5840            }
+5841        }
+5842
+5843        self.peer_params = params;
+5844        let peer_max_udp_payload_size =
+5845            u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX);
+5846        self.path_data_mut(PathId::ZERO)
+5847            .mtud
+5848            .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+5849    }
+5850
+5851    /// Decrypts a packet, returning the packet number on success
+5852    fn decrypt_packet(
+5853        &mut self,
+5854        now: Instant,
+5855        path_id: PathId,
+5856        packet: &mut Packet,
+5857    ) -> Result<Option<u64>, Option<TransportError>> {
+5858        let result = packet_crypto::decrypt_packet_body(
+5859            packet,
+5860            path_id,
+5861            &self.spaces,
+5862            self.zero_rtt_crypto.as_ref(),
+5863            self.key_phase,
+5864            self.prev_crypto.as_ref(),
+5865            self.next_crypto.as_ref(),
+5866        )?;
+5867
+5868        let result = match result {
+5869            Some(r) => r,
+5870            None => return Ok(None),
+5871        };
+5872
+5873        if result.outgoing_key_update_acked {
+5874            if let Some(prev) = self.prev_crypto.as_mut() {
+5875                prev.end_packet = Some((result.number, now));
+5876                self.set_key_discard_timer(now, packet.header.space());
+5877            }
+5878        }
+5879
+5880        if result.incoming_key_update {
+5881            trace!("key update authenticated");
+5882            self.update_keys(Some((result.number, now)), true);
+5883            self.set_key_discard_timer(now, packet.header.space());
+5884        }
+5885
+5886        Ok(Some(result.number))
+5887    }
+5888
+5889    fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) {
+5890        trace!("executing key update");
+5891        // Generate keys for the key phase after the one we're switching to, store them in
+5892        // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into
+5893        // `prev_crypto`.
+5894        let new = self
+5895            .crypto
+5896            .next_1rtt_keys()
+5897            .expect("only called for `Data` packets");
+5898        self.key_phase_size = new
+5899            .local
+5900            .confidentiality_limit()
+5901            .saturating_sub(KEY_UPDATE_MARGIN);
+5902        let old = mem::replace(
+5903            &mut self.spaces[SpaceId::Data]
+5904                .crypto
+5905                .as_mut()
+5906                .unwrap() // safe because update_keys() can only be triggered by short packets
+5907                .packet,
+5908            mem::replace(self.next_crypto.as_mut().unwrap(), new),
+5909        );
+5910        self.spaces[SpaceId::Data]
+5911            .iter_paths_mut()
+5912            .for_each(|s| s.sent_with_keys = 0);
+5913        self.prev_crypto = Some(PrevCrypto {
+5914            crypto: old,
+5915            end_packet,
+5916            update_unacked: remote,
+5917        });
+5918        self.key_phase = !self.key_phase;
+5919    }
+5920
+5921    fn peer_supports_ack_frequency(&self) -> bool {
+5922        self.peer_params.min_ack_delay.is_some()
+5923    }
+5924
+5925    /// Send an IMMEDIATE_ACK frame to the remote endpoint
+5926    ///
+5927    /// According to the spec, this will result in an error if the remote endpoint does not support
+5928    /// the Acknowledgement Frequency extension
+5929    pub(crate) fn immediate_ack(&mut self, path_id: PathId) {
+5930        debug_assert_eq!(
+5931            self.highest_space,
+5932            SpaceId::Data,
+5933            "immediate ack must be written in the data space"
+5934        );
+5935        self.spaces[self.highest_space]
+5936            .for_path(path_id)
+5937            .immediate_ack_pending = true;
+5938    }
+5939
+5940    /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests
+5941    #[cfg(test)]
+5942    pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> {
+5943        let (path_id, first_decode, remaining) = match &event.0 {
+5944            ConnectionEventInner::Datagram(DatagramConnectionEvent {
+5945                path_id,
+5946                first_decode,
+5947                remaining,
+5948                ..
+5949            }) => (path_id, first_decode, remaining),
+5950            _ => return None,
+5951        };
+5952
+5953        if remaining.is_some() {
+5954            panic!("Packets should never be coalesced in tests");
+5955        }
+5956
+5957        let decrypted_header = packet_crypto::unprotect_header(
+5958            first_decode.clone(),
+5959            &self.spaces,
+5960            self.zero_rtt_crypto.as_ref(),
+5961            self.peer_params.stateless_reset_token,
+5962        )?;
+5963
+5964        let mut packet = decrypted_header.packet?;
+5965        packet_crypto::decrypt_packet_body(
+5966            &mut packet,
+5967            *path_id,
+5968            &self.spaces,
+5969            self.zero_rtt_crypto.as_ref(),
+5970            self.key_phase,
+5971            self.prev_crypto.as_ref(),
+5972            self.next_crypto.as_ref(),
+5973        )
+5974        .ok()?;
+5975
+5976        Some(packet.payload.to_vec())
+5977    }
+5978
+5979    /// The number of bytes of packets containing retransmittable frames that have not been
+5980    /// acknowledged or declared lost.
+5981    #[cfg(test)]
+5982    pub(crate) fn bytes_in_flight(&self) -> u64 {
+5983        // TODO(@divma): consider including for multipath?
+5984        self.path_data(PathId::ZERO).in_flight.bytes
+5985    }
+5986
+5987    /// Number of bytes worth of non-ack-only packets that may be sent
+5988    #[cfg(test)]
+5989    pub(crate) fn congestion_window(&self) -> u64 {
+5990        let path = self.path_data(PathId::ZERO);
+5991        path.congestion
+5992            .window()
+5993            .saturating_sub(path.in_flight.bytes)
+5994    }
+5995
+5996    /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running
+5997    #[cfg(test)]
+5998    pub(crate) fn is_idle(&self) -> bool {
+5999        let current_timers = self.timers.values();
+6000        current_timers
+6001            .into_iter()
+6002            .filter(|(timer, _)| {
+6003                !matches!(
+6004                    timer,
+6005                    Timer::Conn(ConnTimer::KeepAlive)
+6006                        | Timer::PerPath(_, PathTimer::PathKeepAlive)
+6007                        | Timer::Conn(ConnTimer::PushNewCid)
+6008                        | Timer::Conn(ConnTimer::KeyDiscard)
+6009                )
+6010            })
+6011            .min_by_key(|(_, time)| *time)
+6012            .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle))
+6013    }
+6014
+6015    /// Whether explicit congestion notification is in use on outgoing packets.
+6016    #[cfg(test)]
+6017    pub(crate) fn using_ecn(&self) -> bool {
+6018        self.path_data(PathId::ZERO).sending_ecn
+6019    }
+6020
+6021    /// The number of received bytes in the current path
+6022    #[cfg(test)]
+6023    pub(crate) fn total_recvd(&self) -> u64 {
+6024        self.path_data(PathId::ZERO).total_recvd
+6025    }
+6026
+6027    #[cfg(test)]
+6028    pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) {
+6029        self.local_cid_state
+6030            .get(&PathId::ZERO)
+6031            .unwrap()
+6032            .active_seq()
+6033    }
+6034
+6035    #[cfg(test)]
+6036    #[track_caller]
+6037    pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) {
+6038        self.local_cid_state
+6039            .get(&PathId(path_id))
+6040            .unwrap()
+6041            .active_seq()
+6042    }
+6043
+6044    /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame
+6045    /// with updated `retire_prior_to` field set to `v`
+6046    #[cfg(test)]
+6047    pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) {
+6048        let n = self
+6049            .local_cid_state
+6050            .get_mut(&PathId::ZERO)
+6051            .unwrap()
+6052            .assign_retire_seq(v);
+6053        self.endpoint_events
+6054            .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n));
+6055    }
+6056
+6057    /// Check the current active remote CID sequence for `PathId::ZERO`
+6058    #[cfg(test)]
+6059    pub(crate) fn active_rem_cid_seq(&self) -> u64 {
+6060        self.rem_cids.get(&PathId::ZERO).unwrap().active_seq()
+6061    }
+6062
+6063    /// Returns the detected maximum udp payload size for the current path
+6064    #[cfg(test)]
+6065    pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 {
+6066        self.path_data(path_id).current_mtu()
+6067    }
+6068
+6069    /// Triggers path validation on all paths
+6070    #[cfg(test)]
+6071    pub(crate) fn trigger_path_validation(&mut self) {
+6072        for path in self.paths.values_mut() {
+6073            path.data.send_new_challenge = true;
+6074        }
+6075    }
+6076
+6077    /// Whether we have 1-RTT data to send
+6078    ///
+6079    /// This checks for frames that can only be sent in the data space (1-RTT):
+6080    /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated.
+6081    /// - Pending PATH_RESPONSE frames.
+6082    /// - Pending data to send in STREAM frames.
+6083    /// - Pending DATAGRAM frames to send.
+6084    ///
+6085    /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that
+6086    /// may need to be sent.
+6087    fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames {
+6088        let path_exclusive = self.paths.get(&path_id).is_some_and(|path| {
+6089            path.data.send_new_challenge
+6090                || path
+6091                    .prev
+6092                    .as_ref()
+6093                    .is_some_and(|(_, path)| path.send_new_challenge)
+6094                || !path.data.path_responses.is_empty()
+6095        });
+6096        let other = self.streams.can_send_stream_data()
+6097            || self
+6098                .datagrams
+6099                .outgoing
+6100                .front()
+6101                .is_some_and(|x| x.size(true) <= max_size);
+6102        SendableFrames {
+6103            acks: false,
+6104            other,
+6105            close: false,
+6106            path_exclusive,
+6107        }
+6108    }
+6109
+6110    /// Terminate the connection instantly, without sending a close packet
+6111    fn kill(&mut self, reason: ConnectionError) {
+6112        self.close_common();
+6113        self.state.move_to_drained(Some(reason));
+6114        self.endpoint_events.push_back(EndpointEventInner::Drained);
+6115    }
+6116
+6117    /// Storage size required for the largest packet that can be transmitted on all currently
+6118    /// available paths
+6119    ///
+6120    /// Buffers passed to [`Connection::poll_transmit`] should be at least this large.
+6121    ///
+6122    /// When multipath is enabled, this value is the minimum MTU across all available paths.
+6123    pub fn current_mtu(&self) -> u16 {
+6124        self.paths
+6125            .iter()
+6126            .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id))
+6127            .map(|(_path_id, path_state)| path_state.data.current_mtu())
+6128            .min()
+6129            .expect("There is always at least one available path")
+6130    }
+6131
+6132    /// Size of non-frame data for a 1-RTT packet
+6133    ///
+6134    /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are
+6135    /// frames. Changes if the length of the remote connection ID changes, which is expected to be
+6136    /// rare. If `pn` is specified, may additionally change unpredictably due to variations in
+6137    /// latency and packet loss.
+6138    fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize {
+6139        let pn_len = PacketNumber::new(
+6140            pn,
+6141            self.spaces[SpaceId::Data]
+6142                .for_path(path)
+6143                .largest_acked_packet
+6144                .unwrap_or(0),
+6145        )
+6146        .len();
+6147
+6148        // 1 byte for flags
+6149        1 + self
+6150            .rem_cids
+6151            .get(&path)
+6152            .map(|cids| cids.active().len())
+6153            .unwrap_or(20)      // Max CID len in QUIC v1
+6154            + pn_len
+6155            + self.tag_len_1rtt()
+6156    }
+6157
+6158    fn predict_1rtt_overhead_no_pn(&self) -> usize {
+6159        let pn_len = 4;
+6160
+6161        let cid_len = self
+6162            .rem_cids
+6163            .values()
+6164            .map(|cids| cids.active().len())
+6165            .max()
+6166            .unwrap_or(20); // Max CID len in QUIC v1
+6167
+6168        // 1 byte for flags
+6169        1 + cid_len + pn_len + self.tag_len_1rtt()
+6170    }
+6171
+6172    fn tag_len_1rtt(&self) -> usize {
+6173        let key = match self.spaces[SpaceId::Data].crypto.as_ref() {
+6174            Some(crypto) => Some(&*crypto.packet.local),
+6175            None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet),
+6176        };
+6177        // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of
+6178        // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead,
+6179        // but that would needlessly prevent sending datagrams during 0-RTT.
+6180        key.map_or(16, |x| x.tag_len())
+6181    }
+6182
+6183    /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate
+6184    fn on_path_validated(&mut self, path_id: PathId) {
+6185        self.path_data_mut(path_id).validated = true;
+6186        let ConnectionSide::Server { server_config } = &self.side else {
+6187            return;
+6188        };
+6189        let remote_addr = self.path_data(path_id).remote;
+6190        let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens;
+6191        new_tokens.clear();
+6192        for _ in 0..server_config.validation_token.sent {
+6193            new_tokens.push(remote_addr);
+6194        }
+6195    }
+6196
+6197    /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP
+6198    fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) {
+6199        if let Some(path) = self.paths.get_mut(&path_id) {
+6200            path.data.status.remote_update(status, status_seq_no);
+6201        } else {
+6202            debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id);
+6203        }
+6204        self.events.push_back(
+6205            PathEvent::RemoteStatus {
+6206                id: path_id,
+6207                status,
+6208            }
+6209            .into(),
+6210        );
+6211    }
+6212
+6213    /// Returns the maximum [`PathId`] to be used for sending in this connection.
+6214    ///
+6215    /// This is calculated as minimum between the local and remote's maximums when multipath is
+6216    /// enabled, or `None` when disabled.
+6217    ///
+6218    /// For data that's received, we should use [`Self::local_max_path_id`] instead.
+6219    /// The reasoning is that the remote might already have updated to its own newer
+6220    /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered.
+6221    fn max_path_id(&self) -> Option<PathId> {
+6222        if self.is_multipath_negotiated() {
+6223            Some(self.remote_max_path_id.min(self.local_max_path_id))
+6224        } else {
+6225            None
+6226        }
+6227    }
+6228
+6229    /// Add addresses the local endpoint considers are reachable for nat traversal
+6230    pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> {
+6231        if let Some(added) = self.iroh_hp.add_local_address(address)? {
+6232            self.spaces[SpaceId::Data].pending.add_address.insert(added);
+6233        };
+6234        Ok(())
+6235    }
+6236
+6237    /// Removes an address the endpoing no longer considers reachable for nat traversal
+6238    ///
+6239    /// Addresses not present in the set will be silently ignored.
+6240    pub fn remove_nat_traversal_address(
+6241        &mut self,
+6242        address: SocketAddr,
+6243    ) -> Result<(), iroh_hp::Error> {
+6244        if let Some(removed) = self.iroh_hp.remove_local_address(address)? {
+6245            self.spaces[SpaceId::Data]
+6246                .pending
+6247                .remove_address
+6248                .insert(removed);
+6249        }
+6250        Ok(())
+6251    }
+6252
+6253    /// Get the current local nat traversal addresses
+6254    pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6255        self.iroh_hp.get_local_nat_traversal_addresses()
+6256    }
+6257
+6258    /// Get the currently advertised nat traversal addresses by the server
+6259    pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6260        Ok(self
+6261            .iroh_hp
+6262            .client_side()?
+6263            .get_remote_nat_traversal_addresses())
+6264    }
+6265
+6266    /// Initiates a new nat traversal round
+6267    ///
+6268    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+6269    /// frames, and initiating probing of the known remote addresses. When a new round is
+6270    /// initiated, the previous one is cancelled, and paths that have not been opened are closed.
+6271    ///
+6272    /// Returns the server addresses that are now being probed.
+6273    pub fn initiate_nat_traversal_round(
+6274        &mut self,
+6275        now: Instant,
+6276    ) -> Result<Vec<SocketAddr>, iroh_hp::Error> {
+6277        let client_state = self.iroh_hp.client_side_mut()?;
+6278        let iroh_hp::NatTraversalRound {
+6279            new_round,
+6280            reach_out_at,
+6281            addresses_to_probe,
+6282            prev_round_path_ids,
+6283        } = client_state.initiate_nat_traversal_round()?;
+6284
+6285        self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at));
+6286
+6287        for path_id in prev_round_path_ids {
+6288            // TODO(@divma): this sounds reasonable but we need if this actually works for the
+6289            // purposes of the protocol
+6290            let validated = self
+6291                .path(path_id)
+6292                .map(|path| path.validated)
+6293                .unwrap_or(false);
+6294
+6295            if !validated {
+6296                let _ = self.close_path(
+6297                    now,
+6298                    path_id,
+6299                    TransportErrorCode::APPLICATION_ABANDON_PATH.into(),
+6300                );
+6301            }
+6302        }
+6303
+6304        let mut err = None;
+6305
+6306        let mut path_ids = Vec::with_capacity(addresses_to_probe.len());
+6307        let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len());
+6308        let ipv6 = self.paths.values().any(|p| p.data.remote.is_ipv6());
+6309
+6310        for (ip, port) in addresses_to_probe {
+6311            // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes.
+6312            let remote = match ip {
+6313                IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port),
+6314                IpAddr::V4(addr) => SocketAddr::new(addr.into(), port),
+6315                IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port),
+6316                IpAddr::V6(_) => {
+6317                    trace!("not using IPv6 nat candidate for IPv4 socket");
+6318                    continue;
+6319                }
+6320            };
+6321            match self.open_path_ensure(remote, PathStatus::Backup, now) {
+6322                Ok((path_id, path_was_known)) if !path_was_known => {
+6323                    path_ids.push(path_id);
+6324                    probed_addresses.push(remote);
+6325                }
+6326                Ok((path_id, _)) => {
+6327                    trace!(%path_id, %remote,"nat traversal: path existed for remote")
+6328                }
+6329                Err(e) => {
+6330                    debug!(%remote, %e,"nat traversal: failed to probe remote");
+6331                    err.get_or_insert(e);
+6332                }
+6333            }
+6334        }
+6335
+6336        self.iroh_hp
+6337            .client_side_mut()
+6338            .expect("connection side validated")
+6339            .set_round_path_ids(path_ids);
+6340
+6341        Ok(probed_addresses)
+6342    }
+6343}
+6344
+6345impl fmt::Debug for Connection {
+6346    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+6347        f.debug_struct("Connection")
+6348            .field("handshake_cid", &self.handshake_cid)
+6349            .finish()
+6350    }
+6351}
+6352
+6353#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+6354enum PathBlocked {
+6355    No,
+6356    AntiAmplification,
+6357    Congestion,
+6358    Pacing,
+6359}
+6360
+6361/// Fields of `Connection` specific to it being client-side or server-side
+6362enum ConnectionSide {
+6363    Client {
+6364        /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded
+6365        token: Bytes,
+6366        token_store: Arc<dyn TokenStore>,
+6367        server_name: String,
+6368    },
+6369    Server {
+6370        server_config: Arc<ServerConfig>,
+6371    },
+6372}
+6373
+6374impl ConnectionSide {
+6375    fn remote_may_migrate(&self, state: &State) -> bool {
+6376        match self {
+6377            Self::Server { server_config } => server_config.migration,
+6378            Self::Client { .. } => {
+6379                if let Some(hs) = state.as_handshake() {
+6380                    hs.allow_server_migration
+6381                } else {
+6382                    false
+6383                }
+6384            }
+6385        }
+6386    }
+6387
+6388    fn is_client(&self) -> bool {
+6389        self.side().is_client()
+6390    }
+6391
+6392    fn is_server(&self) -> bool {
+6393        self.side().is_server()
+6394    }
+6395
+6396    fn side(&self) -> Side {
+6397        match *self {
+6398            Self::Client { .. } => Side::Client,
+6399            Self::Server { .. } => Side::Server,
+6400        }
+6401    }
+6402}
+6403
+6404impl From<SideArgs> for ConnectionSide {
+6405    fn from(side: SideArgs) -> Self {
+6406        match side {
+6407            SideArgs::Client {
+6408                token_store,
+6409                server_name,
+6410            } => Self::Client {
+6411                token: token_store.take(&server_name).unwrap_or_default(),
+6412                token_store,
+6413                server_name,
+6414            },
+6415            SideArgs::Server {
+6416                server_config,
+6417                pref_addr_cid: _,
+6418                path_validated: _,
+6419            } => Self::Server { server_config },
+6420        }
+6421    }
+6422}
+6423
+6424/// Parameters to `Connection::new` specific to it being client-side or server-side
+6425pub(crate) enum SideArgs {
+6426    Client {
+6427        token_store: Arc<dyn TokenStore>,
+6428        server_name: String,
+6429    },
+6430    Server {
+6431        server_config: Arc<ServerConfig>,
+6432        pref_addr_cid: Option<ConnectionId>,
+6433        path_validated: bool,
+6434    },
+6435}
+6436
+6437impl SideArgs {
+6438    pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> {
+6439        match *self {
+6440            Self::Client { .. } => None,
+6441            Self::Server { pref_addr_cid, .. } => pref_addr_cid,
+6442        }
+6443    }
+6444
+6445    pub(crate) fn path_validated(&self) -> bool {
+6446        match *self {
+6447            Self::Client { .. } => true,
+6448            Self::Server { path_validated, .. } => path_validated,
+6449        }
+6450    }
+6451
+6452    pub(crate) fn side(&self) -> Side {
+6453        match *self {
+6454            Self::Client { .. } => Side::Client,
+6455            Self::Server { .. } => Side::Server,
+6456        }
+6457    }
+6458}
+6459
+6460/// Reasons why a connection might be lost
+6461#[derive(Debug, Error, Clone, PartialEq, Eq)]
+6462pub enum ConnectionError {
+6463    /// The peer doesn't implement any supported version
+6464    #[error("peer doesn't implement any supported version")]
+6465    VersionMismatch,
+6466    /// The peer violated the QUIC specification as understood by this implementation
+6467    #[error(transparent)]
+6468    TransportError(#[from] TransportError),
+6469    /// The peer's QUIC stack aborted the connection automatically
+6470    #[error("aborted by peer: {0}")]
+6471    ConnectionClosed(frame::ConnectionClose),
+6472    /// The peer closed the connection
+6473    #[error("closed by peer: {0}")]
+6474    ApplicationClosed(frame::ApplicationClose),
+6475    /// The peer is unable to continue processing this connection, usually due to having restarted
+6476    #[error("reset by peer")]
+6477    Reset,
+6478    /// Communication with the peer has lapsed for longer than the negotiated idle timeout
+6479    ///
+6480    /// If neither side is sending keep-alives, a connection will time out after a long enough idle
+6481    /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`]
+6482    /// and [`TransportConfig::keep_alive_interval()`].
+6483    #[error("timed out")]
+6484    TimedOut,
+6485    /// The local application closed the connection
+6486    #[error("closed")]
+6487    LocallyClosed,
+6488    /// The connection could not be created because not enough of the CID space is available
+6489    ///
+6490    /// Try using longer connection IDs.
+6491    #[error("CIDs exhausted")]
+6492    CidsExhausted,
+6493}
+6494
+6495impl From<Close> for ConnectionError {
+6496    fn from(x: Close) -> Self {
+6497        match x {
+6498            Close::Connection(reason) => Self::ConnectionClosed(reason),
+6499            Close::Application(reason) => Self::ApplicationClosed(reason),
+6500        }
+6501    }
+6502}
+6503
+6504// For compatibility with API consumers
+6505impl From<ConnectionError> for io::Error {
+6506    fn from(x: ConnectionError) -> Self {
+6507        use ConnectionError::*;
+6508        let kind = match x {
+6509            TimedOut => io::ErrorKind::TimedOut,
+6510            Reset => io::ErrorKind::ConnectionReset,
+6511            ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted,
+6512            TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => {
+6513                io::ErrorKind::Other
+6514            }
+6515        };
+6516        Self::new(kind, x)
+6517    }
+6518}
+6519
+6520/// Errors that might trigger a path being closed
+6521// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API
+6522#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)]
+6523pub enum PathError {
+6524    /// The extension was not negotiated with the peer
+6525    #[error("multipath extension not negotiated")]
+6526    MultipathNotNegotiated,
+6527    /// Paths can only be opened client-side
+6528    #[error("the server side may not open a path")]
+6529    ServerSideNotAllowed,
+6530    /// Current limits do not allow us to open more paths
+6531    #[error("maximum number of concurrent paths reached")]
+6532    MaxPathIdReached,
+6533    /// No remote CIDs available to open a new path
+6534    #[error("remoted CIDs exhausted")]
+6535    RemoteCidsExhausted,
+6536    /// Path could not be validated and will be abandoned
+6537    #[error("path validation failed")]
+6538    ValidationFailed,
+6539    /// The remote address for the path is not supported by the endpoint
+6540    #[error("invalid remote address")]
+6541    InvalidRemoteAddress(SocketAddr),
+6542}
+6543
+6544/// Errors triggered when abandoning a path
+6545#[derive(Debug, Error, Clone, Eq, PartialEq)]
+6546pub enum ClosePathError {
+6547    /// The path is already closed or was never opened
+6548    #[error("closed path")]
+6549    ClosedPath,
+6550    /// This is the last path, which can not be abandoned
+6551    #[error("last open path")]
+6552    LastOpenPath,
+6553}
+6554
+6555#[derive(Debug, Error, Clone, Copy)]
+6556#[error("Multipath extension not negotiated")]
+6557pub struct MultipathNotNegotiated {
+6558    _private: (),
+6559}
+6560
+6561/// Events of interest to the application
+6562#[derive(Debug)]
+6563pub enum Event {
+6564    /// The connection's handshake data is ready
+6565    HandshakeDataReady,
+6566    /// The connection was successfully established
+6567    Connected,
+6568    /// The TLS handshake was confirmed
+6569    HandshakeConfirmed,
+6570    /// The connection was lost
+6571    ///
+6572    /// Emitted if the peer closes the connection or an error is encountered.
+6573    ConnectionLost {
+6574        /// Reason that the connection was closed
+6575        reason: ConnectionError,
+6576    },
+6577    /// Stream events
+6578    Stream(StreamEvent),
+6579    /// One or more application datagrams have been received
+6580    DatagramReceived,
+6581    /// One or more application datagrams have been sent after blocking
+6582    DatagramsUnblocked,
+6583    /// (Multi)Path events
+6584    Path(PathEvent),
+6585    /// Iroh's nat traversal events
+6586    NatTraversal(iroh_hp::Event),
+6587}
+6588
+6589impl From<PathEvent> for Event {
+6590    fn from(source: PathEvent) -> Self {
+6591        Self::Path(source)
+6592    }
+6593}
+6594
+6595fn get_max_ack_delay(params: &TransportParameters) -> Duration {
+6596    Duration::from_micros(params.max_ack_delay.0 * 1000)
+6597}
+6598
+6599// Prevents overflow and improves behavior in extreme circumstances
+6600const MAX_BACKOFF_EXPONENT: u32 = 16;
+6601
+6602/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag
+6603///
+6604/// This must be at least as large as the header for a well-formed empty packet to be coalesced,
+6605/// plus some space for frames. We only care about handshake headers because short header packets
+6606/// necessarily have smaller headers, and initial packets are only ever the first packet in a
+6607/// datagram (because we coalesce in ascending packet space order and the only reason to split a
+6608/// packet is when packet space changes).
+6609const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32;
+6610
+6611/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header
+6612///
+6613/// Excludes packet-type-specific fields such as packet number or Initial token
+6614// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid +
+6615// scid len + scid + length + pn
+6616const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize =
+6617    1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4;
+6618
+6619/// Perform key updates this many packets before the AEAD confidentiality limit.
+6620///
+6621/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss.
+6622const KEY_UPDATE_MARGIN: u64 = 10_000;
+6623
+6624#[derive(Default)]
+6625struct SentFrames {
+6626    retransmits: ThinRetransmits,
+6627    /// The packet number of the largest acknowledged packet for each path
+6628    largest_acked: FxHashMap<PathId, u64>,
+6629    stream_frames: StreamMetaVec,
+6630    /// Whether the packet contains non-retransmittable frames (like datagrams)
+6631    non_retransmits: bool,
+6632    /// If the datagram containing these frames should be padded to the min MTU
+6633    requires_padding: bool,
+6634}
+6635
+6636impl SentFrames {
+6637    /// Returns whether the packet contains only ACKs
+6638    fn is_ack_only(&self, streams: &StreamsState) -> bool {
+6639        !self.largest_acked.is_empty()
+6640            && !self.non_retransmits
+6641            && self.stream_frames.is_empty()
+6642            && self.retransmits.is_empty(streams)
+6643    }
+6644}
+6645
+6646/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters.
+6647///
+6648/// 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.>
+6649///
+6650/// 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.>
+6651///
+6652/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout.
+6653fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> {
+6654    match (x, y) {
+6655        (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None,
+6656        (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)),
+6657        (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)),
+6658        (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)),
+6659    }
+6660}
+6661
+6662#[cfg(test)]
+6663mod tests {
+6664    use super::*;
+6665
+6666    #[test]
+6667    fn negotiate_max_idle_timeout_commutative() {
+6668        let test_params = [
+6669            (None, None, None),
+6670            (None, Some(VarInt(0)), None),
+6671            (None, Some(VarInt(2)), Some(Duration::from_millis(2))),
+6672            (Some(VarInt(0)), Some(VarInt(0)), None),
+6673            (
+6674                Some(VarInt(2)),
+6675                Some(VarInt(0)),
+6676                Some(Duration::from_millis(2)),
+6677            ),
+6678            (
+6679                Some(VarInt(1)),
+6680                Some(VarInt(4)),
+6681                Some(Duration::from_millis(1)),
+6682            ),
+6683        ];
+6684
+6685        for (left, right, result) in test_params {
+6686            assert_eq!(negotiate_max_idle_timeout(left, right), result);
+6687            assert_eq!(negotiate_max_idle_timeout(right, left), result);
+6688        }
+6689    }
+6690}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/mtud.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/mtud.rs.html new file mode 100644 index 000000000..e94f6f998 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/mtud.rs.html @@ -0,0 +1,980 @@ +mtud.rs - source

iroh_quinn_proto/connection/
mtud.rs

1use crate::{Instant, MAX_UDP_PAYLOAD, MtuDiscoveryConfig, packet::SpaceId};
+2use std::cmp;
+3use tracing::trace;
+4
+5/// Implements Datagram Packetization Layer Path Maximum Transmission Unit Discovery
+6///
+7/// See [`MtuDiscoveryConfig`] for details
+8#[derive(Clone, Debug)]
+9pub(crate) struct MtuDiscovery {
+10    /// Detected MTU for the path
+11    current_mtu: u16,
+12    /// The state of the MTU discovery, if enabled
+13    state: Option<EnabledMtuDiscovery>,
+14    /// The state of the black hole detector
+15    black_hole_detector: BlackHoleDetector,
+16}
+17
+18impl MtuDiscovery {
+19    pub(crate) fn new(
+20        initial_plpmtu: u16,
+21        min_mtu: u16,
+22        peer_max_udp_payload_size: Option<u16>,
+23        config: MtuDiscoveryConfig,
+24    ) -> Self {
+25        debug_assert!(
+26            initial_plpmtu >= min_mtu,
+27            "initial_max_udp_payload_size must be at least {min_mtu}"
+28        );
+29
+30        let mut mtud = Self::with_state(
+31            initial_plpmtu,
+32            min_mtu,
+33            Some(EnabledMtuDiscovery::new(config)),
+34        );
+35
+36        // We might be migrating an existing connection to a new path, in which case the transport
+37        // parameters have already been transmitted, and we already know the value of
+38        // `peer_max_udp_payload_size`
+39        if let Some(peer_max_udp_payload_size) = peer_max_udp_payload_size {
+40            mtud.on_peer_max_udp_payload_size_received(peer_max_udp_payload_size);
+41        }
+42
+43        mtud
+44    }
+45
+46    /// MTU discovery will be disabled and the current MTU will be fixed to the provided value
+47    pub(crate) fn disabled(plpmtu: u16, min_mtu: u16) -> Self {
+48        Self::with_state(plpmtu, min_mtu, None)
+49    }
+50
+51    fn with_state(current_mtu: u16, min_mtu: u16, state: Option<EnabledMtuDiscovery>) -> Self {
+52        Self {
+53            current_mtu,
+54            state,
+55            black_hole_detector: BlackHoleDetector::new(min_mtu),
+56        }
+57    }
+58
+59    /// Returns the current MTU
+60    pub(crate) fn current_mtu(&self) -> u16 {
+61        self.current_mtu
+62    }
+63
+64    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+65    ///
+66    /// Returns [`None`] if MTUD discovery is disabled. Otherwise delegates to
+67    /// [`EnabledMtuDiscovery::poll_transmit`].
+68    pub(crate) fn poll_transmit(&mut self, now: Instant, next_pn: u64) -> Option<u16> {
+69        self.state
+70            .as_mut()
+71            .and_then(|state| state.poll_transmit(now, self.current_mtu, next_pn))
+72    }
+73
+74    /// Notifies the [`MtuDiscovery`] that the peer's `max_udp_payload_size` transport parameter has
+75    /// been received
+76    pub(crate) fn on_peer_max_udp_payload_size_received(&mut self, peer_max_udp_payload_size: u16) {
+77        self.current_mtu = self.current_mtu.min(peer_max_udp_payload_size);
+78
+79        if let Some(state) = self.state.as_mut() {
+80            // It is possible for black hole detection to trigger before the connection has been
+81            // fully established, if the initial MTU is greater the minimum MTU. We should never
+82            // send probes before the connection has been fully established and we have received
+83            // the peer's transport parameters though.
+84            debug_assert!(
+85                !matches!(state.phase, Phase::Searching(_)),
+86                "Transport parameters received after MTU probing started"
+87            );
+88            state.peer_max_udp_payload_size = peer_max_udp_payload_size;
+89        }
+90    }
+91
+92    /// Notifies the [`MtuDiscovery`] that a packet has been ACKed
+93    ///
+94    /// Returns true if the packet was an MTU probe
+95    pub(crate) fn on_acked(&mut self, space: SpaceId, pn: u64, len: u16) -> bool {
+96        // MTU probes are only sent in application data space
+97        if space != SpaceId::Data {
+98            return false;
+99        }
+100
+101        // Update the state of the MTU search
+102        if let Some(new_mtu) = self
+103            .state
+104            .as_mut()
+105            .and_then(|state| state.on_probe_acked(pn))
+106        {
+107            self.current_mtu = new_mtu;
+108            trace!(current_mtu = self.current_mtu, "new MTU detected");
+109
+110            self.black_hole_detector.on_probe_acked(pn, len);
+111            true
+112        } else {
+113            self.black_hole_detector.on_non_probe_acked(pn, len);
+114            false
+115        }
+116    }
+117
+118    /// Returns the packet number of the in-flight MTU probe, if any
+119    pub(crate) fn in_flight_mtu_probe(&self) -> Option<u64> {
+120        match &self.state {
+121            Some(EnabledMtuDiscovery {
+122                phase: Phase::Searching(search_state),
+123                ..
+124            }) => search_state.in_flight_probe,
+125            _ => None,
+126        }
+127    }
+128
+129    /// Notifies the [`MtuDiscovery`] that the in-flight MTU probe was lost
+130    pub(crate) fn on_probe_lost(&mut self) {
+131        if let Some(state) = &mut self.state {
+132            state.on_probe_lost();
+133        }
+134    }
+135
+136    /// Notifies the [`MtuDiscovery`] that a non-probe packet was lost
+137    ///
+138    /// When done notifying of lost packets, [`MtuDiscovery::black_hole_detected`] must be called, to
+139    /// ensure the last loss burst is properly processed and to trigger black hole recovery logic if
+140    /// necessary.
+141    pub(crate) fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+142        self.black_hole_detector.on_non_probe_lost(pn, len);
+143    }
+144
+145    /// Returns true if a black hole was detected
+146    ///
+147    /// Calling this function will close the previous loss burst. If a black hole is detected, the
+148    /// current MTU will be reset to `min_mtu`.
+149    pub(crate) fn black_hole_detected(&mut self, now: Instant) -> bool {
+150        if !self.black_hole_detector.black_hole_detected() {
+151            return false;
+152        }
+153
+154        self.current_mtu = self.black_hole_detector.min_mtu;
+155
+156        if let Some(state) = &mut self.state {
+157            state.on_black_hole_detected(now);
+158        }
+159
+160        true
+161    }
+162}
+163
+164/// Additional state for enabled MTU discovery
+165#[derive(Debug, Clone)]
+166struct EnabledMtuDiscovery {
+167    phase: Phase,
+168    peer_max_udp_payload_size: u16,
+169    config: MtuDiscoveryConfig,
+170}
+171
+172impl EnabledMtuDiscovery {
+173    fn new(config: MtuDiscoveryConfig) -> Self {
+174        Self {
+175            phase: Phase::Initial,
+176            peer_max_udp_payload_size: MAX_UDP_PAYLOAD,
+177            config,
+178        }
+179    }
+180
+181    /// Returns the amount of bytes that should be sent as an MTU probe, if any.
+182    ///
+183    /// A probe only needs to be sent if:
+184    ///
+185    /// - There is no current in-flight probe.
+186    /// - A search for a new MTU is in progress.
+187    /// - The MTU discovery was completed but the [`MtuDiscoveryConfig::interval`] expired,
+188    ///   this re-starts a n MTU search.
+189    fn poll_transmit(&mut self, now: Instant, current_mtu: u16, next_pn: u64) -> Option<u16> {
+190        if let Phase::Initial = &self.phase {
+191            // Start the first search
+192            self.phase = Phase::Searching(SearchState::new(
+193                current_mtu,
+194                self.peer_max_udp_payload_size,
+195                &self.config,
+196            ));
+197        } else if let Phase::Complete(next_mtud_activation) = &self.phase {
+198            if now < *next_mtud_activation {
+199                return None;
+200            }
+201
+202            // Start a new search (we have reached the next activation time)
+203            self.phase = Phase::Searching(SearchState::new(
+204                current_mtu,
+205                self.peer_max_udp_payload_size,
+206                &self.config,
+207            ));
+208        }
+209
+210        if let Phase::Searching(state) = &mut self.phase {
+211            // Nothing to do while there is a probe in flight
+212            if state.in_flight_probe.is_some() {
+213                return None;
+214            }
+215
+216            // Retransmit lost probes, if any
+217            if 0 < state.lost_probe_count && state.lost_probe_count < MAX_PROBE_RETRANSMITS {
+218                state.in_flight_probe = Some(next_pn);
+219                return Some(state.last_probed_mtu);
+220            }
+221
+222            let last_probe_succeeded = state.lost_probe_count == 0;
+223
+224            // The probe is definitely lost (we reached the MAX_PROBE_RETRANSMITS threshold)
+225            if !last_probe_succeeded {
+226                state.lost_probe_count = 0;
+227                state.in_flight_probe = None;
+228            }
+229
+230            if let Some(probe_udp_payload_size) = state.next_mtu_to_probe(last_probe_succeeded) {
+231                state.in_flight_probe = Some(next_pn);
+232                state.last_probed_mtu = probe_udp_payload_size;
+233                return Some(probe_udp_payload_size);
+234            } else {
+235                let next_mtud_activation = now + self.config.interval;
+236                self.phase = Phase::Complete(next_mtud_activation);
+237                return None;
+238            }
+239        }
+240
+241        None
+242    }
+243
+244    /// Called when a packet is acknowledged in [`SpaceId::Data`]
+245    ///
+246    /// Returns the new `current_mtu` if the packet number corresponds to the in-flight MTU probe
+247    fn on_probe_acked(&mut self, pn: u64) -> Option<u16> {
+248        match &mut self.phase {
+249            Phase::Searching(state) if state.in_flight_probe == Some(pn) => {
+250                state.in_flight_probe = None;
+251                state.lost_probe_count = 0;
+252                Some(state.last_probed_mtu)
+253            }
+254            _ => None,
+255        }
+256    }
+257
+258    /// Called when the in-flight MTU probe was lost
+259    fn on_probe_lost(&mut self) {
+260        // We might no longer be searching, e.g. if a black hole was detected
+261        if let Phase::Searching(state) = &mut self.phase {
+262            state.in_flight_probe = None;
+263            state.lost_probe_count += 1;
+264        }
+265    }
+266
+267    /// Called when a black hole is detected
+268    fn on_black_hole_detected(&mut self, now: Instant) {
+269        // Stop searching, if applicable, and reset the timer
+270        let next_mtud_activation = now + self.config.black_hole_cooldown;
+271        self.phase = Phase::Complete(next_mtud_activation);
+272    }
+273}
+274
+275#[derive(Debug, Clone, Copy)]
+276enum Phase {
+277    /// We haven't started polling yet
+278    Initial,
+279    /// We are currently searching for a higher PMTU
+280    Searching(SearchState),
+281    /// Searching has completed and will be triggered again at the provided instant
+282    Complete(Instant),
+283}
+284
+285#[derive(Debug, Clone, Copy)]
+286struct SearchState {
+287    /// The lower bound for the current binary search
+288    lower_bound: u16,
+289    /// The upper bound for the current binary search
+290    upper_bound: u16,
+291    /// The minimum change to stop the current binary search
+292    minimum_change: u16,
+293    /// The UDP payload size we last sent a probe for
+294    last_probed_mtu: u16,
+295    /// Packet number of an in-flight probe (if any)
+296    in_flight_probe: Option<u64>,
+297    /// Lost probes at the current probe size
+298    lost_probe_count: usize,
+299}
+300
+301impl SearchState {
+302    /// Creates a new search state, with the specified lower bound (the upper bound is derived from
+303    /// the config and the peer's `max_udp_payload_size` transport parameter)
+304    fn new(
+305        mut lower_bound: u16,
+306        peer_max_udp_payload_size: u16,
+307        config: &MtuDiscoveryConfig,
+308    ) -> Self {
+309        lower_bound = lower_bound.min(peer_max_udp_payload_size);
+310        let upper_bound = config
+311            .upper_bound
+312            .clamp(lower_bound, peer_max_udp_payload_size);
+313
+314        Self {
+315            in_flight_probe: None,
+316            lost_probe_count: 0,
+317            lower_bound,
+318            upper_bound,
+319            minimum_change: config.minimum_change,
+320            // During initialization, we consider the lower bound to have already been
+321            // successfully probed
+322            last_probed_mtu: lower_bound,
+323        }
+324    }
+325
+326    /// Determines the next MTU to probe using binary search
+327    fn next_mtu_to_probe(&mut self, last_probe_succeeded: bool) -> Option<u16> {
+328        debug_assert_eq!(self.in_flight_probe, None);
+329
+330        if last_probe_succeeded {
+331            self.lower_bound = self.last_probed_mtu;
+332        } else {
+333            self.upper_bound = self.last_probed_mtu - 1;
+334        }
+335
+336        let next_mtu = (self.lower_bound as i32 + self.upper_bound as i32) / 2;
+337
+338        // Binary search stopping condition
+339        if ((next_mtu - self.last_probed_mtu as i32).unsigned_abs() as u16) < self.minimum_change {
+340            // Special case: if the upper bound is far enough, we want to probe it as a last
+341            // step (otherwise we will never achieve the upper bound)
+342            if self.upper_bound.saturating_sub(self.last_probed_mtu) >= self.minimum_change {
+343                return Some(self.upper_bound);
+344            }
+345
+346            return None;
+347        }
+348
+349        Some(next_mtu as u16)
+350    }
+351}
+352
+353/// Judges whether packet loss might indicate a drop in MTU
+354///
+355/// Our MTU black hole detection scheme is a heuristic based on the order in which packets were sent
+356/// (the packet number order), their sizes, and which are deemed lost.
+357///
+358/// First, contiguous groups of lost packets ("loss bursts") are aggregated, because a group of
+359/// packets all lost together were probably lost for the same reason.
+360///
+361/// A loss burst is deemed "suspicious" if it contains no packets that are (a) smaller than the
+362/// minimum MTU or (b) smaller than a more recent acknowledged packet, because such a burst could be
+363/// fully explained by a reduction in MTU.
+364///
+365/// When the number of suspicious loss bursts exceeds [`BLACK_HOLE_THRESHOLD`], we judge the
+366/// evidence for an MTU black hole to be sufficient.
+367#[derive(Clone, Debug)]
+368struct BlackHoleDetector {
+369    /// Packet loss bursts currently considered suspicious
+370    suspicious_loss_bursts: Vec<LossBurst>,
+371    /// Loss burst currently being aggregated, if any
+372    current_loss_burst: Option<CurrentLossBurst>,
+373    /// Packet number of the biggest packet larger than `min_mtu` which we've received
+374    /// acknowledgment of more recently than any suspicious loss burst, if any
+375    largest_post_loss_packet: u64,
+376    /// The maximum of `min_mtu` and the size of `largest_post_loss_packet`, or exactly `min_mtu` if
+377    /// no larger packets have been received since the most recent loss burst.
+378    acked_mtu: u16,
+379    /// The UDP payload size guaranteed to be supported by the network
+380    min_mtu: u16,
+381}
+382
+383impl BlackHoleDetector {
+384    fn new(min_mtu: u16) -> Self {
+385        Self {
+386            suspicious_loss_bursts: Vec::with_capacity(BLACK_HOLE_THRESHOLD + 1),
+387            current_loss_burst: None,
+388            largest_post_loss_packet: 0,
+389            acked_mtu: min_mtu,
+390            min_mtu,
+391        }
+392    }
+393
+394    fn on_probe_acked(&mut self, pn: u64, len: u16) {
+395        // MTU probes are always larger than the previous MTU, so no previous loss bursts are
+396        // suspicious. At most one MTU probe is in flight at a time, so we don't need to worry about
+397        // reordering between them.
+398        self.suspicious_loss_bursts.clear();
+399        self.acked_mtu = len;
+400        // This might go backwards, but that's okay: a successful ACK means we haven't yet judged a
+401        // more recently sent packet lost, and we just want to track the largest packet that's been
+402        // successfully delivered more recently than a loss.
+403        self.largest_post_loss_packet = pn;
+404    }
+405
+406    fn on_non_probe_acked(&mut self, pn: u64, len: u16) {
+407        if len <= self.acked_mtu {
+408            // We've already seen a larger packet since the most recent suspicious loss burst;
+409            // nothing to do.
+410            return;
+411        }
+412        self.acked_mtu = len;
+413        // This might go backwards, but that's okay as described in `on_probe_acked`.
+414        self.largest_post_loss_packet = pn;
+415        // Loss bursts packets smaller than this are retroactively deemed non-suspicious.
+416        self.suspicious_loss_bursts
+417            .retain(|burst| burst.smallest_packet_size > len);
+418    }
+419
+420    fn on_non_probe_lost(&mut self, pn: u64, len: u16) {
+421        // A loss burst is a group of consecutive packets that are declared lost, so a distance
+422        // greater than 1 indicates a new burst
+423        let end_last_burst = self
+424            .current_loss_burst
+425            .as_ref()
+426            .is_some_and(|current| pn - current.latest_non_probe != 1);
+427
+428        if end_last_burst {
+429            self.finish_loss_burst();
+430        }
+431
+432        self.current_loss_burst = Some(CurrentLossBurst {
+433            latest_non_probe: pn,
+434            smallest_packet_size: self
+435                .current_loss_burst
+436                .map_or(len, |prev| cmp::min(prev.smallest_packet_size, len)),
+437        });
+438    }
+439
+440    fn black_hole_detected(&mut self) -> bool {
+441        self.finish_loss_burst();
+442
+443        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+444            return false;
+445        }
+446
+447        self.suspicious_loss_bursts.clear();
+448
+449        true
+450    }
+451
+452    /// Marks the end of the current loss burst, checking whether it was suspicious
+453    fn finish_loss_burst(&mut self) {
+454        let Some(burst) = self.current_loss_burst.take() else {
+455            return;
+456        };
+457        // If a loss burst contains a packet smaller than the minimum MTU or a more recently
+458        // transmitted packet, it is not suspicious.
+459        if burst.smallest_packet_size <= self.min_mtu
+460            || (burst.latest_non_probe < self.largest_post_loss_packet
+461                && burst.smallest_packet_size <= self.acked_mtu)
+462        {
+463            return;
+464        }
+465        // The loss burst is now deemed suspicious.
+466
+467        // A suspicious loss burst more recent than `largest_post_loss_packet` invalidates it. This
+468        // makes `acked_mtu` a conservative approximation. Ideally we'd update `safe_mtu` and
+469        // `largest_post_loss_packet` to describe the largest acknowledged packet sent later than
+470        // this burst, but that would require tracking the size of an unpredictable number of
+471        // recently acknowledged packets, and erring on the side of false positives is safe.
+472        if burst.latest_non_probe > self.largest_post_loss_packet {
+473            self.acked_mtu = self.min_mtu;
+474        }
+475
+476        let burst = LossBurst {
+477            smallest_packet_size: burst.smallest_packet_size,
+478        };
+479
+480        if self.suspicious_loss_bursts.len() <= BLACK_HOLE_THRESHOLD {
+481            self.suspicious_loss_bursts.push(burst);
+482            return;
+483        }
+484
+485        // To limit memory use, only track the most suspicious loss bursts.
+486        let smallest = self
+487            .suspicious_loss_bursts
+488            .iter_mut()
+489            .min_by_key(|prev| prev.smallest_packet_size)
+490            .filter(|prev| prev.smallest_packet_size < burst.smallest_packet_size);
+491        if let Some(smallest) = smallest {
+492            *smallest = burst;
+493        }
+494    }
+495
+496    #[cfg(test)]
+497    fn suspicious_loss_burst_count(&self) -> usize {
+498        self.suspicious_loss_bursts.len()
+499    }
+500
+501    #[cfg(test)]
+502    fn largest_non_probe_lost(&self) -> Option<u64> {
+503        self.current_loss_burst.as_ref().map(|x| x.latest_non_probe)
+504    }
+505}
+506
+507#[derive(Copy, Clone, Debug)]
+508struct LossBurst {
+509    smallest_packet_size: u16,
+510}
+511
+512#[derive(Copy, Clone, Debug)]
+513struct CurrentLossBurst {
+514    smallest_packet_size: u16,
+515    latest_non_probe: u64,
+516}
+517
+518// Corresponds to the RFC's `MAX_PROBES` constant (see
+519// https://www.rfc-editor.org/rfc/rfc8899#section-5.1.2)
+520const MAX_PROBE_RETRANSMITS: usize = 3;
+521/// Maximum number of suspicious loss bursts that will not trigger black hole detection
+522const BLACK_HOLE_THRESHOLD: usize = 3;
+523
+524#[cfg(test)]
+525mod tests {
+526    use super::*;
+527    use crate::Duration;
+528    use crate::MAX_UDP_PAYLOAD;
+529    use crate::packet::SpaceId;
+530    use assert_matches::assert_matches;
+531
+532    fn default_mtud() -> MtuDiscovery {
+533        let config = MtuDiscoveryConfig::default();
+534        MtuDiscovery::new(1_200, 1_200, None, config)
+535    }
+536
+537    fn completed(mtud: &MtuDiscovery) -> bool {
+538        matches!(mtud.state.as_ref().unwrap().phase, Phase::Complete(_))
+539    }
+540
+541    /// Drives mtud until it reaches `Phase::Completed`
+542    fn drive_to_completion(
+543        mtud: &mut MtuDiscovery,
+544        now: Instant,
+545        link_payload_size_limit: u16,
+546    ) -> Vec<u16> {
+547        let mut probed_sizes = Vec::new();
+548        for probe_pn in 1..100 {
+549            let result = mtud.poll_transmit(now, probe_pn);
+550
+551            if completed(mtud) {
+552                break;
+553            }
+554
+555            // "Send" next probe
+556            assert!(result.is_some());
+557            let probe_size = result.unwrap();
+558            probed_sizes.push(probe_size);
+559
+560            if probe_size <= link_payload_size_limit {
+561                mtud.on_acked(SpaceId::Data, probe_pn, probe_size);
+562            } else {
+563                mtud.on_probe_lost();
+564            }
+565        }
+566        probed_sizes
+567    }
+568
+569    #[test]
+570    fn black_hole_detector_ignores_burst_containing_non_suspicious_packet() {
+571        let mut mtud = default_mtud();
+572        mtud.on_non_probe_lost(2, 1300);
+573        mtud.on_non_probe_lost(3, 1300);
+574        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+575        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+576
+577        mtud.on_non_probe_lost(4, 800);
+578        assert!(!mtud.black_hole_detected(Instant::now()));
+579        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+580        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+581    }
+582
+583    #[test]
+584    fn black_hole_detector_counts_burst_containing_only_suspicious_packets() {
+585        let mut mtud = default_mtud();
+586        mtud.on_non_probe_lost(2, 1300);
+587        mtud.on_non_probe_lost(3, 1300);
+588        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), Some(3));
+589        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+590
+591        assert!(!mtud.black_hole_detected(Instant::now()));
+592        assert_eq!(mtud.black_hole_detector.largest_non_probe_lost(), None);
+593        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 1);
+594    }
+595
+596    #[test]
+597    fn black_hole_detector_ignores_empty_burst() {
+598        let mut mtud = default_mtud();
+599        assert!(!mtud.black_hole_detected(Instant::now()));
+600        assert_eq!(mtud.black_hole_detector.suspicious_loss_burst_count(), 0);
+601    }
+602
+603    #[test]
+604    fn mtu_discovery_disabled_does_nothing() {
+605        let mut mtud = MtuDiscovery::disabled(1_200, 1_200);
+606        let probe_size = mtud.poll_transmit(Instant::now(), 0);
+607        assert_eq!(probe_size, None);
+608    }
+609
+610    #[test]
+611    fn mtu_discovery_disabled_lost_four_packet_bursts_triggers_black_hole_detection() {
+612        let mut mtud = MtuDiscovery::disabled(1_400, 1_250);
+613        let now = Instant::now();
+614
+615        for i in 0..4 {
+616            // The packets are never contiguous, so each one has its own burst
+617            mtud.on_non_probe_lost(i * 2, 1300);
+618        }
+619
+620        assert!(mtud.black_hole_detected(now));
+621        assert_eq!(mtud.current_mtu, 1250);
+622        assert_matches!(mtud.state, None);
+623    }
+624
+625    #[test]
+626    fn mtu_discovery_lost_two_packet_bursts_does_not_trigger_black_hole_detection() {
+627        let mut mtud = default_mtud();
+628        let now = Instant::now();
+629
+630        for i in 0..2 {
+631            mtud.on_non_probe_lost(i, 1300);
+632            assert!(!mtud.black_hole_detected(now));
+633        }
+634    }
+635
+636    #[test]
+637    fn mtu_discovery_lost_four_packet_bursts_triggers_black_hole_detection_and_resets_timer() {
+638        let mut mtud = default_mtud();
+639        let now = Instant::now();
+640
+641        for i in 0..4 {
+642            // The packets are never contiguous, so each one has its own burst
+643            mtud.on_non_probe_lost(i * 2, 1300);
+644        }
+645
+646        assert!(mtud.black_hole_detected(now));
+647        assert_eq!(mtud.current_mtu, 1200);
+648        if let Phase::Complete(next_mtud_activation) = mtud.state.unwrap().phase {
+649            assert_eq!(next_mtud_activation, now + Duration::from_secs(60));
+650        } else {
+651            panic!("Unexpected MTUD phase!");
+652        }
+653    }
+654
+655    #[test]
+656    fn mtu_discovery_after_complete_reactivates_when_interval_elapsed() {
+657        let mut config = MtuDiscoveryConfig::default();
+658        config.upper_bound(9_000);
+659        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+660        let now = Instant::now();
+661        drive_to_completion(&mut mtud, now, 1_500);
+662
+663        // Polling right after completion does not cause new packets to be sent
+664        assert_eq!(mtud.poll_transmit(now, 42), None);
+665        assert!(completed(&mtud));
+666        assert_eq!(mtud.current_mtu, 1_471);
+667
+668        // Polling after the interval has passed does (taking the current mtu as lower bound)
+669        assert_eq!(
+670            mtud.poll_transmit(now + Duration::from_secs(600), 43),
+671            Some(5235)
+672        );
+673
+674        match mtud.state.unwrap().phase {
+675            Phase::Searching(state) => {
+676                assert_eq!(state.lower_bound, 1_471);
+677                assert_eq!(state.upper_bound, 9_000);
+678            }
+679            _ => {
+680                panic!("Unexpected MTUD phase!")
+681            }
+682        }
+683    }
+684
+685    #[test]
+686    fn mtu_discovery_lost_three_probes_lowers_probe_size() {
+687        let mut mtud = default_mtud();
+688
+689        let mut probe_sizes = (0..4).map(|i| {
+690            let probe_size = mtud.poll_transmit(Instant::now(), i);
+691            assert!(probe_size.is_some(), "no probe returned for packet {i}");
+692
+693            mtud.on_probe_lost();
+694            probe_size.unwrap()
+695        });
+696
+697        // After the first probe is lost, it gets retransmitted twice
+698        let first_probe_size = probe_sizes.next().unwrap();
+699        for _ in 0..2 {
+700            assert_eq!(probe_sizes.next().unwrap(), first_probe_size)
+701        }
+702
+703        // After the third probe is lost, we decrement our probe size
+704        let fourth_probe_size = probe_sizes.next().unwrap();
+705        assert!(fourth_probe_size < first_probe_size);
+706        assert_eq!(
+707            fourth_probe_size,
+708            first_probe_size - (first_probe_size - 1_200) / 2 - 1
+709        );
+710    }
+711
+712    #[test]
+713    fn mtu_discovery_with_peer_max_udp_payload_size_clamps_upper_bound() {
+714        let mut mtud = default_mtud();
+715
+716        mtud.on_peer_max_udp_payload_size_received(1300);
+717        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+718
+719        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1300);
+720        assert_eq!(mtud.current_mtu, 1300);
+721        let expected_probed_sizes = &[1250, 1275, 1300];
+722        assert_eq!(probed_sizes, expected_probed_sizes);
+723        assert!(completed(&mtud));
+724    }
+725
+726    #[test]
+727    fn mtu_discovery_with_previous_peer_max_udp_payload_size_clamps_upper_bound() {
+728        let mut mtud = MtuDiscovery::new(1500, 1_200, Some(1400), MtuDiscoveryConfig::default());
+729
+730        assert_eq!(mtud.current_mtu, 1400);
+731        assert_eq!(mtud.state.as_ref().unwrap().peer_max_udp_payload_size, 1400);
+732
+733        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+734
+735        assert_eq!(mtud.current_mtu, 1400);
+736        assert!(probed_sizes.is_empty());
+737        assert!(completed(&mtud));
+738    }
+739
+740    #[cfg(debug_assertions)]
+741    #[test]
+742    #[should_panic(expected = "Transport parameters received after MTU probing started")]
+743    fn mtu_discovery_with_peer_max_udp_payload_size_during_search_panics() {
+744        let mut mtud = default_mtud();
+745        assert!(mtud.poll_transmit(Instant::now(), 0).is_some());
+746        assert!(matches!(
+747            mtud.state.as_ref().unwrap().phase,
+748            Phase::Searching(_)
+749        ));
+750        mtud.on_peer_max_udp_payload_size_received(1300);
+751    }
+752
+753    #[test]
+754    fn mtu_discovery_with_1500_limit() {
+755        let mut mtud = default_mtud();
+756
+757        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+758
+759        let expected_probed_sizes = &[1326, 1389, 1420, 1452];
+760        assert_eq!(probed_sizes, expected_probed_sizes);
+761        assert_eq!(mtud.current_mtu, 1452);
+762        assert!(completed(&mtud));
+763    }
+764
+765    #[test]
+766    fn mtu_discovery_with_1500_limit_and_10000_upper_bound() {
+767        let mut config = MtuDiscoveryConfig::default();
+768        config.upper_bound(10_000);
+769        let mut mtud = MtuDiscovery::new(1_200, 1_200, None, config);
+770
+771        let probed_sizes = drive_to_completion(&mut mtud, Instant::now(), 1500);
+772
+773        let expected_probed_sizes = &[
+774            5600, 5600, 5600, 3399, 3399, 3399, 2299, 2299, 2299, 1749, 1749, 1749, 1474, 1611,
+775            1611, 1611, 1542, 1542, 1542, 1507, 1507, 1507,
+776        ];
+777        assert_eq!(probed_sizes, expected_probed_sizes);
+778        assert_eq!(mtud.current_mtu, 1474);
+779        assert!(completed(&mtud));
+780    }
+781
+782    #[test]
+783    fn mtu_discovery_no_lost_probes_finds_maximum_udp_payload() {
+784        let mut config = MtuDiscoveryConfig::default();
+785        config.upper_bound(MAX_UDP_PAYLOAD);
+786        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+787
+788        drive_to_completion(&mut mtud, Instant::now(), u16::MAX);
+789
+790        assert_eq!(mtud.current_mtu, 65527);
+791        assert!(completed(&mtud));
+792    }
+793
+794    #[test]
+795    fn mtu_discovery_lost_half_of_probes_finds_maximum_udp_payload() {
+796        let mut config = MtuDiscoveryConfig::default();
+797        config.upper_bound(MAX_UDP_PAYLOAD);
+798        let mut mtud = MtuDiscovery::new(1200, 1200, None, config);
+799
+800        let now = Instant::now();
+801        let mut iterations = 0;
+802        for i in 1..100 {
+803            iterations += 1;
+804
+805            let probe_pn = i * 2 - 1;
+806            let other_pn = i * 2;
+807
+808            let result = mtud.poll_transmit(Instant::now(), probe_pn);
+809
+810            if completed(&mtud) {
+811                break;
+812            }
+813
+814            // "Send" next probe
+815            assert!(result.is_some());
+816            assert!(mtud.in_flight_mtu_probe().is_some());
+817
+818            // Nothing else to send while the probe is in-flight
+819            assert_matches!(mtud.poll_transmit(now, other_pn), None);
+820
+821            if i % 2 == 0 {
+822                // ACK probe and ensure it results in an increase of current_mtu
+823                let previous_max_size = mtud.current_mtu;
+824                mtud.on_acked(SpaceId::Data, probe_pn, result.unwrap());
+825                println!(
+826                    "ACK packet {}. Previous MTU = {previous_max_size}. New MTU = {}",
+827                    result.unwrap(),
+828                    mtud.current_mtu
+829                );
+830                // assert!(mtud.current_mtu > previous_max_size);
+831            } else {
+832                mtud.on_probe_lost();
+833            }
+834        }
+835
+836        assert_eq!(iterations, 25);
+837        assert_eq!(mtud.current_mtu, 65527);
+838        assert!(completed(&mtud));
+839    }
+840
+841    #[test]
+842    fn search_state_lower_bound_higher_than_upper_bound_clamps_upper_bound() {
+843        let mut config = MtuDiscoveryConfig::default();
+844        config.upper_bound(1400);
+845
+846        let state = SearchState::new(1500, u16::MAX, &config);
+847        assert_eq!(state.lower_bound, 1500);
+848        assert_eq!(state.upper_bound, 1500);
+849    }
+850
+851    #[test]
+852    fn search_state_lower_bound_higher_than_peer_max_udp_payload_size_clamps_lower_bound() {
+853        let mut config = MtuDiscoveryConfig::default();
+854        config.upper_bound(9000);
+855
+856        let state = SearchState::new(1500, 1300, &config);
+857        assert_eq!(state.lower_bound, 1300);
+858        assert_eq!(state.upper_bound, 1300);
+859    }
+860
+861    #[test]
+862    fn search_state_upper_bound_higher_than_peer_max_udp_payload_size_clamps_upper_bound() {
+863        let mut config = MtuDiscoveryConfig::default();
+864        config.upper_bound(9000);
+865
+866        let state = SearchState::new(1200, 1450, &config);
+867        assert_eq!(state.lower_bound, 1200);
+868        assert_eq!(state.upper_bound, 1450);
+869    }
+870
+871    // Loss of packets larger than have been acknowledged should indicate a black hole
+872    #[test]
+873    fn simple_black_hole_detection() {
+874        let mut bhd = BlackHoleDetector::new(1200);
+875        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1300);
+876        for i in 0..BLACK_HOLE_THRESHOLD {
+877            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+878        }
+879        // But not before `BLACK_HOLE_THRESHOLD + 1` bursts
+880        assert!(!bhd.black_hole_detected());
+881        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+882        assert!(bhd.black_hole_detected());
+883    }
+884
+885    // Loss of packets followed in transmission order by confirmation of a larger packet should not
+886    // indicate a black hole
+887    #[test]
+888    fn non_suspicious_bursts() {
+889        let mut bhd = BlackHoleDetector::new(1200);
+890        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1500);
+891        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+892            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+893        }
+894        assert!(!bhd.black_hole_detected());
+895    }
+896
+897    // Loss of packets smaller than have been acknowledged previously should still indicate a black
+898    // hole
+899    #[test]
+900    fn dynamic_mtu_reduction() {
+901        let mut bhd = BlackHoleDetector::new(1200);
+902        bhd.on_non_probe_acked(0, 1500);
+903        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+904            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+905        }
+906        assert!(bhd.black_hole_detected());
+907    }
+908
+909    // Bursts containing heterogeneous packets are judged based on the smallest
+910    #[test]
+911    fn mixed_non_suspicious_bursts() {
+912        let mut bhd = BlackHoleDetector::new(1200);
+913        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+914        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+915            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+916            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1300);
+917        }
+918        assert!(!bhd.black_hole_detected());
+919    }
+920
+921    // Multi-packet bursts are only counted once
+922    #[test]
+923    fn bursts_count_once() {
+924        let mut bhd = BlackHoleDetector::new(1200);
+925        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 3, 1400);
+926        for i in 0..(BLACK_HOLE_THRESHOLD) {
+927            bhd.on_non_probe_lost(i as u64 * 3, 1500);
+928            bhd.on_non_probe_lost(i as u64 * 3 + 1, 1500);
+929        }
+930        assert!(!bhd.black_hole_detected());
+931        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 3, 1500);
+932        assert!(bhd.black_hole_detected());
+933    }
+934
+935    // Non-suspicious bursts don't interfere with detection of suspicious bursts
+936    #[test]
+937    fn interleaved_bursts() {
+938        let mut bhd = BlackHoleDetector::new(1200);
+939        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 4, 1400);
+940        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+941            bhd.on_non_probe_lost(i as u64 * 4, 1500);
+942            bhd.on_non_probe_lost(i as u64 * 4 + 2, 1300);
+943        }
+944        assert!(bhd.black_hole_detected());
+945    }
+946
+947    // Bursts that are non-suspicious before a delivered packet become suspicious past it
+948    #[test]
+949    fn suspicious_after_acked() {
+950        let mut bhd = BlackHoleDetector::new(1200);
+951        bhd.on_non_probe_acked((BLACK_HOLE_THRESHOLD + 1) as u64 * 2, 1400);
+952        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+953            bhd.on_non_probe_lost(i as u64 * 2, 1300);
+954        }
+955        assert!(
+956            !bhd.black_hole_detected(),
+957            "1300 byte losses preceding a 1400 byte delivery are not suspicious"
+958        );
+959        for i in 0..(BLACK_HOLE_THRESHOLD + 1) {
+960            bhd.on_non_probe_lost((BLACK_HOLE_THRESHOLD as u64 + 1 + i as u64) * 2, 1300);
+961        }
+962        assert!(
+963            bhd.black_hole_detected(),
+964            "1300 byte losses following a 1400 byte delivery are suspicious"
+965        );
+966    }
+967
+968    // Acknowledgment of a packet marks prior loss bursts with the same packet size as
+969    // non-suspicious
+970    #[test]
+971    fn retroactively_non_suspicious() {
+972        let mut bhd = BlackHoleDetector::new(1200);
+973        for i in 0..BLACK_HOLE_THRESHOLD {
+974            bhd.on_non_probe_lost(i as u64 * 2, 1400);
+975        }
+976        bhd.on_non_probe_acked(BLACK_HOLE_THRESHOLD as u64 * 2, 1400);
+977        bhd.on_non_probe_lost(BLACK_HOLE_THRESHOLD as u64 * 2 + 1, 1400);
+978        assert!(!bhd.black_hole_detected());
+979    }
+980}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/pacing.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/pacing.rs.html new file mode 100644 index 000000000..19fabe57a --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/pacing.rs.html @@ -0,0 +1,325 @@ +pacing.rs - source

iroh_quinn_proto/connection/
pacing.rs

1//! Pacing of packet transmissions.
+2
+3use crate::{Duration, Instant};
+4
+5use tracing::warn;
+6
+7/// A simple token-bucket pacer
+8///
+9/// The pacer's capacity is derived on a fraction of the congestion window
+10/// which can be sent in regular intervals
+11/// Once the bucket is empty, further transmission is blocked.
+12/// The bucket refills at a rate slightly faster
+13/// than one congestion window per RTT, as recommended in
+14/// <https://tools.ietf.org/html/draft-ietf-quic-recovery-34#section-7.7>
+15#[derive(Debug)]
+16pub(super) struct Pacer {
+17    capacity: u64,
+18    last_window: u64,
+19    last_mtu: u16,
+20    tokens: u64,
+21    prev: Instant,
+22}
+23
+24impl Pacer {
+25    /// Obtains a new [`Pacer`].
+26    pub(super) fn new(smoothed_rtt: Duration, window: u64, mtu: u16, now: Instant) -> Self {
+27        let capacity = optimal_capacity(smoothed_rtt, window, mtu);
+28        Self {
+29            capacity,
+30            last_window: window,
+31            last_mtu: mtu,
+32            tokens: capacity,
+33            prev: now,
+34        }
+35    }
+36
+37    /// Record that a packet has been transmitted.
+38    pub(super) fn on_transmit(&mut self, packet_length: u16) {
+39        self.tokens = self.tokens.saturating_sub(packet_length.into())
+40    }
+41
+42    /// Return how long we need to wait before sending `bytes_to_send`
+43    ///
+44    /// If we can send a packet right away, this returns `None`. Otherwise, returns `Some(d)`,
+45    /// where `d` is the time before this function should be called again.
+46    ///
+47    /// The 5/4 ratio used here comes from the suggestion that N = 1.25 in the draft IETF RFC for
+48    /// QUIC.
+49    pub(super) fn delay(
+50        &mut self,
+51        smoothed_rtt: Duration,
+52        bytes_to_send: u64,
+53        mtu: u16,
+54        window: u64,
+55        now: Instant,
+56    ) -> Option<Instant> {
+57        debug_assert_ne!(
+58            window, 0,
+59            "zero-sized congestion control window is nonsense"
+60        );
+61
+62        if window != self.last_window || mtu != self.last_mtu {
+63            self.capacity = optimal_capacity(smoothed_rtt, window, mtu);
+64
+65            // Clamp the tokens
+66            self.tokens = self.capacity.min(self.tokens);
+67            self.last_window = window;
+68            self.last_mtu = mtu;
+69        }
+70
+71        // if we can already send a packet, there is no need for delay
+72        if self.tokens >= bytes_to_send {
+73            return None;
+74        }
+75
+76        // we disable pacing for extremely large windows
+77        if window > u64::from(u32::MAX) {
+78            return None;
+79        }
+80
+81        let window = window as u32;
+82
+83        let time_elapsed = now.checked_duration_since(self.prev).unwrap_or_else(|| {
+84            warn!("received a timestamp early than a previous recorded time, ignoring");
+85            Default::default()
+86        });
+87
+88        if smoothed_rtt.as_nanos() == 0 {
+89            return None;
+90        }
+91
+92        let elapsed_rtts = time_elapsed.as_secs_f64() / smoothed_rtt.as_secs_f64();
+93        let new_tokens = (window as f64 * 1.25 * elapsed_rtts).round() as u64;
+94        self.tokens = self.tokens.saturating_add(new_tokens).min(self.capacity);
+95
+96        // In the unlikely event that we're getting polled faster than tokens are generated, ensure
+97        // that `elapsed_rtts` can grow until we make progress.
+98        if new_tokens > 0 {
+99            self.prev = now;
+100        }
+101
+102        // if we can already send a packet, there is no need for delay
+103        if self.tokens >= bytes_to_send {
+104            return None;
+105        }
+106
+107        let unscaled_delay = smoothed_rtt
+108            .checked_mul((bytes_to_send.max(self.capacity) - self.tokens) as _)
+109            .unwrap_or(Duration::MAX)
+110            / window;
+111
+112        // divisions come before multiplications to prevent overflow
+113        // this is the time at which the pacing window becomes empty
+114        Some(now + (unscaled_delay / 5) * 4)
+115    }
+116}
+117
+118/// Calculates a pacer capacity for a certain window and RTT
+119///
+120/// The goal is to emit a burst (of size `capacity`) in timer intervals
+121/// which compromise between
+122/// - ideally distributing datagrams over time
+123/// - constantly waking up the connection to produce additional datagrams
+124///
+125/// Too short burst intervals means we will never meet them since the timer
+126/// accuracy in user-space is not high enough. If we miss the interval by more
+127/// than 25%, we will lose that part of the congestion window since no additional
+128/// tokens for the extra-elapsed time can be stored.
+129///
+130/// Too long burst intervals make pacing less effective.
+131fn optimal_capacity(smoothed_rtt: Duration, window: u64, mtu: u16) -> u64 {
+132    let rtt = smoothed_rtt.as_nanos().max(1);
+133    let mtu = u64::from(mtu);
+134
+135    let target_capacity = ((window as u128 * TARGET_BURST_INTERVAL.as_nanos()) / rtt) as u64;
+136    // Never restrict capacity below one MTU.
+137    let max_capacity = Ord::max(
+138        ((window as u128 * MAX_BURST_INTERVAL.as_nanos()) / rtt) as u64,
+139        mtu,
+140    );
+141
+142    // Batch the greater of `TARGET_BURST_INTERVAL` or `MIN_BURST_SIZE` worth of traffic at a
+143    // time. To avoid inducing excessive latency, limit that result to at most `MAX_BURST_INTERVAL`
+144    // worth of traffic.
+145    Ord::min(
+146        max_capacity,
+147        target_capacity.clamp(MIN_BURST_SIZE * mtu, MAX_BURST_SIZE * mtu),
+148    )
+149}
+150
+151/// Period of traffic to batch together on a reasonably fast connection
+152const TARGET_BURST_INTERVAL: Duration = Duration::from_millis(2);
+153
+154/// Maximum period of traffic to batch together on a slow connection
+155///
+156/// Takes precedence over [`MIN_BURST_SIZE`].
+157const MAX_BURST_INTERVAL: Duration = Duration::from_millis(10);
+158
+159/// Minimum number of datagrams to batch together, so long as we won't have to wait for more than
+160/// [`MAX_BURST_INTERVAL`]
+161const MIN_BURST_SIZE: u64 = 10;
+162
+163/// Creating 256 packets took 1ms in a benchmark, so larger bursts don't make sense.
+164const MAX_BURST_SIZE: u64 = 256;
+165
+166#[cfg(test)]
+167mod tests {
+168    use super::*;
+169
+170    #[test]
+171    fn does_not_panic_on_bad_instant() {
+172        let old_instant = Instant::now();
+173        let new_instant = old_instant + Duration::from_micros(15);
+174        let rtt = Duration::from_micros(400);
+175
+176        assert!(
+177            Pacer::new(rtt, 30000, 1500, new_instant)
+178                .delay(Duration::from_micros(0), 0, 1500, 1, old_instant)
+179                .is_none()
+180        );
+181        assert!(
+182            Pacer::new(rtt, 30000, 1500, new_instant)
+183                .delay(Duration::from_micros(0), 1600, 1500, 1, old_instant)
+184                .is_none()
+185        );
+186        assert!(
+187            Pacer::new(rtt, 30000, 1500, new_instant)
+188                .delay(Duration::from_micros(0), 1500, 1500, 3000, old_instant)
+189                .is_none()
+190        );
+191    }
+192
+193    #[test]
+194    fn derives_initial_capacity() {
+195        let window = 2_000_000;
+196        let mtu = 1500;
+197        let rtt = Duration::from_millis(50);
+198        let now = Instant::now();
+199
+200        let pacer = Pacer::new(rtt, window, mtu, now);
+201        assert_eq!(
+202            pacer.capacity,
+203            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+204        );
+205        assert_eq!(pacer.tokens, pacer.capacity);
+206
+207        let pacer = Pacer::new(Duration::from_millis(0), window, mtu, now);
+208        assert_eq!(pacer.capacity, MAX_BURST_SIZE * mtu as u64);
+209        assert_eq!(pacer.tokens, pacer.capacity);
+210
+211        let pacer = Pacer::new(rtt, 1, mtu, now);
+212        assert_eq!(pacer.capacity, mtu as u64);
+213        assert_eq!(pacer.tokens, pacer.capacity);
+214    }
+215
+216    #[test]
+217    fn adjusts_capacity() {
+218        let window = 2_000_000;
+219        let mtu = 1500;
+220        let rtt = Duration::from_millis(50);
+221        let now = Instant::now();
+222
+223        let mut pacer = Pacer::new(rtt, window, mtu, now);
+224        assert_eq!(
+225            pacer.capacity,
+226            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+227        );
+228        assert_eq!(pacer.tokens, pacer.capacity);
+229        let initial_tokens = pacer.tokens;
+230
+231        pacer.delay(rtt, mtu as u64, mtu, window * 2, now);
+232        assert_eq!(
+233            pacer.capacity,
+234            (2 * window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+235        );
+236        assert_eq!(pacer.tokens, initial_tokens);
+237
+238        pacer.delay(rtt, mtu as u64, mtu, window / 2, now);
+239        assert_eq!(
+240            pacer.capacity,
+241            (window as u128 / 2 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+242        );
+243        assert_eq!(pacer.tokens, initial_tokens / 2);
+244
+245        pacer.delay(rtt, mtu as u64, mtu * 2, window, now);
+246        assert_eq!(
+247            pacer.capacity,
+248            (window as u128 * TARGET_BURST_INTERVAL.as_nanos() / rtt.as_nanos()) as u64
+249        );
+250
+251        pacer.delay(rtt, mtu as u64, 20_000, window, now);
+252        assert_eq!(pacer.capacity, 20_000_u64 * MIN_BURST_SIZE);
+253    }
+254
+255    #[test]
+256    fn computes_pause_correctly() {
+257        let window = 2_000_000u64;
+258        let mtu = 1000;
+259        let rtt = Duration::from_millis(50);
+260        let old_instant = Instant::now();
+261
+262        let mut pacer = Pacer::new(rtt, window, mtu, old_instant);
+263        let packet_capacity = pacer.capacity / mtu as u64;
+264
+265        for _ in 0..packet_capacity {
+266            assert_eq!(
+267                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+268                None,
+269                "When capacity is available packets should be sent immediately"
+270            );
+271
+272            pacer.on_transmit(mtu);
+273        }
+274
+275        let pace_duration = Duration::from_nanos((TARGET_BURST_INTERVAL.as_nanos() * 4 / 5) as u64);
+276
+277        let actual_delay = pacer
+278            .delay(rtt, mtu as u64, mtu, window, old_instant)
+279            .expect("Send must be delayed")
+280            .duration_since(old_instant);
+281
+282        let diff = actual_delay.abs_diff(pace_duration);
+283
+284        // Allow up to 2ns difference due to rounding
+285        assert!(
+286            diff < Duration::from_nanos(2),
+287            "expected ≈ {pace_duration:?}, got {actual_delay:?} (diff {diff:?})"
+288        );
+289        // Refill half of the tokens
+290        assert_eq!(
+291            pacer.delay(
+292                rtt,
+293                mtu as u64,
+294                mtu,
+295                window,
+296                old_instant + pace_duration / 2
+297            ),
+298            None
+299        );
+300        assert_eq!(pacer.tokens, pacer.capacity / 2);
+301
+302        for _ in 0..packet_capacity / 2 {
+303            assert_eq!(
+304                pacer.delay(rtt, mtu as u64, mtu, window, old_instant),
+305                None,
+306                "When capacity is available packets should be sent immediately"
+307            );
+308
+309            pacer.on_transmit(mtu);
+310        }
+311
+312        // Refill all capacity by waiting more than the expected duration
+313        assert_eq!(
+314            pacer.delay(
+315                rtt,
+316                mtu as u64,
+317                mtu,
+318                window,
+319                old_instant + pace_duration * 3 / 2
+320            ),
+321            None
+322        );
+323        assert_eq!(pacer.tokens, pacer.capacity);
+324    }
+325}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html new file mode 100644 index 000000000..ecd4ff885 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html @@ -0,0 +1,375 @@ +packet_builder.rs - source

iroh_quinn_proto/connection/
packet_builder.rs

1use bytes::{BufMut, Bytes};
+2use rand::Rng;
+3use tracing::{debug, trace, trace_span};
+4
+5use super::{Connection, PathId, SentFrames, TransmitBuf, spaces::SentPacket};
+6use crate::{
+7    ConnectionId, Instant, MIN_INITIAL_SIZE, TransportError, TransportErrorCode,
+8    connection::{ConnectionSide, qlog::QlogSentPacket},
+9    frame::{self, Close},
+10    packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId},
+11};
+12
+13/// QUIC packet builder
+14///
+15/// This allows building QUIC packets: it takes care of writing the header, allows writing
+16/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it
+17/// encrypts the packet so it is ready to be sent on the wire.
+18///
+19/// The builder manages the write buffer into which the packet is written, and directly
+20/// implements [`BufMut`] to write frames into the packet.
+21pub(super) struct PacketBuilder<'a, 'b> {
+22    pub(super) buf: &'a mut TransmitBuf<'b>,
+23    pub(super) space: SpaceId,
+24    path: PathId,
+25    pub(super) partial_encode: PartialEncode,
+26    pub(super) ack_eliciting: bool,
+27    pub(super) exact_number: u64,
+28    pub(super) short_header: bool,
+29    /// Smallest absolute position in the associated buffer that must be occupied by this packet's
+30    /// frames
+31    pub(super) min_size: usize,
+32    pub(super) tag_len: usize,
+33    pub(super) _span: tracing::span::EnteredSpan,
+34}
+35
+36impl<'a, 'b> PacketBuilder<'a, 'b> {
+37    /// Write a new packet header to `buffer` and determine the packet's properties
+38    ///
+39    /// Marks the connection drained and returns `None` if the confidentiality limit would be
+40    /// violated.
+41    pub(super) fn new(
+42        now: Instant,
+43        space_id: SpaceId,
+44        path_id: PathId,
+45        dst_cid: ConnectionId,
+46        buffer: &'a mut TransmitBuf<'b>,
+47        ack_eliciting: bool,
+48        conn: &mut Connection,
+49        #[allow(unused)] qlog: &mut QlogSentPacket,
+50    ) -> Option<Self>
+51    where
+52        'b: 'a,
+53    {
+54        let version = conn.version;
+55        // Initiate key update if we're approaching the confidentiality limit
+56        let sent_with_keys = conn.spaces[space_id].sent_with_keys();
+57        if space_id == SpaceId::Data {
+58            if sent_with_keys >= conn.key_phase_size {
+59                debug!("routine key update due to phase exhaustion");
+60                conn.force_key_update();
+61            }
+62        } else {
+63            let confidentiality_limit = conn.spaces[space_id]
+64                .crypto
+65                .as_ref()
+66                .map_or_else(
+67                    || &conn.zero_rtt_crypto.as_ref().unwrap().packet,
+68                    |keys| &keys.packet.local,
+69                )
+70                .confidentiality_limit();
+71            if sent_with_keys.saturating_add(1) == confidentiality_limit {
+72                // We still have time to attempt a graceful close
+73                conn.close_inner(
+74                    now,
+75                    Close::Connection(frame::ConnectionClose {
+76                        error_code: TransportErrorCode::AEAD_LIMIT_REACHED,
+77                        frame_type: None,
+78                        reason: Bytes::from_static(b"confidentiality limit reached"),
+79                    }),
+80                )
+81            } else if sent_with_keys > confidentiality_limit {
+82                // Confidentiality limited violated and there's nothing we can do
+83                conn.kill(
+84                    TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached").into(),
+85                );
+86                return None;
+87            }
+88        }
+89
+90        let space = &mut conn.spaces[space_id];
+91        let exact_number = space.for_path(path_id).get_tx_number(&mut conn.rng);
+92        let span = trace_span!("send", space = ?space_id, pn = exact_number, %path_id).entered();
+93
+94        let number = PacketNumber::new(
+95            exact_number,
+96            space.for_path(path_id).largest_acked_packet.unwrap_or(0),
+97        );
+98        let header = match space_id {
+99            SpaceId::Data if space.crypto.is_some() => Header::Short {
+100                dst_cid,
+101                number,
+102                spin: if conn.spin_enabled {
+103                    conn.spin
+104                } else {
+105                    conn.rng.random()
+106                },
+107                key_phase: conn.key_phase,
+108            },
+109            SpaceId::Data => Header::Long {
+110                ty: LongType::ZeroRtt,
+111                src_cid: conn.handshake_cid,
+112                dst_cid,
+113                number,
+114                version,
+115            },
+116            SpaceId::Handshake => Header::Long {
+117                ty: LongType::Handshake,
+118                src_cid: conn.handshake_cid,
+119                dst_cid,
+120                number,
+121                version,
+122            },
+123            SpaceId::Initial => Header::Initial(InitialHeader {
+124                src_cid: conn.handshake_cid,
+125                dst_cid,
+126                token: match &conn.side {
+127                    ConnectionSide::Client { token, .. } => token.clone(),
+128                    ConnectionSide::Server { .. } => Bytes::new(),
+129                },
+130                number,
+131                version,
+132            }),
+133        };
+134
+135        let partial_encode = header.encode(buffer);
+136        if conn.peer_params.grease_quic_bit && conn.rng.random() {
+137            buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT;
+138        }
+139
+140        let (sample_size, tag_len) = if let Some(ref crypto) = space.crypto {
+141            (
+142                crypto.header.local.sample_size(),
+143                crypto.packet.local.tag_len(),
+144            )
+145        } else if space_id == SpaceId::Data {
+146            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+147            (zero_rtt.header.sample_size(), zero_rtt.packet.tag_len())
+148        } else {
+149            unreachable!();
+150        };
+151
+152        // Each packet must be large enough for header protection sampling, i.e. the combined
+153        // lengths of the encoded packet number and protected payload must be at least 4 bytes
+154        // longer than the sample required for header protection. Further, each packet should be at
+155        // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the
+156        // peer may send stateless resets that are indistinguishable from regular traffic.
+157
+158        // pn_len + payload_len + tag_len >= sample_size + 4
+159        // payload_len >= sample_size + 4 - pn_len - tag_len
+160        let min_size = Ord::max(
+161            buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len),
+162            partial_encode.start + dst_cid.len() + 6,
+163        );
+164        let max_size = buffer.datagram_max_offset() - tag_len;
+165        debug_assert!(max_size >= min_size);
+166
+167        qlog.header(
+168            &header,
+169            Some(exact_number),
+170            space_id,
+171            space_id == SpaceId::Data && conn.spaces[SpaceId::Data].crypto.is_none(),
+172            path_id,
+173        );
+174
+175        Some(Self {
+176            buf: buffer,
+177            space: space_id,
+178            path: path_id,
+179            partial_encode,
+180            exact_number,
+181            short_header: header.is_short(),
+182            min_size,
+183            tag_len,
+184            ack_eliciting,
+185            _span: span,
+186        })
+187    }
+188
+189    /// Append the minimum amount of padding to the packet such that, after encryption, the
+190    /// enclosing datagram will occupy at least `min_size` bytes
+191    pub(super) fn pad_to(&mut self, min_size: u16) {
+192        // The datagram might already have a larger minimum size than the caller is requesting, if
+193        // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets
+194        // already.
+195        self.min_size = Ord::max(
+196            self.min_size,
+197            self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len,
+198        );
+199    }
+200
+201    /// Returns a writable buffer limited to the remaining frame space
+202    ///
+203    /// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of
+204    /// space available to write QUIC frames into.
+205    // In rust 1.82 we can use `-> impl BufMut + use<'_, 'a, 'b>`
+206    pub(super) fn frame_space_mut(&mut self) -> bytes::buf::Limit<&mut TransmitBuf<'b>> {
+207        self.buf.limit(self.frame_space_remaining())
+208    }
+209
+210    pub(super) fn finish_and_track(
+211        mut self,
+212        now: Instant,
+213        conn: &mut Connection,
+214        path_id: PathId,
+215        sent: SentFrames,
+216        pad_datagram: PadDatagram,
+217        qlog: QlogSentPacket,
+218    ) {
+219        match pad_datagram {
+220            PadDatagram::No => (),
+221            PadDatagram::ToSize(size) => self.pad_to(size),
+222            PadDatagram::ToSegmentSize => self.pad_to(self.buf.segment_size() as u16),
+223            PadDatagram::ToMinMtu => self.pad_to(MIN_INITIAL_SIZE),
+224        }
+225        let ack_eliciting = self.ack_eliciting;
+226        let exact_number = self.exact_number;
+227        let space_id = self.space;
+228        let (size, padded) = self.finish(conn, now, qlog);
+229
+230        let size = match padded || ack_eliciting {
+231            true => size as u16,
+232            false => 0,
+233        };
+234
+235        let packet = SentPacket {
+236            path_generation: conn.paths.get_mut(&path_id).unwrap().data.generation(),
+237            largest_acked: sent.largest_acked,
+238            time_sent: now,
+239            size,
+240            ack_eliciting,
+241            retransmits: sent.retransmits,
+242            stream_frames: sent.stream_frames,
+243        };
+244
+245        conn.paths.get_mut(&path_id).unwrap().data.sent(
+246            exact_number,
+247            packet,
+248            conn.spaces[space_id].for_path(path_id),
+249        );
+250        conn.path_stats.entry(path_id).or_default().sent_packets += 1;
+251        conn.reset_keep_alive(path_id, now);
+252        if size != 0 {
+253            if ack_eliciting {
+254                conn.spaces[space_id]
+255                    .for_path(path_id)
+256                    .time_of_last_ack_eliciting_packet = Some(now);
+257                if conn.permit_idle_reset {
+258                    conn.reset_idle_timeout(now, space_id, path_id);
+259                }
+260                conn.permit_idle_reset = false;
+261            }
+262            conn.set_loss_detection_timer(now, path_id);
+263            conn.path_data_mut(path_id).pacing.on_transmit(size);
+264        }
+265    }
+266
+267    /// Encrypt packet, returning the length of the packet and whether padding was added
+268    pub(super) fn finish(
+269        self,
+270        conn: &mut Connection,
+271        now: Instant,
+272        #[allow(unused_mut)] mut qlog: QlogSentPacket,
+273    ) -> (usize, bool) {
+274        debug_assert!(
+275            self.buf.len() <= self.buf.datagram_max_offset() - self.tag_len,
+276            "packet exceeds maximum size"
+277        );
+278        let pad = self.buf.len() < self.min_size;
+279        if pad {
+280            let padding = self.min_size - self.buf.len();
+281            trace!("PADDING * {}", padding);
+282            self.buf.put_bytes(0, padding);
+283            qlog.frame_padding(padding);
+284        }
+285
+286        let space = &conn.spaces[self.space];
+287        let (header_crypto, packet_crypto) = if let Some(ref crypto) = space.crypto {
+288            (&*crypto.header.local, &*crypto.packet.local)
+289        } else if self.space == SpaceId::Data {
+290            let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap();
+291            (&*zero_rtt.header, &*zero_rtt.packet)
+292        } else {
+293            unreachable!("tried to send {:?} packet without keys", self.space);
+294        };
+295
+296        debug_assert_eq!(
+297            packet_crypto.tag_len(),
+298            self.tag_len,
+299            "Mismatching crypto tag len"
+300        );
+301
+302        self.buf.put_bytes(0, packet_crypto.tag_len());
+303        let encode_start = self.partial_encode.start;
+304        let packet_buf = &mut self.buf.as_mut_slice()[encode_start..];
+305        // for packet protection, PathId::ZERO and no path are equivalent.
+306        self.partial_encode.finish(
+307            packet_buf,
+308            header_crypto,
+309            Some((self.exact_number, self.path, packet_crypto)),
+310        );
+311
+312        let packet_len = self.buf.len() - encode_start;
+313        trace!(size = %packet_len, short_header = %self.short_header, "wrote packet");
+314        qlog.finalize(packet_len);
+315        conn.qlog.emit_packet_sent(qlog, now);
+316        (packet_len, pad)
+317    }
+318
+319    /// The number of additional bytes the current packet would take up if it was finished now
+320    ///
+321    /// This will include any padding which is required to make the size large enough to be
+322    /// encrypted correctly.
+323    pub(super) fn predict_packet_end(&self) -> usize {
+324        self.buf.len().max(self.min_size) + self.tag_len - self.buf.len()
+325    }
+326
+327    /// Returns the remaining space in the packet that can be taken up by QUIC frames
+328    ///
+329    /// This leaves space in the datagram for the cryptographic tag that needs to be written
+330    /// when the packet is finished.
+331    pub(super) fn frame_space_remaining(&self) -> usize {
+332        let max_offset = self.buf.datagram_max_offset() - self.tag_len;
+333        max_offset.saturating_sub(self.buf.len())
+334    }
+335}
+336
+337#[derive(Debug, Copy, Clone)]
+338pub(super) enum PadDatagram {
+339    /// Do not pad the datagram
+340    No,
+341    /// To a specific size
+342    ToSize(u16),
+343    /// Pad to the current MTU/segment size
+344    ///
+345    /// For the first datagram in a transmit the MTU is the same as the
+346    /// [`TransmitBuf::segment_size`].
+347    ToSegmentSize,
+348    /// Pad to [`MIN_INITIAL_SIZE`], the minimal QUIC MTU of 1200 bytes
+349    ToMinMtu,
+350}
+351
+352impl std::ops::BitOrAssign for PadDatagram {
+353    fn bitor_assign(&mut self, rhs: Self) {
+354        *self = *self | rhs;
+355    }
+356}
+357
+358impl std::ops::BitOr for PadDatagram {
+359    type Output = Self;
+360
+361    fn bitor(self, rhs: Self) -> Self::Output {
+362        match (self, rhs) {
+363            (Self::No, rhs) => rhs,
+364            (Self::ToSize(size), Self::No) => Self::ToSize(size),
+365            (Self::ToSize(a), Self::ToSize(b)) => Self::ToSize(a.max(b)),
+366            (Self::ToSize(_), Self::ToSegmentSize) => Self::ToSegmentSize,
+367            (Self::ToSize(_), Self::ToMinMtu) => Self::ToMinMtu,
+368            (Self::ToSegmentSize, Self::No) => Self::ToSegmentSize,
+369            (Self::ToSegmentSize, Self::ToSize(_)) => Self::ToSegmentSize,
+370            (Self::ToSegmentSize, Self::ToSegmentSize) => Self::ToSegmentSize,
+371            (Self::ToSegmentSize, Self::ToMinMtu) => Self::ToMinMtu,
+372            (Self::ToMinMtu, _) => Self::ToMinMtu,
+373        }
+374    }
+375}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html new file mode 100644 index 000000000..c7ee71bf4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/packet_crypto.rs.html @@ -0,0 +1,197 @@ +packet_crypto.rs - source

iroh_quinn_proto/connection/
packet_crypto.rs

1use tracing::{debug, trace};
+2
+3use crate::Instant;
+4use crate::connection::spaces::PacketSpace;
+5use crate::crypto::{HeaderKey, KeyPair, PacketKey};
+6use crate::packet::{Packet, PartialDecode, SpaceId};
+7use crate::token::ResetToken;
+8use crate::{RESET_TOKEN_SIZE, TransportError};
+9
+10use super::PathId;
+11
+12/// Removes header protection of a packet, or returns `None` if the packet was dropped
+13pub(super) fn unprotect_header(
+14    partial_decode: PartialDecode,
+15    spaces: &[PacketSpace; 3],
+16    zero_rtt_crypto: Option<&ZeroRttCrypto>,
+17    stateless_reset_token: Option<ResetToken>,
+18) -> Option<UnprotectHeaderResult> {
+19    let header_crypto = if partial_decode.is_0rtt() {
+20        if let Some(crypto) = zero_rtt_crypto {
+21            Some(&*crypto.header)
+22        } else {
+23            debug!("dropping unexpected 0-RTT packet");
+24            return None;
+25        }
+26    } else if let Some(space) = partial_decode.space() {
+27        if let Some(ref crypto) = spaces[space].crypto {
+28            Some(&*crypto.header.remote)
+29        } else {
+30            debug!(
+31                "discarding unexpected {:?} packet ({} bytes)",
+32                space,
+33                partial_decode.len(),
+34            );
+35            return None;
+36        }
+37    } else {
+38        // Unprotected packet
+39        None
+40    };
+41
+42    let packet = partial_decode.data();
+43    let stateless_reset = packet.len() >= RESET_TOKEN_SIZE + 5
+44        && stateless_reset_token.as_deref() == Some(&packet[packet.len() - RESET_TOKEN_SIZE..]);
+45
+46    match partial_decode.finish(header_crypto) {
+47        Ok(packet) => Some(UnprotectHeaderResult {
+48            packet: Some(packet),
+49            stateless_reset,
+50        }),
+51        Err(_) if stateless_reset => Some(UnprotectHeaderResult {
+52            packet: None,
+53            stateless_reset: true,
+54        }),
+55        Err(e) => {
+56            trace!("unable to complete packet decoding: {}", e);
+57            None
+58        }
+59    }
+60}
+61
+62pub(super) struct UnprotectHeaderResult {
+63    /// The packet with the now unprotected header (`None` in the case of stateless reset packets
+64    /// that fail to be decoded)
+65    pub(super) packet: Option<Packet>,
+66    /// Whether the packet was a stateless reset packet
+67    pub(super) stateless_reset: bool,
+68}
+69
+70/// Decrypts a packet's body in-place
+71pub(super) fn decrypt_packet_body(
+72    packet: &mut Packet,
+73    path_id: PathId,
+74    spaces: &[PacketSpace; 3],
+75    zero_rtt_crypto: Option<&ZeroRttCrypto>,
+76    conn_key_phase: bool,
+77    prev_crypto: Option<&PrevCrypto>,
+78    next_crypto: Option<&KeyPair<Box<dyn PacketKey>>>,
+79) -> Result<Option<DecryptPacketResult>, Option<TransportError>> {
+80    if !packet.header.is_protected() {
+81        // Unprotected packets also don't have packet numbers
+82        return Ok(None);
+83    }
+84    let space = packet.header.space();
+85
+86    if path_id != PathId::ZERO && space != SpaceId::Data {
+87        // do not try to decrypt illegal multipath packets
+88        return Err(Some(TransportError::PROTOCOL_VIOLATION(
+89            "multipath packet on non Data packet number space",
+90        )));
+91    }
+92    // Packets that do not belong to known path ids are valid as long as they can be decrypted.
+93    // If we didn't have a path, that's for the purposes of this function equivalent to not
+94    // having received packets on that path yet. So both of these cases are represented by `None`.
+95    let rx_packet = spaces[space]
+96        .path_space(path_id)
+97        .and_then(|space| space.rx_packet);
+98    let number = packet
+99        .header
+100        .number()
+101        .ok_or(None)?
+102        .expand(rx_packet.map(|n| n + 1).unwrap_or_default());
+103    let packet_key_phase = packet.header.key_phase();
+104
+105    let mut crypto_update = false;
+106    let crypto = if packet.header.is_0rtt() {
+107        &zero_rtt_crypto.unwrap().packet
+108    } else if packet_key_phase == conn_key_phase || space != SpaceId::Data {
+109        &spaces[space].crypto.as_ref().unwrap().packet.remote
+110    } else if let Some(prev) = prev_crypto.and_then(|crypto| {
+111        // If this packet comes prior to acknowledgment of the key update by the peer,
+112        if crypto.end_packet.is_none_or(|(pn, _)| number < pn) {
+113            // use the previous keys.
+114            Some(crypto)
+115        } else {
+116            // Otherwise, this must be a remotely-initiated key update, so fall through to the
+117            // final case.
+118            None
+119        }
+120    }) {
+121        &prev.crypto.remote
+122    } else {
+123        // We're in the Data space with a key phase mismatch and either there is no locally
+124        // initiated key update or the locally initiated key update was acknowledged by a
+125        // lower-numbered packet. The key phase mismatch must therefore represent a new
+126        // remotely-initiated key update.
+127        crypto_update = true;
+128        &next_crypto.unwrap().remote
+129    };
+130
+131    crypto
+132        .decrypt(path_id, number, &packet.header_data, &mut packet.payload)
+133        .map_err(|_| {
+134            trace!("decryption failed with packet number {}", number);
+135            None
+136        })?;
+137
+138    if !packet.reserved_bits_valid() {
+139        return Err(Some(TransportError::PROTOCOL_VIOLATION(
+140            "reserved bits set",
+141        )));
+142    }
+143
+144    let mut outgoing_key_update_acked = false;
+145    if let Some(prev) = prev_crypto {
+146        if prev.end_packet.is_none() && packet_key_phase == conn_key_phase {
+147            outgoing_key_update_acked = true;
+148        }
+149    }
+150
+151    if crypto_update {
+152        // Validate incoming key update
+153        // If `rx_packet` is `None`, then either the path is entirely new, or we haven't received
+154        // any packets on this path yet. In that case, having the first packet be a crypto update
+155        // is fine.
+156        let invalid_packet_number = rx_packet.is_some_and(|rx_packet| number <= rx_packet);
+157        if invalid_packet_number || prev_crypto.is_some_and(|x| x.update_unacked) {
+158            trace!(?number, ?rx_packet, %path_id, "crypto update failed");
+159            return Err(Some(TransportError::KEY_UPDATE_ERROR("")));
+160        }
+161    }
+162
+163    Ok(Some(DecryptPacketResult {
+164        number,
+165        outgoing_key_update_acked,
+166        incoming_key_update: crypto_update,
+167    }))
+168}
+169
+170pub(super) struct DecryptPacketResult {
+171    /// The packet number
+172    pub(super) number: u64,
+173    /// Whether a locally initiated key update has been acknowledged by the peer
+174    pub(super) outgoing_key_update_acked: bool,
+175    /// Whether the peer has initiated a key update
+176    pub(super) incoming_key_update: bool,
+177}
+178
+179pub(super) struct PrevCrypto {
+180    /// The keys used for the previous key phase, temporarily retained to decrypt packets sent by
+181    /// the peer prior to its own key update.
+182    pub(super) crypto: KeyPair<Box<dyn PacketKey>>,
+183    /// The incoming packet that ends the interval for which these keys are applicable, and the time
+184    /// of its receipt.
+185    ///
+186    /// Incoming packets should be decrypted using these keys iff this is `None` or their packet
+187    /// number is lower. `None` indicates that we have not yet received a packet using newer keys,
+188    /// which implies that the update was locally initiated.
+189    pub(super) end_packet: Option<(u64, Instant)>,
+190    /// Whether the following key phase is from a remotely initiated update that we haven't acked
+191    pub(super) update_unacked: bool,
+192}
+193
+194pub(super) struct ZeroRttCrypto {
+195    pub(super) header: Box<dyn HeaderKey>,
+196    pub(super) packet: Box<dyn PacketKey>,
+197}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/paths.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/paths.rs.html new file mode 100644 index 000000000..9092bb1c5 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/paths.rs.html @@ -0,0 +1,889 @@ +paths.rs - source

iroh_quinn_proto/connection/
paths.rs

1use std::{cmp, net::SocketAddr};
+2
+3use identity_hash::IntMap;
+4use thiserror::Error;
+5use tracing::{debug, trace};
+6
+7use super::{
+8    PathError, PathStats,
+9    mtud::MtuDiscovery,
+10    pacing::Pacer,
+11    spaces::{PacketNumberSpace, SentPacket},
+12};
+13use crate::{
+14    ConnectionId, Duration, Instant, TIMER_GRANULARITY, TransportConfig, VarInt, coding,
+15    congestion, frame::ObservedAddr, packet::SpaceId,
+16};
+17
+18#[cfg(feature = "qlog")]
+19use qlog::events::quic::RecoveryMetricsUpdated;
+20
+21/// Id representing different paths when using multipath extension
+22#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+23pub struct PathId(pub(crate) u32);
+24
+25impl std::hash::Hash for PathId {
+26    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+27        state.write_u32(self.0);
+28    }
+29}
+30
+31impl identity_hash::IdentityHashable for PathId {}
+32
+33impl coding::Codec for PathId {
+34    fn decode<B: bytes::Buf>(r: &mut B) -> coding::Result<Self> {
+35        let v = VarInt::decode(r)?;
+36        let v = u32::try_from(v.0).map_err(|_| coding::UnexpectedEnd)?;
+37        Ok(Self(v))
+38    }
+39
+40    fn encode<B: bytes::BufMut>(&self, w: &mut B) {
+41        VarInt(self.0.into()).encode(w)
+42    }
+43}
+44
+45impl PathId {
+46    /// The maximum path ID allowed.
+47    pub const MAX: Self = Self(u32::MAX);
+48
+49    /// The 0 path id.
+50    pub const ZERO: Self = Self(0);
+51
+52    /// The number of bytes this [`PathId`] uses when encoded as a [`VarInt`]
+53    pub(crate) const fn size(&self) -> usize {
+54        VarInt(self.0 as u64).size()
+55    }
+56
+57    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+58    /// of overflowing.
+59    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+60        let rhs = rhs.into();
+61        let inner = self.0.saturating_add(rhs.0);
+62        Self(inner)
+63    }
+64
+65    /// Saturating integer subtraction. Computes self - rhs, saturating at the numeric bounds
+66    /// instead of overflowing.
+67    pub fn saturating_sub(self, rhs: impl Into<Self>) -> Self {
+68        let rhs = rhs.into();
+69        let inner = self.0.saturating_sub(rhs.0);
+70        Self(inner)
+71    }
+72
+73    /// Get the next [`PathId`]
+74    pub(crate) fn next(&self) -> Self {
+75        self.saturating_add(Self(1))
+76    }
+77
+78    /// Get the underlying u32
+79    pub(crate) fn as_u32(&self) -> u32 {
+80        self.0
+81    }
+82}
+83
+84impl std::fmt::Display for PathId {
+85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+86        self.0.fmt(f)
+87    }
+88}
+89
+90impl<T: Into<u32>> From<T> for PathId {
+91    fn from(source: T) -> Self {
+92        Self(source.into())
+93    }
+94}
+95
+96/// State needed for a single path ID.
+97///
+98/// A single path ID can migrate according to the rules in RFC9000 §9, either voluntary or
+99/// involuntary. We need to keep the [`PathData`] of the previously used such path available
+100/// in order to defend against migration attacks (see RFC9000 §9.3.1, §9.3.2 and §9.3.3) as
+101/// well as to support path probing (RFC9000 §9.1).
+102#[derive(Debug)]
+103pub(super) struct PathState {
+104    pub(super) data: PathData,
+105    pub(super) prev: Option<(ConnectionId, PathData)>,
+106}
+107
+108impl PathState {
+109    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+110    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) {
+111        // Visit known paths from newest to oldest to find the one `pn` was sent on
+112        for path_data in [&mut self.data]
+113            .into_iter()
+114            .chain(self.prev.as_mut().map(|(_, data)| data))
+115        {
+116            if path_data.remove_in_flight(packet) {
+117                return;
+118            }
+119        }
+120    }
+121}
+122
+123#[derive(Debug)]
+124pub(super) struct SentChallengeInfo {
+125    /// When was the challenge sent on the wire.
+126    pub(super) sent_instant: Instant,
+127    /// The remote to which this path challenge was sent.
+128    pub(super) remote: SocketAddr,
+129}
+130
+131/// Description of a particular network path
+132#[derive(Debug)]
+133pub(super) struct PathData {
+134    pub(super) remote: SocketAddr,
+135    pub(super) rtt: RttEstimator,
+136    /// Whether we're enabling ECN on outgoing packets
+137    pub(super) sending_ecn: bool,
+138    /// Congestion controller state
+139    pub(super) congestion: Box<dyn congestion::Controller>,
+140    /// Pacing state
+141    pub(super) pacing: Pacer,
+142    /// Actually sent challenges (on the wire).
+143    pub(super) challenges_sent: IntMap<u64, SentChallengeInfo>,
+144    /// Whether to *immediately* trigger another PATH_CHALLENGE (via [`super::Connection::can_send`])
+145    pub(super) send_new_challenge: bool,
+146    /// Pending responses to PATH_CHALLENGE frames
+147    pub(super) path_responses: PathResponses,
+148    /// Whether we're certain the peer can both send and receive on this address
+149    ///
+150    /// Initially equal to `use_stateless_retry` for servers, and becomes false again on every
+151    /// migration. Always true for clients.
+152    pub(super) validated: bool,
+153    /// Total size of all UDP datagrams sent on this path
+154    pub(super) total_sent: u64,
+155    /// Total size of all UDP datagrams received on this path
+156    pub(super) total_recvd: u64,
+157    /// The state of the MTU discovery process
+158    pub(super) mtud: MtuDiscovery,
+159    /// Packet number of the first packet sent after an RTT sample was collected on this path
+160    ///
+161    /// Used in persistent congestion determination.
+162    pub(super) first_packet_after_rtt_sample: Option<(SpaceId, u64)>,
+163    /// The in-flight packets and bytes
+164    ///
+165    /// Note that this is across all spaces on this path
+166    pub(super) in_flight: InFlight,
+167    /// Whether this path has had it's remote address reported back to the peer. This only happens
+168    /// if both peers agree to so based on their transport parameters.
+169    pub(super) observed_addr_sent: bool,
+170    /// Observed address frame with the largest sequence number received from the peer on this path.
+171    pub(super) last_observed_addr_report: Option<ObservedAddr>,
+172    /// The QUIC-MULTIPATH path status
+173    pub(super) status: PathStatusState,
+174    /// Number of the first packet sent on this path
+175    ///
+176    /// With RFC9000 §9 style migration (i.e. not multipath) the PathId does not change and
+177    /// hence packet numbers continue. This is used to determine whether a packet was sent
+178    /// on such an earlier path. Insufficient to determine if a packet was sent on a later
+179    /// path.
+180    first_packet: Option<u64>,
+181    /// The number of times a PTO has been sent without receiving an ack.
+182    pub(super) pto_count: u32,
+183
+184    //
+185    // Per-path idle & keep alive
+186    //
+187    /// Idle timeout for the path
+188    ///
+189    /// If expired, the path will be abandoned.  This is different from the connection-wide
+190    /// idle timeout which closes the connection if expired.
+191    pub(super) idle_timeout: Option<Duration>,
+192    /// Keep alives to send on this path
+193    ///
+194    /// There is also a connection-level keep alive configured in the
+195    /// [`TransportParameters`].  This triggers activity on any path which can keep the
+196    /// connection alive.
+197    ///
+198    /// [`TransportParameters`]: crate::transport_parameters::TransportParameters
+199    pub(super) keep_alive: Option<Duration>,
+200
+201    /// Whether the path has already been considered opened from an application perspective
+202    ///
+203    /// This means, for paths other than the original [`PathId::ZERO`], a first path challenge has
+204    /// been responded to, regardless of the initial validation status of the path. This state is
+205    /// irreversible, since it's not affected by the path being closed.
+206    pub(super) open: bool,
+207
+208    /// The time at which this path state should've received a PATH_ABANDON already.
+209    ///
+210    /// Receiving data on this path generates a transport error after that point in time.
+211    ///
+212    /// If set to `None`, then this path isn't abandoned yet and is allowed to receive data.
+213    pub(super) last_allowed_receive: Option<Instant>,
+214
+215    /// Snapshot of the qlog recovery metrics
+216    #[cfg(feature = "qlog")]
+217    recovery_metrics: RecoveryMetrics,
+218
+219    /// Tag uniquely identifying a path in a connection
+220    generation: u64,
+221}
+222
+223impl PathData {
+224    pub(super) fn new(
+225        remote: SocketAddr,
+226        allow_mtud: bool,
+227        peer_max_udp_payload_size: Option<u16>,
+228        generation: u64,
+229        now: Instant,
+230        config: &TransportConfig,
+231    ) -> Self {
+232        let congestion = config
+233            .congestion_controller_factory
+234            .clone()
+235            .build(now, config.get_initial_mtu());
+236        Self {
+237            remote,
+238            rtt: RttEstimator::new(config.initial_rtt),
+239            sending_ecn: true,
+240            pacing: Pacer::new(
+241                config.initial_rtt,
+242                congestion.initial_window(),
+243                config.get_initial_mtu(),
+244                now,
+245            ),
+246            congestion,
+247            challenges_sent: Default::default(),
+248            send_new_challenge: false,
+249            path_responses: PathResponses::default(),
+250            validated: false,
+251            total_sent: 0,
+252            total_recvd: 0,
+253            mtud: config
+254                .mtu_discovery_config
+255                .as_ref()
+256                .filter(|_| allow_mtud)
+257                .map_or(
+258                    MtuDiscovery::disabled(config.get_initial_mtu(), config.min_mtu),
+259                    |mtud_config| {
+260                        MtuDiscovery::new(
+261                            config.get_initial_mtu(),
+262                            config.min_mtu,
+263                            peer_max_udp_payload_size,
+264                            mtud_config.clone(),
+265                        )
+266                    },
+267                ),
+268            first_packet_after_rtt_sample: None,
+269            in_flight: InFlight::new(),
+270            observed_addr_sent: false,
+271            last_observed_addr_report: None,
+272            status: Default::default(),
+273            first_packet: None,
+274            pto_count: 0,
+275            idle_timeout: None,
+276            keep_alive: None,
+277            open: false,
+278            last_allowed_receive: None,
+279            #[cfg(feature = "qlog")]
+280            recovery_metrics: RecoveryMetrics::default(),
+281            generation,
+282        }
+283    }
+284
+285    /// Create a new path from a previous one.
+286    ///
+287    /// This should only be called when migrating paths.
+288    pub(super) fn from_previous(
+289        remote: SocketAddr,
+290        prev: &Self,
+291        generation: u64,
+292        now: Instant,
+293    ) -> Self {
+294        let congestion = prev.congestion.clone_box();
+295        let smoothed_rtt = prev.rtt.get();
+296        Self {
+297            remote,
+298            rtt: prev.rtt,
+299            pacing: Pacer::new(smoothed_rtt, congestion.window(), prev.current_mtu(), now),
+300            sending_ecn: true,
+301            congestion,
+302            challenges_sent: Default::default(),
+303            send_new_challenge: false,
+304            path_responses: PathResponses::default(),
+305            validated: false,
+306            total_sent: 0,
+307            total_recvd: 0,
+308            mtud: prev.mtud.clone(),
+309            first_packet_after_rtt_sample: prev.first_packet_after_rtt_sample,
+310            in_flight: InFlight::new(),
+311            observed_addr_sent: false,
+312            last_observed_addr_report: None,
+313            status: prev.status.clone(),
+314            first_packet: None,
+315            pto_count: 0,
+316            idle_timeout: prev.idle_timeout,
+317            keep_alive: prev.keep_alive,
+318            open: false,
+319            last_allowed_receive: None,
+320            #[cfg(feature = "qlog")]
+321            recovery_metrics: prev.recovery_metrics.clone(),
+322            generation,
+323        }
+324    }
+325
+326    /// Whether we're in the process of validating this path with PATH_CHALLENGEs
+327    pub(super) fn is_validating_path(&self) -> bool {
+328        !self.challenges_sent.is_empty() || self.send_new_challenge
+329    }
+330
+331    /// Indicates whether we're a server that hasn't validated the peer's address and hasn't
+332    /// received enough data from the peer to permit sending `bytes_to_send` additional bytes
+333    pub(super) fn anti_amplification_blocked(&self, bytes_to_send: u64) -> bool {
+334        !self.validated && self.total_recvd * 3 < self.total_sent + bytes_to_send
+335    }
+336
+337    /// Returns the path's current MTU
+338    pub(super) fn current_mtu(&self) -> u16 {
+339        self.mtud.current_mtu()
+340    }
+341
+342    /// Account for transmission of `packet` with number `pn` in `space`
+343    pub(super) fn sent(&mut self, pn: u64, packet: SentPacket, space: &mut PacketNumberSpace) {
+344        self.in_flight.insert(&packet);
+345        if self.first_packet.is_none() {
+346            self.first_packet = Some(pn);
+347        }
+348        if let Some(forgotten) = space.sent(pn, packet) {
+349            self.remove_in_flight(&forgotten);
+350        }
+351    }
+352
+353    /// Remove `packet` with number `pn` from this path's congestion control counters, or return
+354    /// `false` if `pn` was sent before this path was established.
+355    pub(super) fn remove_in_flight(&mut self, packet: &SentPacket) -> bool {
+356        if packet.path_generation != self.generation {
+357            return false;
+358        }
+359        self.in_flight.remove(packet);
+360        true
+361    }
+362
+363    /// Increment the total size of sent UDP datagrams
+364    pub(super) fn inc_total_sent(&mut self, inc: u64) {
+365        self.total_sent = self.total_sent.saturating_add(inc);
+366        if !self.validated {
+367            trace!(
+368                remote = %self.remote,
+369                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+370                "anti amplification budget decreased"
+371            );
+372        }
+373    }
+374
+375    /// Increment the total size of received UDP datagrams
+376    pub(super) fn inc_total_recvd(&mut self, inc: u64) {
+377        self.total_recvd = self.total_recvd.saturating_add(inc);
+378        if !self.validated {
+379            trace!(
+380                remote = %self.remote,
+381                anti_amplification_budget = %(self.total_recvd * 3).saturating_sub(self.total_sent),
+382                "anti amplification budget increased"
+383            );
+384        }
+385    }
+386
+387    #[cfg(feature = "qlog")]
+388    pub(super) fn qlog_recovery_metrics(
+389        &mut self,
+390        path_id: PathId,
+391    ) -> Option<RecoveryMetricsUpdated> {
+392        let controller_metrics = self.congestion.metrics();
+393
+394        let metrics = RecoveryMetrics {
+395            min_rtt: Some(self.rtt.min),
+396            smoothed_rtt: Some(self.rtt.get()),
+397            latest_rtt: Some(self.rtt.latest),
+398            rtt_variance: Some(self.rtt.var),
+399            pto_count: Some(self.pto_count),
+400            bytes_in_flight: Some(self.in_flight.bytes),
+401            packets_in_flight: Some(self.in_flight.ack_eliciting),
+402
+403            congestion_window: Some(controller_metrics.congestion_window),
+404            ssthresh: controller_metrics.ssthresh,
+405            pacing_rate: controller_metrics.pacing_rate,
+406        };
+407
+408        let event = metrics.to_qlog_event(path_id, &self.recovery_metrics);
+409        self.recovery_metrics = metrics;
+410        event
+411    }
+412
+413    /// Return how long we need to wait before sending `bytes_to_send`
+414    ///
+415    /// See [`Pacer::delay`].
+416    pub(super) fn pacing_delay(&mut self, bytes_to_send: u64, now: Instant) -> Option<Instant> {
+417        let smoothed_rtt = self.rtt.get();
+418        self.pacing.delay(
+419            smoothed_rtt,
+420            bytes_to_send,
+421            self.current_mtu(),
+422            self.congestion.window(),
+423            now,
+424        )
+425    }
+426
+427    /// Updates the last observed address report received on this path.
+428    ///
+429    /// If the address was updated, it's returned to be informed to the application.
+430    #[must_use = "updated observed address must be reported to the application"]
+431    pub(super) fn update_observed_addr_report(
+432        &mut self,
+433        observed: ObservedAddr,
+434    ) -> Option<SocketAddr> {
+435        match self.last_observed_addr_report.as_mut() {
+436            Some(prev) => {
+437                if prev.seq_no >= observed.seq_no {
+438                    // frames that do not increase the sequence number on this path are ignored
+439                    None
+440                } else if prev.ip == observed.ip && prev.port == observed.port {
+441                    // keep track of the last seq_no but do not report the address as updated
+442                    prev.seq_no = observed.seq_no;
+443                    None
+444                } else {
+445                    let addr = observed.socket_addr();
+446                    self.last_observed_addr_report = Some(observed);
+447                    Some(addr)
+448                }
+449            }
+450            None => {
+451                let addr = observed.socket_addr();
+452                self.last_observed_addr_report = Some(observed);
+453                Some(addr)
+454            }
+455        }
+456    }
+457
+458    pub(crate) fn remote_status(&self) -> Option<PathStatus> {
+459        self.status.remote_status.map(|(_seq, status)| status)
+460    }
+461
+462    pub(crate) fn local_status(&self) -> PathStatus {
+463        self.status.local_status
+464    }
+465
+466    pub(super) fn generation(&self) -> u64 {
+467        self.generation
+468    }
+469}
+470
+471/// Congestion metrics as described in [`recovery_metrics_updated`].
+472///
+473/// [`recovery_metrics_updated`]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-quic-events.html#name-recovery_metrics_updated
+474#[cfg(feature = "qlog")]
+475#[derive(Default, Clone, PartialEq, Debug)]
+476#[non_exhaustive]
+477struct RecoveryMetrics {
+478    pub min_rtt: Option<Duration>,
+479    pub smoothed_rtt: Option<Duration>,
+480    pub latest_rtt: Option<Duration>,
+481    pub rtt_variance: Option<Duration>,
+482    pub pto_count: Option<u32>,
+483    pub bytes_in_flight: Option<u64>,
+484    pub packets_in_flight: Option<u64>,
+485    pub congestion_window: Option<u64>,
+486    pub ssthresh: Option<u64>,
+487    pub pacing_rate: Option<u64>,
+488}
+489
+490#[cfg(feature = "qlog")]
+491impl RecoveryMetrics {
+492    /// Retain only values that have been updated since the last snapshot.
+493    fn retain_updated(&self, previous: &Self) -> Self {
+494        macro_rules! keep_if_changed {
+495            ($name:ident) => {
+496                if previous.$name == self.$name {
+497                    None
+498                } else {
+499                    self.$name
+500                }
+501            };
+502        }
+503
+504        Self {
+505            min_rtt: keep_if_changed!(min_rtt),
+506            smoothed_rtt: keep_if_changed!(smoothed_rtt),
+507            latest_rtt: keep_if_changed!(latest_rtt),
+508            rtt_variance: keep_if_changed!(rtt_variance),
+509            pto_count: keep_if_changed!(pto_count),
+510            bytes_in_flight: keep_if_changed!(bytes_in_flight),
+511            packets_in_flight: keep_if_changed!(packets_in_flight),
+512            congestion_window: keep_if_changed!(congestion_window),
+513            ssthresh: keep_if_changed!(ssthresh),
+514            pacing_rate: keep_if_changed!(pacing_rate),
+515        }
+516    }
+517
+518    /// Emit a `MetricsUpdated` event containing only updated values
+519    fn to_qlog_event(&self, path_id: PathId, previous: &Self) -> Option<RecoveryMetricsUpdated> {
+520        let updated = self.retain_updated(previous);
+521
+522        if updated == Self::default() {
+523            return None;
+524        }
+525
+526        Some(RecoveryMetricsUpdated {
+527            min_rtt: updated.min_rtt.map(|rtt| rtt.as_secs_f32()),
+528            smoothed_rtt: updated.smoothed_rtt.map(|rtt| rtt.as_secs_f32()),
+529            latest_rtt: updated.latest_rtt.map(|rtt| rtt.as_secs_f32()),
+530            rtt_variance: updated.rtt_variance.map(|rtt| rtt.as_secs_f32()),
+531            pto_count: updated
+532                .pto_count
+533                .map(|count| count.try_into().unwrap_or(u16::MAX)),
+534            bytes_in_flight: updated.bytes_in_flight,
+535            packets_in_flight: updated.packets_in_flight,
+536            congestion_window: updated.congestion_window,
+537            ssthresh: updated.ssthresh,
+538            pacing_rate: updated.pacing_rate,
+539            path_id: Some(path_id.as_u32() as u64),
+540        })
+541    }
+542}
+543
+544/// RTT estimation for a particular network path
+545#[derive(Copy, Clone, Debug)]
+546pub struct RttEstimator {
+547    /// The most recent RTT measurement made when receiving an ack for a previously unacked packet
+548    latest: Duration,
+549    /// The smoothed RTT of the connection, computed as described in RFC6298
+550    smoothed: Option<Duration>,
+551    /// The RTT variance, computed as described in RFC6298
+552    var: Duration,
+553    /// The minimum RTT seen in the connection, ignoring ack delay.
+554    min: Duration,
+555}
+556
+557impl RttEstimator {
+558    pub(super) fn new(initial_rtt: Duration) -> Self {
+559        Self {
+560            latest: initial_rtt,
+561            smoothed: None,
+562            var: initial_rtt / 2,
+563            min: initial_rtt,
+564        }
+565    }
+566
+567    /// Resets the estimator using a new initial_rtt value.
+568    ///
+569    /// This only resets the initial_rtt **if** no samples have been recorded yet. If there
+570    /// are any recorded samples the initial estimate can not be adjusted after the fact.
+571    ///
+572    /// This is useful when you receive a PATH_RESPONSE in the first packet received on a
+573    /// new path. In this case you can use the delay of the PATH_CHALLENGE-PATH_RESPONSE as
+574    /// the initial RTT to get a better expected estimation.
+575    ///
+576    /// A PATH_CHALLENGE-PATH_RESPONSE pair later in the connection should not be used
+577    /// explicitly as an estimation since PATH_CHALLENGE is an ACK-eliciting packet itself
+578    /// already.
+579    pub(crate) fn reset_initial_rtt(&mut self, initial_rtt: Duration) {
+580        if self.smoothed.is_none() {
+581            self.latest = initial_rtt;
+582            self.var = initial_rtt / 2;
+583            self.min = initial_rtt;
+584        }
+585    }
+586
+587    /// The current best RTT estimation.
+588    pub fn get(&self) -> Duration {
+589        self.smoothed.unwrap_or(self.latest)
+590    }
+591
+592    /// Conservative estimate of RTT
+593    ///
+594    /// Takes the maximum of smoothed and latest RTT, as recommended
+595    /// in 6.1.2 of the recovery spec (draft 29).
+596    pub fn conservative(&self) -> Duration {
+597        self.get().max(self.latest)
+598    }
+599
+600    /// Minimum RTT registered so far for this estimator.
+601    pub fn min(&self) -> Duration {
+602        self.min
+603    }
+604
+605    /// PTO computed as described in RFC9002#6.2.1.
+606    pub(crate) fn pto_base(&self) -> Duration {
+607        self.get() + cmp::max(4 * self.var, TIMER_GRANULARITY)
+608    }
+609
+610    /// Records an RTT sample.
+611    pub(crate) fn update(&mut self, ack_delay: Duration, rtt: Duration) {
+612        self.latest = rtt;
+613        // https://www.rfc-editor.org/rfc/rfc9002.html#section-5.2-3:
+614        // min_rtt does not adjust for ack_delay to avoid underestimating.
+615        self.min = cmp::min(self.min, self.latest);
+616        // Based on RFC6298.
+617        if let Some(smoothed) = self.smoothed {
+618            let adjusted_rtt = if self.min + ack_delay <= self.latest {
+619                self.latest - ack_delay
+620            } else {
+621                self.latest
+622            };
+623            let var_sample = smoothed.abs_diff(adjusted_rtt);
+624            self.var = (3 * self.var + var_sample) / 4;
+625            self.smoothed = Some((7 * smoothed + adjusted_rtt) / 8);
+626        } else {
+627            self.smoothed = Some(self.latest);
+628            self.var = self.latest / 2;
+629            self.min = self.latest;
+630        }
+631    }
+632}
+633
+634#[derive(Default, Debug)]
+635pub(crate) struct PathResponses {
+636    pending: Vec<PathResponse>,
+637}
+638
+639impl PathResponses {
+640    pub(crate) fn push(&mut self, packet: u64, token: u64, remote: SocketAddr) {
+641        /// Arbitrary permissive limit to prevent abuse
+642        const MAX_PATH_RESPONSES: usize = 16;
+643        let response = PathResponse {
+644            packet,
+645            token,
+646            remote,
+647        };
+648        let existing = self.pending.iter_mut().find(|x| x.remote == remote);
+649        if let Some(existing) = existing {
+650            // Update a queued response
+651            if existing.packet <= packet {
+652                *existing = response;
+653            }
+654            return;
+655        }
+656        if self.pending.len() < MAX_PATH_RESPONSES {
+657            self.pending.push(response);
+658        } else {
+659            // We don't expect to ever hit this with well-behaved peers, so we don't bother dropping
+660            // older challenges.
+661            trace!("ignoring excessive PATH_CHALLENGE");
+662        }
+663    }
+664
+665    pub(crate) fn pop_off_path(&mut self, remote: SocketAddr) -> Option<(u64, SocketAddr)> {
+666        let response = *self.pending.last()?;
+667        if response.remote == remote {
+668            // We don't bother searching further because we expect that the on-path response will
+669            // get drained in the immediate future by a call to `pop_on_path`
+670            return None;
+671        }
+672        self.pending.pop();
+673        Some((response.token, response.remote))
+674    }
+675
+676    pub(crate) fn pop_on_path(&mut self, remote: SocketAddr) -> Option<u64> {
+677        let response = *self.pending.last()?;
+678        if response.remote != remote {
+679            // We don't bother searching further because we expect that the off-path response will
+680            // get drained in the immediate future by a call to `pop_off_path`
+681            return None;
+682        }
+683        self.pending.pop();
+684        Some(response.token)
+685    }
+686
+687    pub(crate) fn is_empty(&self) -> bool {
+688        self.pending.is_empty()
+689    }
+690}
+691
+692#[derive(Copy, Clone, Debug)]
+693struct PathResponse {
+694    /// The packet number the corresponding PATH_CHALLENGE was received in
+695    packet: u64,
+696    /// The token of the PATH_CHALLENGE
+697    token: u64,
+698    /// The address the corresponding PATH_CHALLENGE was received from
+699    remote: SocketAddr,
+700}
+701
+702/// Summary statistics of packets that have been sent on a particular path, but which have not yet
+703/// been acked or deemed lost
+704#[derive(Debug)]
+705pub(super) struct InFlight {
+706    /// Sum of the sizes of all sent packets considered "in flight" by congestion control
+707    ///
+708    /// The size does not include IP or UDP overhead. Packets only containing ACK frames do not
+709    /// count towards this to ensure congestion control does not impede congestion feedback.
+710    pub(super) bytes: u64,
+711    /// Number of packets in flight containing frames other than ACK and PADDING
+712    ///
+713    /// This can be 0 even when bytes is not 0 because PADDING frames cause a packet to be
+714    /// considered "in flight" by congestion control. However, if this is nonzero, bytes will always
+715    /// also be nonzero.
+716    pub(super) ack_eliciting: u64,
+717}
+718
+719impl InFlight {
+720    fn new() -> Self {
+721        Self {
+722            bytes: 0,
+723            ack_eliciting: 0,
+724        }
+725    }
+726
+727    fn insert(&mut self, packet: &SentPacket) {
+728        self.bytes += u64::from(packet.size);
+729        self.ack_eliciting += u64::from(packet.ack_eliciting);
+730    }
+731
+732    /// Update counters to account for a packet becoming acknowledged, lost, or abandoned
+733    fn remove(&mut self, packet: &SentPacket) {
+734        self.bytes -= u64::from(packet.size);
+735        self.ack_eliciting -= u64::from(packet.ack_eliciting);
+736    }
+737}
+738
+739/// State for QUIC-MULTIPATH PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP frames
+740#[derive(Debug, Clone, Default)]
+741pub(super) struct PathStatusState {
+742    /// The local status
+743    local_status: PathStatus,
+744    /// Local sequence number, for both PATH_STATUS_AVAILABLE and PATH_STATUS_BACKUP
+745    ///
+746    /// This is the number of the *next* path status frame to be sent.
+747    local_seq: VarInt,
+748    /// The status set by the remote
+749    remote_status: Option<(VarInt, PathStatus)>,
+750}
+751
+752impl PathStatusState {
+753    /// To be called on received PATH_STATUS_AVAILABLE/PATH_STATUS_BACKUP frames
+754    pub(super) fn remote_update(&mut self, status: PathStatus, seq: VarInt) {
+755        if self.remote_status.is_some_and(|(curr, _)| curr >= seq) {
+756            return trace!(%seq, "ignoring path status update");
+757        }
+758
+759        let prev = self.remote_status.replace((seq, status)).map(|(_, s)| s);
+760        if prev != Some(status) {
+761            debug!(?status, ?seq, "remote changed path status");
+762        }
+763    }
+764
+765    /// Updates the local status
+766    ///
+767    /// If the local status changed, the previous value is returned
+768    pub(super) fn local_update(&mut self, status: PathStatus) -> Option<PathStatus> {
+769        if self.local_status == status {
+770            return None;
+771        }
+772
+773        self.local_seq = self.local_seq.saturating_add(1u8);
+774        Some(std::mem::replace(&mut self.local_status, status))
+775    }
+776
+777    pub(crate) fn seq(&self) -> VarInt {
+778        self.local_seq
+779    }
+780}
+781
+782/// The QUIC-MULTIPATH path status
+783///
+784/// See section "3.3 Path Status Management":
+785/// <https://quicwg.org/multipath/draft-ietf-quic-multipath.html#name-path-status-management>
+786#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
+787pub enum PathStatus {
+788    /// Paths marked with as available will be used when scheduling packets
+789    ///
+790    /// If multiple paths are available, packets will be scheduled on whichever has
+791    /// capacity.
+792    #[default]
+793    Available,
+794    /// Paths marked as backup will only be used if there are no available paths
+795    ///
+796    /// If the max_idle_timeout is specified the path will be kept alive so that it does not
+797    /// expire.
+798    Backup,
+799}
+800
+801/// Application events about paths
+802#[derive(Debug, Clone, PartialEq, Eq)]
+803pub enum PathEvent {
+804    /// A new path has been opened
+805    Opened {
+806        /// Which path is now open
+807        id: PathId,
+808    },
+809    /// A path has been closed
+810    Closed {
+811        /// Which path has been closed
+812        id: PathId,
+813        /// Error code supplied by the peer
+814        /// See <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-error-codes>
+815        /// for a list of known errors.
+816        error_code: VarInt,
+817    },
+818    /// All remaining state for a path has been removed
+819    ///
+820    /// The [`PathEvent::Closed`] would have been emitted for this path earlier.
+821    Abandoned {
+822        /// Which path had its state dropped
+823        id: PathId,
+824        /// The final path stats, they are no longer available via [`Connection::stats`]
+825        ///
+826        /// [`Connection::stats`]: super::Connection::stats
+827        path_stats: PathStats,
+828    },
+829    /// Path was closed locally
+830    LocallyClosed {
+831        /// Path for which the error occurred
+832        id: PathId,
+833        /// The error that occurred
+834        error: PathError,
+835    },
+836    /// The remote changed the status of the path
+837    ///
+838    /// The local status is not changed because of this event. It is up to the application
+839    /// to update the local status, which is used for packet scheduling, when the remote
+840    /// changes the status.
+841    RemoteStatus {
+842        /// Path which has changed status
+843        id: PathId,
+844        /// The new status set by the remote
+845        status: PathStatus,
+846    },
+847    /// Received an observation of our external address from the peer.
+848    ObservedAddr {
+849        /// Path over which the observed address was reported, [`PathId::ZERO`] when multipath is
+850        /// not negotiated
+851        id: PathId,
+852        /// The address observed by the remote over this path
+853        addr: SocketAddr,
+854    },
+855}
+856
+857/// Error from setting path status
+858#[derive(Debug, Error, Clone, PartialEq, Eq)]
+859pub enum SetPathStatusError {
+860    /// Error indicating that a path has not been opened or has already been abandoned
+861    #[error("closed path")]
+862    ClosedPath,
+863    /// Error indicating that this operation requires multipath to be negotiated whereas it hasn't been
+864    #[error("multipath not negotiated")]
+865    MultipathNotNegotiated,
+866}
+867
+868/// Error indicating that a path has not been opened or has already been abandoned
+869#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+870#[error("closed path")]
+871pub struct ClosedPath {
+872    pub(super) _private: (),
+873}
+874
+875#[cfg(test)]
+876mod tests {
+877    use super::*;
+878
+879    #[test]
+880    fn test_path_id_saturating_add() {
+881        // add within range behaves normally
+882        let large: PathId = u16::MAX.into();
+883        let next = u32::from(u16::MAX) + 1;
+884        assert_eq!(large.saturating_add(1u8), PathId::from(next));
+885
+886        // outside range saturates
+887        assert_eq!(PathId::MAX.saturating_add(1u8), PathId::MAX)
+888    }
+889}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/qlog.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/qlog.rs.html new file mode 100644 index 000000000..83e250e83 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/qlog.rs.html @@ -0,0 +1,1066 @@ +qlog.rs - source

iroh_quinn_proto/connection/
qlog.rs

1//! Implements support for emitting qlog events.
+2//!
+3//! This uses the [`n0-qlog`] crate to emit qlog events. The n0-qlog crate, and thus this implementation,
+4//! is currently based on [draft-ietf-quic-qlog-main-schema-13] an [draft-ietf-quic-qlog-quic-events-12].
+5//!
+6//! [draft-ietf-quic-qlog-main-schema-13]: https://www.ietf.org/archive/id/draft-ietf-quic-qlog-main-schema-13.html
+7//! [draft-ietf-quic-qlog-quic-events-12]: https://www.ietf.org/archive/id/draft-ietf-quic-qlog-quic-events-12.html
+8
+9// Function bodies in this module are regularly cfg'd out
+10#![allow(unused_variables)]
+11
+12#[cfg(not(feature = "qlog"))]
+13use std::marker::PhantomData;
+14#[cfg(feature = "qlog")]
+15use std::sync::{Arc, Mutex};
+16use std::{
+17    net::{IpAddr, SocketAddr},
+18    time::Duration,
+19};
+20
+21#[cfg(feature = "qlog")]
+22use qlog::{
+23    CommonFields, HexSlice, TokenType, VantagePoint,
+24    events::{
+25        ApplicationError, ConnectionClosedFrameError, Event, EventData, RawInfo, TupleEndpointInfo,
+26        quic::{
+27            self, AckedRanges, AddressDiscoveryRole, ConnectionStarted, ErrorSpace, PacketHeader,
+28            PacketLost, PacketLostTrigger, PacketReceived, PacketSent, PacketType,
+29            ParametersRestored, ParametersSet, PreferredAddress, QlogTimerType, QuicFrame,
+30            StreamType, TimerEventType, TimerType, TimerUpdated, TransportInitiator, TupleAssigned,
+31        },
+32    },
+33    streamer::QlogStreamer,
+34};
+35#[cfg(feature = "qlog")]
+36use tracing::warn;
+37
+38use crate::{
+39    Connection, ConnectionId, Frame, Instant, PathId,
+40    connection::{PathData, SentPacket, timer::Timer},
+41    frame::{EcnCounts, StreamMeta},
+42    packet::{Header, SpaceId},
+43    range_set::ArrayRangeSet,
+44    transport_parameters::TransportParameters,
+45};
+46#[cfg(feature = "qlog")]
+47use crate::{
+48    QlogConfig, Side, TransportErrorCode,
+49    connection::timer::{ConnTimer, PathTimer},
+50    frame::Close,
+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(&self, event: EventData, now: Instant, tuple: Option<String>) {
+108        // Time will be overwritten by `add_event_with_instant`
+109        let mut event = Event::with_time(0.0, event);
+110        event.tuple = tuple;
+111        let mut qlog_streamer = self.0.lock().unwrap();
+112        if let Err(e) = qlog_streamer.add_event_with_instant(event, now) {
+113            warn!("could not emit qlog event: {e}");
+114        }
+115    }
+116}
+117
+118/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely
+119#[derive(Clone, Default)]
+120pub(crate) struct QlogSink {
+121    #[cfg(feature = "qlog")]
+122    stream: Option<QlogStream>,
+123}
+124
+125impl QlogSink {
+126    #[cfg(feature = "qlog")]
+127    pub(crate) fn new(
+128        config: QlogConfig,
+129        initial_dst_cid: ConnectionId,
+130        side: Side,
+131        now: Instant,
+132    ) -> Self {
+133        let stream = QlogStream::new(config, initial_dst_cid, side, now)
+134            .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}"))
+135            .ok();
+136        Self { stream }
+137    }
+138
+139    pub(crate) fn emit_connection_started(
+140        &self,
+141        now: Instant,
+142        loc_cid: ConnectionId,
+143        rem_cid: ConnectionId,
+144        remote: SocketAddr,
+145        local_ip: Option<IpAddr>,
+146        transport_params: &TransportParameters,
+147    ) {
+148        #[cfg(feature = "qlog")]
+149        {
+150            let Some(stream) = self.stream.as_ref() else {
+151                return;
+152            };
+153            stream.emit_event(
+154                EventData::ConnectionStarted(ConnectionStarted {
+155                    local: tuple_endpoint_info(local_ip, None, Some(loc_cid)),
+156                    remote: tuple_endpoint_info(
+157                        Some(remote.ip()),
+158                        Some(remote.port()),
+159                        Some(rem_cid),
+160                    ),
+161                }),
+162                now,
+163            );
+164
+165            let params = transport_params.to_qlog(TransportInitiator::Local);
+166            let event = EventData::ParametersSet(params);
+167            stream.emit_event(event, now);
+168        }
+169    }
+170
+171    pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) {
+172        #[cfg(feature = "qlog")]
+173        {
+174            let Some(stream) = self.stream.as_ref() else {
+175                return;
+176            };
+177
+178            let Some(metrics) = path.qlog_recovery_metrics(path_id) else {
+179                return;
+180            };
+181
+182            stream.emit_event(EventData::MetricsUpdated(metrics), now);
+183        }
+184    }
+185
+186    pub(super) fn emit_packet_lost(
+187        &self,
+188        pn: u64,
+189        info: &SentPacket,
+190        loss_delay: Duration,
+191        space: SpaceId,
+192        now: Instant,
+193    ) {
+194        #[cfg(feature = "qlog")]
+195        {
+196            let Some(stream) = self.stream.as_ref() else {
+197                return;
+198            };
+199
+200            let event = PacketLost {
+201                header: Some(PacketHeader {
+202                    packet_number: Some(pn),
+203                    packet_type: packet_type(space, false),
+204                    length: Some(info.size),
+205                    ..Default::default()
+206                }),
+207                frames: None,
+208                trigger: Some(
+209                    match info.time_sent.saturating_duration_since(now) >= loss_delay {
+210                        true => PacketLostTrigger::TimeThreshold,
+211                        false => PacketLostTrigger::ReorderingThreshold,
+212                    },
+213                ),
+214                is_mtu_probe_packet: None,
+215            };
+216
+217            stream.emit_event(EventData::PacketLost(event), now);
+218        }
+219    }
+220
+221    pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) {
+222        #[cfg(feature = "qlog")]
+223        {
+224            let Some(stream) = self.stream.as_ref() else {
+225                return;
+226            };
+227            let params = conn.peer_params.to_qlog_restored();
+228            let event = EventData::ParametersRestored(params);
+229            stream.emit_event(event, now);
+230        }
+231    }
+232
+233    pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) {
+234        #[cfg(feature = "qlog")]
+235        {
+236            let Some(stream) = self.stream.as_ref() else {
+237                return;
+238            };
+239            let params = conn.peer_params.to_qlog(TransportInitiator::Remote);
+240            let event = EventData::ParametersSet(params);
+241            stream.emit_event(event, now);
+242        }
+243    }
+244
+245    pub(super) fn emit_tuple_assigned(&self, path_id: PathId, remote: SocketAddr, now: Instant) {
+246        #[cfg(feature = "qlog")]
+247        {
+248            let Some(stream) = self.stream.as_ref() else {
+249                return;
+250            };
+251            let tuple_id = fmt_tuple_id(path_id.as_u32() as u64);
+252            let event = TupleAssigned {
+253                tuple_id,
+254                tuple_local: None,
+255                tuple_remote: Some(tuple_endpoint_info(
+256                    Some(remote.ip()),
+257                    Some(remote.port()),
+258                    None,
+259                )),
+260            };
+261
+262            stream.emit_event(EventData::TupleAssigned(event), now);
+263        }
+264    }
+265
+266    pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) {
+267        #[cfg(feature = "qlog")]
+268        {
+269            let Some(stream) = self.stream.as_ref() else {
+270                return;
+271            };
+272            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+273            stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id);
+274        }
+275    }
+276
+277    pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) {
+278        #[cfg(feature = "qlog")]
+279        {
+280            let Some(stream) = self.stream.as_ref() else {
+281                return;
+282            };
+283            let mut packet = packet;
+284            packet.emit_padding();
+285            let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id);
+286            let event = packet.inner;
+287            stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id);
+288        }
+289    }
+290
+291    /// Emits a timer event.
+292    ///
+293    /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use
+294    /// its `emit_timer_` methods.
+295    #[cfg(feature = "qlog")]
+296    fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) {
+297        let Some(stream) = self.stream.as_ref() else {
+298            return;
+299        };
+300
+301        let timer_type: Option<TimerType> = match timer {
+302            Timer::Conn(conn_timer) => match conn_timer {
+303                ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()),
+304                ConnTimer::Close => Some(TimerType::custom("close")),
+305                ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")),
+306                ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")),
+307                ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")),
+308            },
+309            Timer::PerPath(_, path_timer) => match path_timer {
+310                PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()),
+311                PathTimer::PathIdle => Some(TimerType::custom("path_idle")),
+312                PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()),
+313                PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")),
+314                PathTimer::PathOpen => Some(TimerType::custom("path_open")),
+315                PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")),
+316                PathTimer::Pacing => Some(TimerType::custom("pacing")),
+317                PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()),
+318                PathTimer::DiscardPath => Some(TimerType::custom("discard_path")),
+319            },
+320        };
+321
+322        let Some(timer_type) = timer_type else {
+323            return;
+324        };
+325
+326        let delta = match op {
+327            TimerOp::Set(instant) => instant
+328                .checked_duration_since(now)
+329                .map(|dur| dur.as_secs_f32() * 1000.),
+330            _ => None,
+331        };
+332        let path_id = match timer {
+333            Timer::Conn(_) => None,
+334            Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64),
+335        };
+336
+337        let event_type = match op {
+338            TimerOp::Set(_) => TimerEventType::Set,
+339            TimerOp::Expire => TimerEventType::Expired,
+340            TimerOp::Cancelled => TimerEventType::Cancelled,
+341        };
+342
+343        let event = TimerUpdated {
+344            path_id,
+345            timer_type: Some(timer_type),
+346            timer_id: None,
+347            packet_number_space: None,
+348            event_type,
+349            delta,
+350        };
+351        stream.emit_event(EventData::TimerUpdated(event), now);
+352    }
+353
+354    /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp.
+355    ///
+356    /// This may be used if you want to pass a [`QlogSink`] downwards together with the current
+357    /// `now` timestamp, to not have to pass the latter separately as an additional argument just
+358    /// for qlog support.
+359    pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> {
+360        #[cfg(feature = "qlog")]
+361        let s = QlogSinkWithTime { sink: self, now };
+362        #[cfg(not(feature = "qlog"))]
+363        let s = QlogSinkWithTime {
+364            _phantom: PhantomData,
+365        };
+366        s
+367    }
+368}
+369
+370/// A [`QlogSink`] with a `now` timestamp.
+371pub(super) struct QlogSinkWithTime<'a> {
+372    #[cfg(feature = "qlog")]
+373    sink: &'a QlogSink,
+374    #[cfg(feature = "qlog")]
+375    now: Instant,
+376    #[cfg(not(feature = "qlog"))]
+377    _phantom: PhantomData<&'a ()>,
+378}
+379
+380impl<'a> QlogSinkWithTime<'a> {
+381    pub(super) fn emit_timer_stop(&self, timer: Timer) {
+382        #[cfg(feature = "qlog")]
+383        self.sink.emit_timer(timer, TimerOp::Cancelled, self.now)
+384    }
+385
+386    pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) {
+387        #[cfg(feature = "qlog")]
+388        self.sink
+389            .emit_timer(timer, TimerOp::Set(expire_at), self.now)
+390    }
+391
+392    pub(super) fn emit_timer_expire(&self, timer: Timer) {
+393        #[cfg(feature = "qlog")]
+394        self.sink.emit_timer(timer, TimerOp::Expire, self.now)
+395    }
+396}
+397
+398#[cfg(feature = "qlog")]
+399enum TimerOp {
+400    Set(Instant),
+401    Expire,
+402    Cancelled,
+403}
+404
+405/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled.
+406#[derive(Default)]
+407pub(crate) struct QlogSentPacket {
+408    #[cfg(feature = "qlog")]
+409    inner: PacketSent,
+410}
+411
+412impl QlogSentPacket {
+413    /// Sets data from the packet header.
+414    pub(crate) fn header(
+415        &mut self,
+416        header: &Header,
+417        pn: Option<u64>,
+418        space: SpaceId,
+419        is_0rtt: bool,
+420        path_id: PathId,
+421    ) {
+422        #[cfg(feature = "qlog")]
+423        {
+424            self.inner.header.scid = header.src_cid().map(stringify_cid);
+425            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+426            self.inner.header.packet_number = pn;
+427            self.inner.header.packet_type = packet_type(space, is_0rtt);
+428            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+429        }
+430    }
+431
+432    /// Adds a frame by pushing a [`Frame`].
+433    pub(crate) fn frame(&mut self, frame: &Frame) {
+434        #[cfg(feature = "qlog")]
+435        self.frame_raw(frame.to_qlog())
+436    }
+437
+438    /// Adds a PADDING frame.
+439    ///
+440    /// This is a no-op if the `qlog` feature is not enabled.
+441    pub(crate) fn frame_padding(&mut self, count: usize) {
+442        #[cfg(feature = "qlog")]
+443        self.frame_raw(QuicFrame::Padding {
+444            raw: Some(RawInfo {
+445                length: Some(count as u64),
+446                payload_length: Some(count as u64),
+447                data: None,
+448            }),
+449        });
+450    }
+451
+452    /// Adds an ACK frame.
+453    ///
+454    /// This is a no-op if the `qlog` feature is not enabled.
+455    pub(crate) fn frame_ack(
+456        &mut self,
+457        delay: u64,
+458        ranges: &ArrayRangeSet,
+459        ecn: Option<&EcnCounts>,
+460    ) {
+461        #[cfg(feature = "qlog")]
+462        self.frame_raw(QuicFrame::Ack {
+463            ack_delay: Some(delay as f32),
+464            acked_ranges: Some(AckedRanges::Double(
+465                ranges
+466                    .iter()
+467                    .map(|range| (range.start, range.end))
+468                    .collect(),
+469            )),
+470            ect1: ecn.map(|e| e.ect1),
+471            ect0: ecn.map(|e| e.ect0),
+472            ce: ecn.map(|e| e.ce),
+473            raw: None,
+474        });
+475    }
+476
+477    /// Adds a PATH_ACK frame.
+478    ///
+479    /// This is a no-op if the `qlog` feature is not enabled.
+480    pub(crate) fn frame_path_ack(
+481        &mut self,
+482        path_id: PathId,
+483        delay: u64,
+484        ranges: &ArrayRangeSet,
+485        ecn: Option<&EcnCounts>,
+486    ) {
+487        #[cfg(feature = "qlog")]
+488        self.frame_raw(QuicFrame::PathAck {
+489            path_id: path_id.as_u32() as u64,
+490            ack_delay: Some(delay as f32),
+491            acked_ranges: Some(AckedRanges::Double(
+492                ranges
+493                    .iter()
+494                    .map(|range| (range.start, range.end))
+495                    .collect(),
+496            )),
+497            ect1: ecn.map(|e| e.ect1),
+498            ect0: ecn.map(|e| e.ect0),
+499            ce: ecn.map(|e| e.ce),
+500            raw: None,
+501        });
+502    }
+503
+504    /// Adds a DATAGRAM frame.
+505    ///
+506    /// This is a no-op if the `qlog` feature is not enabled.
+507    pub(crate) fn frame_datagram(&mut self, len: u64) {
+508        #[cfg(feature = "qlog")]
+509        self.frame_raw(QuicFrame::Datagram {
+510            raw: Some(RawInfo {
+511                length: Some(len),
+512                ..Default::default()
+513            }),
+514        });
+515    }
+516
+517    /// Adds a STREAM frame.
+518    ///
+519    /// This is a no-op if the `qlog` feature is not enabled.
+520    pub(crate) fn frame_stream(&mut self, meta: &StreamMeta) {
+521        #[cfg(feature = "qlog")]
+522        self.frame_raw(QuicFrame::Stream {
+523            stream_id: meta.id.into(),
+524            offset: Some(meta.offsets.start),
+525            fin: Some(meta.fin),
+526            raw: Some(RawInfo {
+527                length: Some(meta.offsets.end - meta.offsets.start),
+528                ..Default::default()
+529            }),
+530        });
+531    }
+532
+533    /// Adds a frame by pushing a [`QuicFrame`].
+534    ///
+535    /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve
+536    /// calculations which shouldn't be performed if the `qlog` feature is disabled.
+537    #[cfg(feature = "qlog")]
+538    fn frame_raw(&mut self, frame: QuicFrame) {
+539        self.inner.frames.get_or_insert_default().push(frame);
+540    }
+541
+542    /// Finalizes the packet by setting the final packet length (after encryption).
+543    pub(super) fn finalize(&mut self, len: usize) {
+544        #[cfg(feature = "qlog")]
+545        {
+546            self.inner.header.length = Some(len as u16);
+547        }
+548    }
+549}
+550
+551/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled.
+552pub(crate) struct QlogRecvPacket {
+553    #[cfg(feature = "qlog")]
+554    inner: PacketReceived,
+555    #[cfg(feature = "qlog")]
+556    padding: usize,
+557}
+558
+559impl QlogRecvPacket {
+560    /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled.
+561    ///
+562    /// `len` is the packet's full length (before decryption).
+563    pub(crate) fn new(len: usize) -> Self {
+564        #[cfg(not(feature = "qlog"))]
+565        let this = Self {};
+566
+567        #[cfg(feature = "qlog")]
+568        let this = {
+569            let mut this = Self {
+570                inner: Default::default(),
+571                padding: 0,
+572            };
+573            this.inner.header.length = Some(len as u16);
+574            this
+575        };
+576
+577        this
+578    }
+579
+580    /// Adds info from the packet header.
+581    pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) {
+582        #[cfg(feature = "qlog")]
+583        {
+584            let is_0rtt = !header.is_1rtt();
+585            self.inner.header.scid = header.src_cid().map(stringify_cid);
+586            self.inner.header.dcid = Some(stringify_cid(header.dst_cid()));
+587            self.inner.header.packet_number = pn;
+588            self.inner.header.packet_type = packet_type(header.space(), is_0rtt);
+589            self.inner.header.path_id = Some(path_id.as_u32() as u64);
+590        }
+591    }
+592
+593    /// Adds a frame.
+594    pub(crate) fn frame(&mut self, frame: &Frame) {
+595        #[cfg(feature = "qlog")]
+596        {
+597            if matches!(frame, crate::Frame::Padding) {
+598                self.padding += 1;
+599            } else {
+600                self.emit_padding();
+601                self.inner
+602                    .frames
+603                    .get_or_insert_default()
+604                    .push(frame.to_qlog())
+605            }
+606        }
+607    }
+608
+609    #[cfg(feature = "qlog")]
+610    fn emit_padding(&mut self) {
+611        if self.padding > 0 {
+612            self.inner
+613                .frames
+614                .get_or_insert_default()
+615                .push(QuicFrame::Padding {
+616                    raw: Some(RawInfo {
+617                        length: Some(self.padding as u64),
+618                        payload_length: Some(self.padding as u64),
+619                        data: None,
+620                    }),
+621                });
+622            self.padding = 0;
+623        }
+624    }
+625}
+626
+627#[cfg(feature = "qlog")]
+628impl Frame {
+629    /// Converts a [`crate::Frame`] into a [`QuicFrame`].
+630    pub(crate) fn to_qlog(&self) -> QuicFrame {
+631        match self {
+632            Self::Padding => QuicFrame::Padding {
+633                raw: Some(RawInfo {
+634                    length: None,
+635                    payload_length: Some(1),
+636                    data: None,
+637                }),
+638            },
+639            Self::Ping => QuicFrame::Ping { raw: None },
+640            Self::Ack(f) => QuicFrame::Ack {
+641                ack_delay: Some(f.delay as f32),
+642                acked_ranges: Some(AckedRanges::Double(
+643                    f.iter()
+644                        .map(|range| (*range.start(), *range.end()))
+645                        .collect(),
+646                )),
+647                ect1: f.ecn.as_ref().map(|e| e.ect1),
+648                ect0: f.ecn.as_ref().map(|e| e.ect0),
+649                ce: f.ecn.as_ref().map(|e| e.ce),
+650                raw: None,
+651            },
+652            Self::ResetStream(f) => QuicFrame::ResetStream {
+653                stream_id: f.id.into(),
+654                error_code: Some(f.error_code.into_inner()),
+655                final_size: f.final_offset.into(),
+656                error: ApplicationError::Unknown,
+657                raw: None,
+658            },
+659            Self::StopSending(f) => QuicFrame::StopSending {
+660                stream_id: f.id.into(),
+661                error_code: Some(f.error_code.into_inner()),
+662                error: ApplicationError::Unknown,
+663                raw: None,
+664            },
+665            Self::Crypto(f) => QuicFrame::Crypto {
+666                offset: f.offset,
+667                raw: Some(RawInfo {
+668                    length: Some(f.data.len() as u64),
+669                    ..Default::default()
+670                }),
+671            },
+672            Self::NewToken(f) => QuicFrame::NewToken {
+673                token: qlog::Token {
+674                    ty: Some(TokenType::Retry),
+675                    raw: Some(RawInfo {
+676                        data: HexSlice::maybe_string(Some(&f.token)),
+677                        length: Some(f.token.len() as u64),
+678                        payload_length: None,
+679                    }),
+680                    details: None,
+681                },
+682                raw: None,
+683            },
+684            Self::Stream(s) => QuicFrame::Stream {
+685                stream_id: s.id.into(),
+686                offset: Some(s.offset),
+687                fin: Some(s.fin),
+688                raw: Some(RawInfo {
+689                    length: Some(s.data.len() as u64),
+690                    ..Default::default()
+691                }),
+692            },
+693            Self::MaxData(v) => QuicFrame::MaxData {
+694                maximum: (*v).into(),
+695                raw: None,
+696            },
+697            Self::MaxStreamData { id, offset } => QuicFrame::MaxStreamData {
+698                stream_id: (*id).into(),
+699                maximum: *offset,
+700                raw: None,
+701            },
+702            Self::MaxStreams { dir, count } => QuicFrame::MaxStreams {
+703                maximum: *count,
+704                stream_type: (*dir).into(),
+705                raw: None,
+706            },
+707            Self::DataBlocked { offset } => QuicFrame::DataBlocked {
+708                limit: *offset,
+709                raw: None,
+710            },
+711            Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked {
+712                stream_id: (*id).into(),
+713                limit: *offset,
+714                raw: None,
+715            },
+716            Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked {
+717                stream_type: (*dir).into(),
+718                limit: *limit,
+719                raw: None,
+720            },
+721            Self::NewConnectionId(f) => QuicFrame::NewConnectionId {
+722                sequence_number: f.sequence,
+723                retire_prior_to: f.retire_prior_to,
+724                connection_id_length: Some(f.id.len() as u8),
+725                connection_id: f.id.to_string(),
+726                stateless_reset_token: Some(f.reset_token.to_string()),
+727                raw: None,
+728            },
+729            Self::RetireConnectionId(f) => QuicFrame::RetireConnectionId {
+730                sequence_number: f.sequence,
+731                raw: None,
+732            },
+733            Self::PathChallenge(token) => QuicFrame::PathChallenge {
+734                data: Some(token.to_string()),
+735                raw: None,
+736            },
+737            Self::PathResponse(token) => QuicFrame::PathResponse {
+738                data: Some(token.to_string()),
+739                raw: None,
+740            },
+741            Self::Close(close) => match close {
+742                Close::Connection(f) => {
+743                    let (error, error_code) = transport_error(f.error_code);
+744                    let error = error.map(|transport_error| {
+745                        ConnectionClosedFrameError::TransportError(transport_error)
+746                    });
+747                    QuicFrame::ConnectionClose {
+748                        error_space: Some(ErrorSpace::TransportError),
+749                        error,
+750                        error_code,
+751                        reason: String::from_utf8(f.reason.to_vec()).ok(),
+752                        reason_bytes: None,
+753                        trigger_frame_type: None,
+754                    }
+755                }
+756                Close::Application(f) => QuicFrame::ConnectionClose {
+757                    error_space: Some(ErrorSpace::ApplicationError),
+758                    error: None,
+759                    error_code: Some(f.error_code.into_inner()),
+760                    reason: String::from_utf8(f.reason.to_vec()).ok(),
+761                    reason_bytes: None,
+762                    trigger_frame_type: None,
+763                },
+764            },
+765            Self::Datagram(d) => QuicFrame::Datagram {
+766                raw: Some(RawInfo {
+767                    length: Some(d.data.len() as u64),
+768                    ..Default::default()
+769                }),
+770            },
+771            Self::HandshakeDone => QuicFrame::HandshakeDone { raw: None },
+772            Self::PathAck(ack) => QuicFrame::PathAck {
+773                path_id: ack.path_id.as_u32().into(),
+774                ack_delay: Some(ack.delay as f32),
+775                ect1: ack.ecn.as_ref().map(|e| e.ect1),
+776                ect0: ack.ecn.as_ref().map(|e| e.ect0),
+777                ce: ack.ecn.as_ref().map(|e| e.ce),
+778                raw: None,
+779                acked_ranges: Some(AckedRanges::Double(
+780                    ack.into_iter()
+781                        .map(|range| (*range.start(), *range.end()))
+782                        .collect(),
+783                )),
+784            },
+785            Self::PathAbandon(frame) => QuicFrame::PathAbandon {
+786                path_id: frame.path_id.as_u32().into(),
+787                error_code: frame.error_code.into(),
+788                raw: None,
+789            },
+790            Self::PathStatusAvailable(frame) => QuicFrame::PathStatusAvailable {
+791                path_id: frame.path_id.as_u32().into(),
+792                path_status_sequence_number: frame.status_seq_no.into(),
+793                raw: None,
+794            },
+795            Self::PathStatusBackup(frame) => QuicFrame::PathStatusBackup {
+796                path_id: frame.path_id.as_u32().into(),
+797                path_status_sequence_number: frame.status_seq_no.into(),
+798                raw: None,
+799            },
+800            Self::PathsBlocked(frame) => QuicFrame::PathsBlocked {
+801                maximum_path_id: frame.0.as_u32().into(),
+802                raw: None,
+803            },
+804            Self::PathCidsBlocked(frame) => QuicFrame::PathCidsBlocked {
+805                path_id: frame.path_id.as_u32().into(),
+806                next_sequence_number: frame.next_seq.into(),
+807                raw: None,
+808            },
+809            Self::MaxPathId(id) => QuicFrame::MaxPathId {
+810                maximum_path_id: id.0.as_u32().into(),
+811                raw: None,
+812            },
+813            Self::AckFrequency(f) => QuicFrame::AckFrequency {
+814                sequence_number: f.sequence.into_inner(),
+815                ack_eliciting_threshold: f.ack_eliciting_threshold.into_inner(),
+816                requested_max_ack_delay: f.request_max_ack_delay.into_inner(),
+817                reordering_threshold: f.reordering_threshold.into_inner(),
+818                raw: None,
+819            },
+820            Self::ImmediateAck => QuicFrame::ImmediateAck { raw: None },
+821            Self::ObservedAddr(f) => QuicFrame::ObservedAddress {
+822                sequence_number: f.seq_no.into_inner(),
+823                ip_v4: match f.ip {
+824                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+825                    IpAddr::V6(ipv6_addr) => None,
+826                },
+827                ip_v6: match f.ip {
+828                    IpAddr::V4(ipv4_addr) => None,
+829                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+830                },
+831                port: f.port,
+832                raw: None,
+833            },
+834            Self::AddAddress(f) => QuicFrame::AddAddress {
+835                sequence_number: f.seq_no.into_inner(),
+836                ip_v4: match f.ip {
+837                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+838                    IpAddr::V6(ipv6_addr) => None,
+839                },
+840                ip_v6: match f.ip {
+841                    IpAddr::V4(ipv4_addr) => None,
+842                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+843                },
+844                port: f.port,
+845            },
+846            Self::ReachOut(f) => QuicFrame::ReachOut {
+847                round: f.round.into_inner(),
+848                ip_v4: match f.ip {
+849                    IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()),
+850                    IpAddr::V6(ipv6_addr) => None,
+851                },
+852                ip_v6: match f.ip {
+853                    IpAddr::V4(ipv4_addr) => None,
+854                    IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()),
+855                },
+856                port: f.port,
+857            },
+858            Self::RemoveAddress(f) => QuicFrame::RemoveAddress {
+859                sequence_number: f.seq_no.into_inner(),
+860            },
+861        }
+862    }
+863}
+864
+865#[cfg(feature = "qlog")]
+866impl From<crate::Dir> for StreamType {
+867    fn from(value: crate::Dir) -> Self {
+868        match value {
+869            crate::Dir::Bi => Self::Bidirectional,
+870            crate::Dir::Uni => Self::Unidirectional,
+871        }
+872    }
+873}
+874
+875#[cfg(feature = "qlog")]
+876fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType {
+877    match space {
+878        SpaceId::Initial => PacketType::Initial,
+879        SpaceId::Handshake => PacketType::Handshake,
+880        SpaceId::Data if is_0rtt => PacketType::ZeroRtt,
+881        SpaceId::Data => PacketType::OneRtt,
+882    }
+883}
+884
+885#[cfg(feature = "qlog")]
+886fn stringify_cid(cid: ConnectionId) -> String {
+887    format!("{cid}")
+888}
+889
+890#[cfg(feature = "qlog")]
+891fn tuple_endpoint_info(
+892    ip: Option<IpAddr>,
+893    port: Option<u16>,
+894    cid: Option<ConnectionId>,
+895) -> TupleEndpointInfo {
+896    let (ip_v4, port_v4, ip_v6, port_v6) = match ip {
+897        Some(addr) => match addr {
+898            IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None),
+899            IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port),
+900        },
+901        None => (None, None, None, None),
+902    };
+903    TupleEndpointInfo {
+904        ip_v4,
+905        port_v4,
+906        ip_v6,
+907        port_v6,
+908        connection_ids: cid.map(|cid| vec![cid.to_string()]),
+909    }
+910}
+911
+912#[cfg(feature = "qlog")]
+913fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) {
+914    let transport_error = match code {
+915        TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError),
+916        TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError),
+917        TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused),
+918        TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError),
+919        TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError),
+920        TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError),
+921        TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError),
+922        TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError),
+923        TransportErrorCode::TRANSPORT_PARAMETER_ERROR => {
+924            Some(quic::TransportError::TransportParameterError)
+925        }
+926        TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => {
+927            Some(quic::TransportError::ConnectionIdLimitError)
+928        }
+929        TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation),
+930        TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken),
+931        TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError),
+932        TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => {
+933            Some(quic::TransportError::CryptoBufferExceeded)
+934        }
+935        TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError),
+936        TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached),
+937        TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath),
+938        // multipath
+939        TransportErrorCode::APPLICATION_ABANDON_PATH => {
+940            Some(quic::TransportError::ApplicationAbandonPath)
+941        }
+942        TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => {
+943            Some(quic::TransportError::PathResourceLimitReached)
+944        }
+945        TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor),
+946        TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => {
+947            Some(quic::TransportError::NoCidsAvailableForPath)
+948        }
+949        _ => None,
+950    };
+951    let code = match transport_error {
+952        None => Some(code.into()),
+953        Some(_) => None,
+954    };
+955    (transport_error, code)
+956}
+957
+958#[cfg(feature = "qlog")]
+959fn fmt_tuple_id(path_id: u64) -> String {
+960    format!("p{path_id}")
+961}
+962
+963#[cfg(feature = "qlog")]
+964impl TransportParameters {
+965    fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet {
+966        ParametersSet {
+967            initiator: Some(initiator),
+968            resumption_allowed: None,
+969            early_data_enabled: None,
+970            tls_cipher: None,
+971            original_destination_connection_id: self
+972                .original_dst_cid
+973                .as_ref()
+974                .map(ToString::to_string),
+975            initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string),
+976            retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string),
+977            stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string),
+978            disable_active_migration: Some(self.disable_active_migration),
+979            max_idle_timeout: Some(self.max_idle_timeout.into()),
+980            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+981            ack_delay_exponent: Some(self.ack_delay_exponent.into()),
+982            max_ack_delay: Some(self.max_ack_delay.into()),
+983            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+984            initial_max_data: Some(self.initial_max_data.into()),
+985            initial_max_stream_data_bidi_local: Some(
+986                self.initial_max_stream_data_bidi_local.into(),
+987            ),
+988            initial_max_stream_data_bidi_remote: Some(
+989                self.initial_max_stream_data_bidi_remote.into(),
+990            ),
+991            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+992            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+993            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+994            preferred_address: self.preferred_address.as_ref().map(Into::into),
+995            min_ack_delay: self.min_ack_delay.map(Into::into),
+996            address_discovery: self.address_discovery_role.to_qlog(),
+997            initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64),
+998            max_remote_nat_traversal_addresses: self
+999                .max_remote_nat_traversal_addresses
+1000                .map(|v| u64::from(v.get())),
+1001            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1002            grease_quic_bit: Some(self.grease_quic_bit),
+1003            unknown_parameters: Default::default(),
+1004        }
+1005    }
+1006
+1007    fn to_qlog_restored(self) -> ParametersRestored {
+1008        ParametersRestored {
+1009            disable_active_migration: Some(self.disable_active_migration),
+1010            max_idle_timeout: Some(self.max_idle_timeout.into()),
+1011            max_udp_payload_size: Some(self.max_udp_payload_size.into()),
+1012            active_connection_id_limit: Some(self.active_connection_id_limit.into()),
+1013            initial_max_data: Some(self.initial_max_data.into()),
+1014            initial_max_stream_data_bidi_local: Some(
+1015                self.initial_max_stream_data_bidi_local.into(),
+1016            ),
+1017            initial_max_stream_data_bidi_remote: Some(
+1018                self.initial_max_stream_data_bidi_remote.into(),
+1019            ),
+1020            initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()),
+1021            initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()),
+1022            initial_max_streams_uni: Some(self.initial_max_streams_uni.into()),
+1023            max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into),
+1024            grease_quic_bit: Some(self.grease_quic_bit),
+1025        }
+1026    }
+1027}
+1028
+1029#[cfg(feature = "qlog")]
+1030impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress {
+1031    fn from(value: &crate::transport_parameters::PreferredAddress) -> Self {
+1032        let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default();
+1033        let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default();
+1034        let ip_v4 = value
+1035            .address_v4
+1036            .map(|addr| addr.ip().to_string())
+1037            .unwrap_or_default();
+1038        let ip_v6 = value
+1039            .address_v6
+1040            .map(|addr| addr.ip().to_string())
+1041            .unwrap_or_default();
+1042        let connection_id = value.connection_id.to_string();
+1043        let stateless_reset_token = value.stateless_reset_token.to_string();
+1044
+1045        Self {
+1046            ip_v4,
+1047            ip_v6,
+1048            port_v4,
+1049            port_v6,
+1050            connection_id,
+1051            stateless_reset_token,
+1052        }
+1053    }
+1054}
+1055
+1056#[cfg(feature = "qlog")]
+1057impl crate::address_discovery::Role {
+1058    fn to_qlog(self) -> Option<AddressDiscoveryRole> {
+1059        match self {
+1060            Self::SendOnly => Some(AddressDiscoveryRole::SendOnly),
+1061            Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly),
+1062            Self::Both => Some(AddressDiscoveryRole::Both),
+1063            Self::Disabled => None,
+1064        }
+1065    }
+1066}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html new file mode 100644 index 000000000..bbaa483db --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/send_buffer.rs.html @@ -0,0 +1,394 @@ +send_buffer.rs - source

iroh_quinn_proto/connection/
send_buffer.rs

1use std::{collections::VecDeque, ops::Range};
+2
+3use bytes::{Buf, Bytes};
+4
+5use crate::{VarInt, range_set::ArrayRangeSet};
+6
+7/// Buffer of outgoing retransmittable stream data
+8#[derive(Default, Debug)]
+9pub(super) struct SendBuffer {
+10    /// Data queued by the application but not yet acknowledged. May or may not have been sent.
+11    unacked_segments: VecDeque<Bytes>,
+12    /// Total size of `unacked_segments`
+13    unacked_len: usize,
+14    /// The first offset that hasn't been written by the application, i.e. the offset past the end of `unacked`
+15    offset: u64,
+16    /// The first offset that hasn't been sent
+17    ///
+18    /// Always lies in (offset - unacked.len())..offset
+19    unsent: u64,
+20    /// Acknowledged ranges which couldn't be discarded yet as they don't include the earliest
+21    /// offset in `unacked`
+22    // TODO: Recover storage from these by compacting (#700)
+23    acks: ArrayRangeSet,
+24    /// Previously transmitted ranges deemed lost
+25    retransmits: ArrayRangeSet,
+26}
+27
+28impl SendBuffer {
+29    /// Construct an empty buffer at the initial offset
+30    pub(super) fn new() -> Self {
+31        Self::default()
+32    }
+33
+34    /// Append application data to the end of the stream
+35    pub(super) fn write(&mut self, data: Bytes) {
+36        self.unacked_len += data.len();
+37        self.offset += data.len() as u64;
+38        self.unacked_segments.push_back(data);
+39    }
+40
+41    /// Discard a range of acknowledged stream data
+42    pub(super) fn ack(&mut self, mut range: Range<u64>) {
+43        // Clamp the range to data which is still tracked
+44        let base_offset = self.offset - self.unacked_len as u64;
+45        range.start = base_offset.max(range.start);
+46        range.end = base_offset.max(range.end);
+47
+48        self.acks.insert(range);
+49
+50        while self.acks.min() == Some(self.offset - self.unacked_len as u64) {
+51            let prefix = self.acks.pop_min().unwrap();
+52            let mut to_advance = (prefix.end - prefix.start) as usize;
+53
+54            self.unacked_len -= to_advance;
+55            while to_advance > 0 {
+56                let front = self
+57                    .unacked_segments
+58                    .front_mut()
+59                    .expect("Expected buffered data");
+60
+61                if front.len() <= to_advance {
+62                    to_advance -= front.len();
+63                    self.unacked_segments.pop_front();
+64
+65                    if self.unacked_segments.len() * 4 < self.unacked_segments.capacity() {
+66                        self.unacked_segments.shrink_to_fit();
+67                    }
+68                } else {
+69                    front.advance(to_advance);
+70                    to_advance = 0;
+71                }
+72            }
+73        }
+74    }
+75
+76    /// Compute the next range to transmit on this stream and update state to account for that
+77    /// transmission.
+78    ///
+79    /// `max_len` here includes the space which is available to transmit the
+80    /// offset and length of the data to send. The caller has to guarantee that
+81    /// there is at least enough space available to write maximum-sized metadata
+82    /// (8 byte offset + 8 byte length).
+83    ///
+84    /// The method returns a tuple:
+85    /// - The first return value indicates the range of data to send
+86    /// - The second return value indicates whether the length needs to be encoded
+87    ///   in the STREAM frames metadata (`true`), or whether it can be omitted
+88    ///   since the selected range will fill the whole packet.
+89    pub(super) fn poll_transmit(&mut self, mut max_len: usize) -> (Range<u64>, bool) {
+90        debug_assert!(max_len >= 8 + 8);
+91        let mut encode_length = false;
+92
+93        if let Some(range) = self.retransmits.pop_min() {
+94            // Retransmit sent data
+95
+96            // When the offset is known, we know how many bytes are required to encode it.
+97            // Offset 0 requires no space
+98            if range.start != 0 {
+99                max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(range.start) });
+100            }
+101            if range.end - range.start < max_len as u64 {
+102                encode_length = true;
+103                max_len -= 8;
+104            }
+105
+106            let end = range.end.min((max_len as u64).saturating_add(range.start));
+107            if end != range.end {
+108                self.retransmits.insert(end..range.end);
+109            }
+110            return (range.start..end, encode_length);
+111        }
+112
+113        // Transmit new data
+114
+115        // When the offset is known, we know how many bytes are required to encode it.
+116        // Offset 0 requires no space
+117        if self.unsent != 0 {
+118            max_len -= VarInt::size(unsafe { VarInt::from_u64_unchecked(self.unsent) });
+119        }
+120        if self.offset - self.unsent < max_len as u64 {
+121            encode_length = true;
+122            max_len -= 8;
+123        }
+124
+125        let end = self
+126            .offset
+127            .min((max_len as u64).saturating_add(self.unsent));
+128        let result = self.unsent..end;
+129        self.unsent = end;
+130        (result, encode_length)
+131    }
+132
+133    /// Returns data which is associated with a range
+134    ///
+135    /// This function can return a subset of the range, if the data is stored
+136    /// in noncontiguous fashion in the send buffer. In this case callers
+137    /// should call the function again with an incremented start offset to
+138    /// retrieve more data.
+139    pub(super) fn get(&self, offsets: Range<u64>) -> &[u8] {
+140        let base_offset = self.offset - self.unacked_len as u64;
+141
+142        let mut segment_offset = base_offset;
+143        for segment in self.unacked_segments.iter() {
+144            if offsets.start >= segment_offset
+145                && offsets.start < segment_offset + segment.len() as u64
+146            {
+147                let start = (offsets.start - segment_offset) as usize;
+148                let end = (offsets.end - segment_offset) as usize;
+149
+150                return &segment[start..end.min(segment.len())];
+151            }
+152            segment_offset += segment.len() as u64;
+153        }
+154
+155        &[]
+156    }
+157
+158    /// Queue a range of sent but unacknowledged data to be retransmitted
+159    pub(super) fn retransmit(&mut self, range: Range<u64>) {
+160        debug_assert!(range.end <= self.unsent, "unsent data can't be lost");
+161        self.retransmits.insert(range);
+162    }
+163
+164    pub(super) fn retransmit_all_for_0rtt(&mut self) {
+165        debug_assert_eq!(self.offset, self.unacked_len as u64);
+166        self.unsent = 0;
+167    }
+168
+169    /// First stream offset unwritten by the application, i.e. the offset that the next write will
+170    /// begin at
+171    pub(super) fn offset(&self) -> u64 {
+172        self.offset
+173    }
+174
+175    /// Whether all sent data has been acknowledged
+176    pub(super) fn is_fully_acked(&self) -> bool {
+177        self.unacked_len == 0
+178    }
+179
+180    /// Whether there's data to send
+181    ///
+182    /// There may be sent unacknowledged data even when this is false.
+183    pub(super) fn has_unsent_data(&self) -> bool {
+184        self.unsent != self.offset || !self.retransmits.is_empty()
+185    }
+186
+187    /// Compute the amount of data that hasn't been acknowledged
+188    pub(super) fn unacked(&self) -> u64 {
+189        self.unacked_len as u64 - self.acks.iter().map(|x| x.end - x.start).sum::<u64>()
+190    }
+191}
+192
+193#[cfg(test)]
+194mod tests {
+195    use super::*;
+196
+197    #[test]
+198    fn fragment_with_length() {
+199        let mut buf = SendBuffer::new();
+200        const MSG: &[u8] = b"Hello, world!";
+201        buf.write(MSG.into());
+202        // 0 byte offset => 19 bytes left => 13 byte data isn't enough
+203        // with 8 bytes reserved for length 11 payload bytes will fit
+204        assert_eq!(buf.poll_transmit(19), (0..11, true));
+205        assert_eq!(
+206            buf.poll_transmit(MSG.len() + 16 - 11),
+207            (11..MSG.len() as u64, true)
+208        );
+209        assert_eq!(
+210            buf.poll_transmit(58),
+211            (MSG.len() as u64..MSG.len() as u64, true)
+212        );
+213    }
+214
+215    #[test]
+216    fn fragment_without_length() {
+217        let mut buf = SendBuffer::new();
+218        const MSG: &[u8] = b"Hello, world with some extra data!";
+219        buf.write(MSG.into());
+220        // 0 byte offset => 19 bytes left => can be filled by 34 bytes payload
+221        assert_eq!(buf.poll_transmit(19), (0..19, false));
+222        assert_eq!(
+223            buf.poll_transmit(MSG.len() - 19 + 1),
+224            (19..MSG.len() as u64, false)
+225        );
+226        assert_eq!(
+227            buf.poll_transmit(58),
+228            (MSG.len() as u64..MSG.len() as u64, true)
+229        );
+230    }
+231
+232    #[test]
+233    fn reserves_encoded_offset() {
+234        let mut buf = SendBuffer::new();
+235
+236        // Pretend we have more than 1 GB of data in the buffer
+237        let chunk: Bytes = Bytes::from_static(&[0; 1024 * 1024]);
+238        for _ in 0..1025 {
+239            buf.write(chunk.clone());
+240        }
+241
+242        const SIZE1: u64 = 64;
+243        const SIZE2: u64 = 16 * 1024;
+244        const SIZE3: u64 = 1024 * 1024 * 1024;
+245
+246        // Offset 0 requires no space
+247        assert_eq!(buf.poll_transmit(16), (0..16, false));
+248        buf.retransmit(0..16);
+249        assert_eq!(buf.poll_transmit(16), (0..16, false));
+250        let mut transmitted = 16u64;
+251
+252        // Offset 16 requires 1 byte
+253        assert_eq!(
+254            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+255            (transmitted..SIZE1, false)
+256        );
+257        buf.retransmit(transmitted..SIZE1);
+258        assert_eq!(
+259            buf.poll_transmit((SIZE1 - transmitted + 1) as usize),
+260            (transmitted..SIZE1, false)
+261        );
+262        transmitted = SIZE1;
+263
+264        // Offset 64 requires 2 bytes
+265        assert_eq!(
+266            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+267            (transmitted..SIZE2, false)
+268        );
+269        buf.retransmit(transmitted..SIZE2);
+270        assert_eq!(
+271            buf.poll_transmit((SIZE2 - transmitted + 2) as usize),
+272            (transmitted..SIZE2, false)
+273        );
+274        transmitted = SIZE2;
+275
+276        // Offset 16384 requires requires 4 bytes
+277        assert_eq!(
+278            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+279            (transmitted..SIZE3, false)
+280        );
+281        buf.retransmit(transmitted..SIZE3);
+282        assert_eq!(
+283            buf.poll_transmit((SIZE3 - transmitted + 4) as usize),
+284            (transmitted..SIZE3, false)
+285        );
+286        transmitted = SIZE3;
+287
+288        // Offset 1GB requires 8 bytes
+289        assert_eq!(
+290            buf.poll_transmit(chunk.len() + 8),
+291            (transmitted..transmitted + chunk.len() as u64, false)
+292        );
+293        buf.retransmit(transmitted..transmitted + chunk.len() as u64);
+294        assert_eq!(
+295            buf.poll_transmit(chunk.len() + 8),
+296            (transmitted..transmitted + chunk.len() as u64, false)
+297        );
+298    }
+299
+300    #[test]
+301    fn multiple_segments() {
+302        let mut buf = SendBuffer::new();
+303        const MSG: &[u8] = b"Hello, world!";
+304        const MSG_LEN: u64 = MSG.len() as u64;
+305
+306        const SEG1: &[u8] = b"He";
+307        buf.write(SEG1.into());
+308        const SEG2: &[u8] = b"llo,";
+309        buf.write(SEG2.into());
+310        const SEG3: &[u8] = b" w";
+311        buf.write(SEG3.into());
+312        const SEG4: &[u8] = b"o";
+313        buf.write(SEG4.into());
+314        const SEG5: &[u8] = b"rld!";
+315        buf.write(SEG5.into());
+316
+317        assert_eq!(aggregate_unacked(&buf), MSG);
+318
+319        assert_eq!(buf.poll_transmit(16), (0..8, true));
+320        assert_eq!(buf.get(0..5), SEG1);
+321        assert_eq!(buf.get(2..8), SEG2);
+322        assert_eq!(buf.get(6..8), SEG3);
+323
+324        assert_eq!(buf.poll_transmit(16), (8..MSG_LEN, true));
+325        assert_eq!(buf.get(8..MSG_LEN), SEG4);
+326        assert_eq!(buf.get(9..MSG_LEN), SEG5);
+327
+328        assert_eq!(buf.poll_transmit(42), (MSG_LEN..MSG_LEN, true));
+329
+330        // Now drain the segments
+331        buf.ack(0..1);
+332        assert_eq!(aggregate_unacked(&buf), &MSG[1..]);
+333        buf.ack(0..3);
+334        assert_eq!(aggregate_unacked(&buf), &MSG[3..]);
+335        buf.ack(3..5);
+336        assert_eq!(aggregate_unacked(&buf), &MSG[5..]);
+337        buf.ack(7..9);
+338        assert_eq!(aggregate_unacked(&buf), &MSG[5..]);
+339        buf.ack(4..7);
+340        assert_eq!(aggregate_unacked(&buf), &MSG[9..]);
+341        buf.ack(0..MSG_LEN);
+342        assert_eq!(aggregate_unacked(&buf), &[] as &[u8]);
+343    }
+344
+345    #[test]
+346    fn retransmit() {
+347        let mut buf = SendBuffer::new();
+348        const MSG: &[u8] = b"Hello, world with extra data!";
+349        buf.write(MSG.into());
+350        // Transmit two frames
+351        assert_eq!(buf.poll_transmit(16), (0..16, false));
+352        assert_eq!(buf.poll_transmit(16), (16..23, true));
+353        // Lose the first, but not the second
+354        buf.retransmit(0..16);
+355        // Ensure we only retransmit the lost frame, then continue sending fresh data
+356        assert_eq!(buf.poll_transmit(16), (0..16, false));
+357        assert_eq!(buf.poll_transmit(16), (23..MSG.len() as u64, true));
+358        // Lose the second frame
+359        buf.retransmit(16..23);
+360        assert_eq!(buf.poll_transmit(16), (16..23, true));
+361    }
+362
+363    #[test]
+364    fn ack() {
+365        let mut buf = SendBuffer::new();
+366        const MSG: &[u8] = b"Hello, world!";
+367        buf.write(MSG.into());
+368        assert_eq!(buf.poll_transmit(16), (0..8, true));
+369        buf.ack(0..8);
+370        assert_eq!(aggregate_unacked(&buf), &MSG[8..]);
+371    }
+372
+373    #[test]
+374    fn reordered_ack() {
+375        let mut buf = SendBuffer::new();
+376        const MSG: &[u8] = b"Hello, world with extra data!";
+377        buf.write(MSG.into());
+378        assert_eq!(buf.poll_transmit(16), (0..16, false));
+379        assert_eq!(buf.poll_transmit(16), (16..23, true));
+380        buf.ack(16..23);
+381        assert_eq!(aggregate_unacked(&buf), MSG);
+382        buf.ack(0..16);
+383        assert_eq!(aggregate_unacked(&buf), &MSG[23..]);
+384        assert!(buf.acks.is_empty());
+385    }
+386
+387    fn aggregate_unacked(buf: &SendBuffer) -> Vec<u8> {
+388        let mut result = Vec::new();
+389        for segment in buf.unacked_segments.iter() {
+390            result.extend_from_slice(&segment[..]);
+391        }
+392        result
+393    }
+394}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/spaces.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/spaces.rs.html new file mode 100644 index 000000000..d4decf77d --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/spaces.rs.html @@ -0,0 +1,1334 @@ +spaces.rs - source

iroh_quinn_proto/connection/
spaces.rs

1use std::{
+2    cmp,
+3    collections::{BTreeMap, BTreeSet, VecDeque},
+4    mem,
+5    net::IpAddr,
+6    ops::{Bound, Index, IndexMut},
+7};
+8
+9use rand::Rng;
+10use rustc_hash::{FxHashMap, FxHashSet};
+11use sorted_index_buffer::SortedIndexBuffer;
+12use tracing::trace;
+13
+14use super::{PathId, assembler::Assembler};
+15use crate::{
+16    Dir, Duration, Instant, SocketAddr, StreamId, TransportError, TransportErrorCode, VarInt,
+17    connection::StreamsState,
+18    crypto::Keys,
+19    frame::{self, AddAddress, RemoveAddress},
+20    packet::SpaceId,
+21    range_set::ArrayRangeSet,
+22    shared::IssuedCid,
+23};
+24
+25pub(super) struct PacketSpace {
+26    pub(super) crypto: Option<Keys>,
+27
+28    /// Data to send
+29    pub(super) pending: Retransmits,
+30
+31    /// Incoming cryptographic handshake stream
+32    pub(super) crypto_stream: Assembler,
+33    /// Current offset of outgoing cryptographic handshake stream
+34    pub(super) crypto_offset: u64,
+35
+36    /// Multipath packet number spaces
+37    ///
+38    /// Each [`PathId`] has it's own [`PacketNumberSpace`].  Only the [`SpaceId::Data`] can
+39    /// have multiple packet number spaces, the other spaces only have a number space for
+40    /// `PathId::ZERO`, which is populated at creation.
+41    pub(super) number_spaces: BTreeMap<PathId, PacketNumberSpace>,
+42}
+43
+44impl PacketSpace {
+45    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl Rng + ?Sized)) -> Self {
+46        let number_space_0 = PacketNumberSpace::new(now, space, rng);
+47        Self {
+48            crypto: None,
+49            pending: Retransmits::default(),
+50            crypto_stream: Assembler::new(),
+51            crypto_offset: 0,
+52            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+53        }
+54    }
+55
+56    #[cfg(test)]
+57    pub(super) fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+58        let number_space_0 = PacketNumberSpace::new_deterministic(now, space);
+59        Self {
+60            crypto: None,
+61            pending: Retransmits::default(),
+62            crypto_stream: Assembler::new(),
+63            crypto_offset: 0,
+64            number_spaces: BTreeMap::from([(PathId::ZERO, number_space_0)]),
+65        }
+66    }
+67
+68    /// Returns the [`PacketNumberSpace`] for a path
+69    ///
+70    /// When multipath is disabled use [`PathId::ZERO`].
+71    pub(super) fn path_space(&self, path_id: PathId) -> Option<&PacketNumberSpace> {
+72        self.number_spaces.get(&path_id)
+73    }
+74
+75    /// Returns a mutable reference to the [`PacketNumberSpace`] for a path
+76    ///
+77    /// When multipath is disabled use [`PathId::ZERO`].
+78    pub(super) fn path_space_mut(&mut self, path_id: PathId) -> Option<&mut PacketNumberSpace> {
+79        self.number_spaces.get_mut(&path_id)
+80    }
+81
+82    /// Returns the [`PacketNumberSpace`] for a path
+83    ///
+84    /// When multipath is disabled use `PathId::ZERO`.
+85    // TODO(flub): Note that this only exists as `&mut self` because it creates a new
+86    //    [`PacketNumberSpace`] if one is not yet available for a path.  This forces a few
+87    //    more `&mut` references to users than strictly needed.  An alternative would be to
+88    //    return an Option but that would need to be handled for all callers.  This could be
+89    //    worth exploring once we have all the main multipath bits fitted.
+90    pub(super) fn for_path(&mut self, path: PathId) -> &mut PacketNumberSpace {
+91        self.number_spaces
+92            .get_mut(&path)
+93            .unwrap_or_else(|| panic!("PacketNumberSpace missing for {path}"))
+94    }
+95
+96    pub(super) fn iter_paths_mut(&mut self) -> impl Iterator<Item = &mut PacketNumberSpace> {
+97        self.number_spaces.values_mut()
+98    }
+99
+100    /// Queue data for a tail loss probe (or anti-amplification deadlock prevention) packet
+101    ///
+102    /// Does nothing if no tail loss probe needs to be sent.
+103    ///
+104    /// Probes are sent similarly to normal packets when an expected ACK has not arrived. We never
+105    /// deem a packet lost until we receive an ACK that should have included it, but if a trailing
+106    /// run of packets (or their ACKs) are lost, this might not happen in a timely fashion. We send
+107    /// probe packets to force an ACK, and exempt them from congestion control to prevent a deadlock
+108    /// when the congestion window is filled with lost tail packets.
+109    ///
+110    /// We prefer to send new data, to make the most efficient use of bandwidth. If there's no data
+111    /// waiting to be sent, then we retransmit in-flight data to reduce odds of loss. If there's no
+112    /// in-flight data either, we're probably a client guarding against a handshake
+113    /// anti-amplification deadlock and we just make something up.
+114    pub(super) fn maybe_queue_probe(
+115        &mut self,
+116        path_id: PathId,
+117        request_immediate_ack: bool,
+118        streams: &StreamsState,
+119    ) {
+120        if self.for_path(path_id).loss_probes == 0 {
+121            return;
+122        }
+123
+124        if request_immediate_ack {
+125            // The probe should be ACKed without delay (should only be used in the Data space and
+126            // when the peer supports the acknowledgement frequency extension)
+127            self.for_path(path_id).immediate_ack_pending = true;
+128        }
+129
+130        // Retransmit the data of the oldest in-flight packet
+131        if !self.pending.is_empty(streams) {
+132            // There's real data to send here, no need to make something up
+133            return;
+134        }
+135
+136        // We use retransmits from any path.
+137        for packet in self
+138            .number_spaces
+139            .values_mut()
+140            .flat_map(|s| s.sent_packets.values_mut())
+141        {
+142            if !packet.retransmits.is_empty(streams) {
+143                // Remove retransmitted data from the old packet so we don't end up retransmitting
+144                // it *again* even if the copy we're sending now gets acknowledged.
+145                self.pending |= mem::take(&mut packet.retransmits);
+146                return;
+147            }
+148        }
+149
+150        // Nothing new to send and nothing to retransmit, so fall back on a ping. This should only
+151        // happen in rare cases during the handshake when the server becomes blocked by
+152        // anti-amplification.
+153        if !self.for_path(path_id).immediate_ack_pending {
+154            self.for_path(path_id).ping_pending = true;
+155        }
+156    }
+157
+158    /// Whether there is anything to send in this space
+159    ///
+160    /// For the data space [`Connection::can_send_1rtt`] also needs to be consulted.
+161    ///
+162    /// [`Connection::can_send_1rtt`]: super::Connection::can_send_1rtt
+163    pub(super) fn can_send(&self, path_id: PathId, streams: &StreamsState) -> SendableFrames {
+164        let acks = self
+165            .number_spaces
+166            .values()
+167            .any(|pns| pns.pending_acks.can_send());
+168        let path_exclusive = self
+169            .number_spaces
+170            .get(&path_id)
+171            .is_some_and(|s| s.ping_pending || s.immediate_ack_pending);
+172        let other = !self.pending.is_empty(streams) || path_exclusive;
+173        SendableFrames {
+174            acks,
+175            other,
+176            close: false,
+177            path_exclusive,
+178        }
+179    }
+180
+181    /// The number of packets sent with the current crypto keys
+182    ///
+183    /// Used to know if a key update is needed.
+184    pub(super) fn sent_with_keys(&self) -> u64 {
+185        self.number_spaces.values().map(|s| s.sent_with_keys).sum()
+186    }
+187}
+188
+189impl Index<SpaceId> for [PacketSpace; 3] {
+190    type Output = PacketSpace;
+191    fn index(&self, space: SpaceId) -> &PacketSpace {
+192        &self.as_ref()[space as usize]
+193    }
+194}
+195
+196impl IndexMut<SpaceId> for [PacketSpace; 3] {
+197    fn index_mut(&mut self, space: SpaceId) -> &mut PacketSpace {
+198        &mut self.as_mut()[space as usize]
+199    }
+200}
+201
+202/// The per-path packet number space to support multipath.
+203///
+204/// This contains the data specific to a per-path packet number space.  You should access
+205/// this via [`PacketSpace::for_path`].
+206pub(super) struct PacketNumberSpace {
+207    /// Highest received packet number, if any
+208    pub(super) rx_packet: Option<u64>,
+209    /// The packet number of the next packet that will be sent, if any. In the Data space, the
+210    /// packet number stored here is sometimes skipped by [`PacketNumberFilter`] logic.
+211    pub(super) next_packet_number: u64,
+212    /// The largest packet number the remote peer acknowledged in an ACK frame.
+213    pub(super) largest_acked_packet: Option<u64>,
+214    pub(super) largest_acked_packet_sent: Instant,
+215    /// The highest-numbered ACK-eliciting packet we've sent
+216    pub(super) largest_ack_eliciting_sent: u64,
+217    /// Number of packets in `sent_packets` with numbers above `largest_ack_eliciting_sent`
+218    pub(super) unacked_non_ack_eliciting_tail: u64,
+219    /// Transmitted but not acked
+220    // We use a BTreeMap here so we can efficiently query by range on ACK and for loss detection
+221    pub(super) sent_packets: SortedIndexBuffer<SentPacket>,
+222    /// Packets that were deemed lost
+223    // Older packets are regularly removed in `Connection::drain_lost_packets`.
+224    pub(super) lost_packets: SortedIndexBuffer<LostPacket>,
+225    /// Number of explicit congestion notification codepoints seen on incoming packets
+226    pub(super) ecn_counters: frame::EcnCounts,
+227    /// Recent ECN counters sent by the peer in ACK frames
+228    ///
+229    /// Updated (and inspected) whenever we receive an ACK with a new highest acked packet
+230    /// number. Stored per-space to simplify verification, which would otherwise have difficulty
+231    /// distinguishing between ECN bleaching and counts having been updated by a near-simultaneous
+232    /// ACK already processed in another space.
+233    pub(super) ecn_feedback: frame::EcnCounts,
+234    /// Number of packets sent in the current key phase
+235    pub(super) sent_with_keys: u64,
+236    /// A PING frame needs to be sent on this path
+237    pub(super) ping_pending: bool,
+238    /// An IMMEDIATE_ACK (draft-ietf-quic-ack-frequency) frame needs to be sent on this path
+239    pub(super) immediate_ack_pending: bool,
+240    /// Packet deduplicator
+241    pub(super) dedup: Dedup,
+242    /// Packet numbers to acknowledge
+243    pub(super) pending_acks: PendingAcks,
+244
+245    //
+246    // Loss Detection
+247    //
+248    /// The time the most recently sent retransmittable packet was sent.
+249    pub(super) time_of_last_ack_eliciting_packet: Option<Instant>,
+250    /// Earliest time when we might declare a packet lost.
+251    ///
+252    /// The time at which the earliest sent packet in this space will be considered lost
+253    /// based on exceeding the reordering window in time. Only set for packets numbered
+254    /// prior to a packet that has been acknowledged.
+255    pub(super) loss_time: Option<Instant>,
+256    /// Number of tail loss probes to send
+257    pub(super) loss_probes: u32,
+258
+259    /// Packet numbers to skip, only used in the data package space.
+260    pn_filter: Option<PacketNumberFilter>,
+261}
+262
+263impl PacketNumberSpace {
+264    pub(super) fn new(now: Instant, space: SpaceId, rng: &mut (impl Rng + ?Sized)) -> Self {
+265        let pn_filter = match space {
+266            SpaceId::Initial | SpaceId::Handshake => None,
+267            SpaceId::Data => Some(PacketNumberFilter::new(rng)),
+268        };
+269        Self {
+270            rx_packet: None,
+271            next_packet_number: 0,
+272            largest_acked_packet: None,
+273            largest_acked_packet_sent: now,
+274            largest_ack_eliciting_sent: 0,
+275            unacked_non_ack_eliciting_tail: 0,
+276            sent_packets: SortedIndexBuffer::new(),
+277            lost_packets: SortedIndexBuffer::new(),
+278            ecn_counters: frame::EcnCounts::ZERO,
+279            ecn_feedback: frame::EcnCounts::ZERO,
+280            sent_with_keys: 0,
+281            ping_pending: false,
+282            immediate_ack_pending: false,
+283            dedup: Default::default(),
+284            pending_acks: PendingAcks::new(),
+285            time_of_last_ack_eliciting_packet: None,
+286            loss_time: None,
+287            loss_probes: 0,
+288            pn_filter,
+289        }
+290    }
+291
+292    #[cfg(test)]
+293    fn new_deterministic(now: Instant, space: SpaceId) -> Self {
+294        let pn_filter = match space {
+295            SpaceId::Initial | SpaceId::Handshake => None,
+296            SpaceId::Data => Some(PacketNumberFilter::disabled()),
+297        };
+298        Self {
+299            rx_packet: None,
+300            next_packet_number: 0,
+301            largest_acked_packet: None,
+302            largest_acked_packet_sent: now,
+303            largest_ack_eliciting_sent: 0,
+304            unacked_non_ack_eliciting_tail: 0,
+305            sent_packets: SortedIndexBuffer::new(),
+306            lost_packets: SortedIndexBuffer::new(),
+307            ecn_counters: frame::EcnCounts::ZERO,
+308            ecn_feedback: frame::EcnCounts::ZERO,
+309            sent_with_keys: 0,
+310            ping_pending: false,
+311            immediate_ack_pending: false,
+312            dedup: Default::default(),
+313            pending_acks: PendingAcks::new(),
+314            time_of_last_ack_eliciting_packet: None,
+315            loss_time: None,
+316            loss_probes: 0,
+317            pn_filter,
+318        }
+319    }
+320
+321    /// Get the next outgoing packet number in this space
+322    ///
+323    /// In the Data space, the connection's [`PacketNumberFilter`] must be used rather than calling
+324    /// this directly.
+325    pub(super) fn get_tx_number(&mut self, rng: &mut (impl Rng + ?Sized)) -> u64 {
+326        // TODO: Handle packet number overflow gracefully
+327        assert!(self.next_packet_number < 2u64.pow(62));
+328        let mut pn = self.next_packet_number;
+329        self.next_packet_number += 1;
+330        self.sent_with_keys += 1;
+331
+332        // Skip this number if the filter says so, only enabled in the data space
+333        if let Some(ref mut filter) = self.pn_filter {
+334            if filter.skip_pn(pn, rng) {
+335                pn = self.next_packet_number;
+336                self.next_packet_number += 1;
+337                self.sent_with_keys += 1;
+338            }
+339        }
+340        pn
+341    }
+342
+343    pub(super) fn peek_tx_number(&mut self) -> u64 {
+344        let pn = self.next_packet_number;
+345        if let Some(ref filter) = self.pn_filter {
+346            if pn == filter.next_skipped_packet_number {
+347                return pn + 1;
+348            }
+349        }
+350        pn
+351    }
+352
+353    /// Checks whether a skipped packet number was ACKed.
+354    pub(super) fn check_ack(
+355        &self,
+356        range: std::ops::RangeInclusive<u64>,
+357    ) -> Result<(), TransportError> {
+358        if let Some(ref filter) = self.pn_filter {
+359            if filter
+360                .prev_skipped_packet_number
+361                .is_some_and(|pn| range.contains(&pn))
+362            {
+363                return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked"));
+364            }
+365        }
+366        Ok(())
+367    }
+368
+369    /// Verifies sanity of an ECN block and returns whether congestion was encountered.
+370    pub(super) fn detect_ecn(
+371        &mut self,
+372        newly_acked: u64,
+373        ecn: frame::EcnCounts,
+374    ) -> Result<bool, &'static str> {
+375        let ect0_increase = ecn
+376            .ect0
+377            .checked_sub(self.ecn_feedback.ect0)
+378            .ok_or("peer ECT(0) count regression")?;
+379        let ect1_increase = ecn
+380            .ect1
+381            .checked_sub(self.ecn_feedback.ect1)
+382            .ok_or("peer ECT(1) count regression")?;
+383        let ce_increase = ecn
+384            .ce
+385            .checked_sub(self.ecn_feedback.ce)
+386            .ok_or("peer CE count regression")?;
+387        let total_increase = ect0_increase + ect1_increase + ce_increase;
+388        if total_increase < newly_acked {
+389            return Err("ECN bleaching");
+390        }
+391        if (ect0_increase + ce_increase) < newly_acked || ect1_increase != 0 {
+392            return Err("ECN corruption");
+393        }
+394        // If total_increase > newly_acked (which happens when ACKs are lost), this is required by
+395        // the draft so that long-term drift does not occur. If =, then the only question is whether
+396        // to count CE packets as CE or ECT0. Recording them as CE is more consistent and keeps the
+397        // congestion check obvious.
+398        self.ecn_feedback = ecn;
+399        Ok(ce_increase != 0)
+400    }
+401
+402    /// Stop tracking sent packet `number`, and return what we knew about it
+403    pub(super) fn take(&mut self, number: u64) -> Option<SentPacket> {
+404        let packet = self.sent_packets.remove(number)?;
+405        if !packet.ack_eliciting && number > self.largest_ack_eliciting_sent {
+406            self.unacked_non_ack_eliciting_tail =
+407                self.unacked_non_ack_eliciting_tail.checked_sub(1).unwrap();
+408        }
+409        Some(packet)
+410    }
+411
+412    /// May return a packet that should be forgotten
+413    pub(super) fn sent(&mut self, number: u64, packet: SentPacket) -> Option<SentPacket> {
+414        // Retain state for at most this many non-ACK-eliciting packets sent after the most recently
+415        // sent ACK-eliciting packet. We're never guaranteed to receive an ACK for those, and we
+416        // can't judge them as lost without an ACK, so to limit memory in applications which receive
+417        // packets but don't send ACK-eliciting data for long periods use we must eventually start
+418        // forgetting about them, although it might also be reasonable to just kill the connection
+419        // due to weird peer behavior.
+420        const MAX_UNACKED_NON_ACK_ELICTING_TAIL: u64 = 1_000;
+421
+422        let mut forgotten = None;
+423        if packet.ack_eliciting {
+424            self.unacked_non_ack_eliciting_tail = 0;
+425            self.largest_ack_eliciting_sent = number;
+426        } else if self.unacked_non_ack_eliciting_tail > MAX_UNACKED_NON_ACK_ELICTING_TAIL {
+427            let oldest_after_ack_eliciting = self
+428                .sent_packets
+429                .keys_range((
+430                    Bound::Excluded(self.largest_ack_eliciting_sent),
+431                    Bound::Unbounded,
+432                ))
+433                .next()
+434                .unwrap();
+435            // Per https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types,
+436            // non-ACK-eliciting packets must only contain PADDING, ACK, and CONNECTION_CLOSE
+437            // frames, which require no special handling on ACK or loss beyond removal from
+438            // in-flight counters if padded.
+439            let packet = self
+440                .sent_packets
+441                .remove(oldest_after_ack_eliciting)
+442                .unwrap();
+443            debug_assert!(!packet.ack_eliciting);
+444            forgotten = Some(packet);
+445        } else {
+446            self.unacked_non_ack_eliciting_tail += 1;
+447        }
+448
+449        self.sent_packets.insert(number, packet);
+450        forgotten
+451    }
+452
+453    /// Whether any congestion-controlled packets in this space are not yet acknowledged or lost
+454    pub(super) fn has_in_flight(&self) -> bool {
+455        // The number of non-congestion-controlled (i.e. size == 0) packets in flight at a time
+456        // should be small, since otherwise congestion control wouldn't be effective. Therefore,
+457        // this shouldn't need to visit many packets before finishing one way or another.
+458        self.sent_packets.values().any(|x| x.size != 0)
+459    }
+460}
+461
+462/// Represents one or more packets subject to retransmission
+463#[derive(Debug, Clone)]
+464pub(super) struct SentPacket {
+465    /// [`PathData::generation`](super::PathData::generation) of the path on which this packet was sent
+466    pub(super) path_generation: u64,
+467    /// The time the packet was sent.
+468    pub(super) time_sent: Instant,
+469    /// The number of bytes sent in the packet, not including UDP or IP overhead, but including QUIC
+470    /// framing overhead. Zero if this packet is not counted towards congestion control, i.e. not an
+471    /// "in flight" packet.
+472    pub(super) size: u16,
+473    /// Whether an acknowledgement is expected directly in response to this packet.
+474    pub(super) ack_eliciting: bool,
+475    /// The largest packet number acknowledged by this packet
+476    pub(super) largest_acked: FxHashMap<PathId, u64>,
+477    /// Data which needs to be retransmitted in case the packet is lost.
+478    /// The data is boxed to minimize `SentPacket` size for the typical case of
+479    /// packets only containing ACKs and STREAM frames.
+480    pub(super) retransmits: ThinRetransmits,
+481    /// Metadata for stream frames in a packet
+482    ///
+483    /// The actual application data is stored with the stream state.
+484    pub(super) stream_frames: frame::StreamMetaVec,
+485}
+486
+487/// Represents one or more packets that are deemed lost.
+488#[derive(Debug)]
+489pub(super) struct LostPacket {
+490    /// The time the packet was sent.
+491    pub(super) time_sent: Instant,
+492}
+493
+494/// Retransmittable data queue
+495#[allow(unreachable_pub)] // fuzzing only
+496#[derive(Debug, Default, Clone)]
+497pub struct Retransmits {
+498    pub(super) max_data: bool,
+499    pub(super) max_stream_id: [bool; 2],
+500    pub(super) reset_stream: Vec<(StreamId, VarInt)>,
+501    pub(super) stop_sending: Vec<frame::StopSending>,
+502    pub(super) max_stream_data: FxHashSet<StreamId>,
+503    pub(super) crypto: VecDeque<frame::Crypto>,
+504    pub(super) new_cids: Vec<IssuedCid>,
+505    pub(super) retire_cids: Vec<(PathId, u64)>,
+506    pub(super) ack_frequency: bool,
+507    pub(super) handshake_done: bool,
+508    pub(super) observed_addr: bool,
+509    /// Whether we should inform the peer we will allow higher [`PathId`]s.
+510    pub(super) max_path_id: bool,
+511    /// Whether we should inform the peer that their max [`PathId`] is blocking our attempt to open
+512    /// new paths.
+513    // TODO(@divma): we need logic to prevent sending this more than once after being ack-d once
+514    pub(super) paths_blocked: bool,
+515    /// For each enqueued NEW_TOKEN frame, a copy of the path's remote address
+516    ///
+517    /// There are 2 reasons this is unusual:
+518    ///
+519    /// - If the path changes, NEW_TOKEN frames bound for the old path are not retransmitted on the
+520    ///   new path. That is why this field stores the remote address: so that ones for old paths
+521    ///   can be filtered out.
+522    /// - If a token is lost, a new randomly generated token is re-transmitted, rather than the
+523    ///   original. This is so that if both transmissions are received, the client won't risk
+524    ///   sending the same token twice. That is why this field does _not_ store any actual token.
+525    ///
+526    /// It is true that a QUIC endpoint will only want to effectively have NEW_TOKEN frames
+527    /// enqueued for its current path at a given point in time. Based on that, we could conceivably
+528    /// change this from a vector to an `Option<(SocketAddr, usize)>` or just a `usize` or
+529    /// something. However, due to the architecture of Quinn, it is considerably simpler to not do
+530    /// that; consider what such a change would mean for implementing `BitOrAssign` on Self.
+531    pub(super) new_tokens: Vec<SocketAddr>,
+532    /// Paths which need to be abandoned
+533    pub(super) path_abandon: BTreeMap<PathId, TransportErrorCode>,
+534    /// If a [`frame::PathStatusAvailable`] and [`frame::PathStatusBackup`] need to be sent for a path
+535    pub(super) path_status: BTreeSet<PathId>,
+536    /// If a PATH_CIDS_BLOCKED frame needs to be sent for a path
+537    pub(super) path_cids_blocked: Vec<PathId>,
+538
+539    // Nat traversal data
+540    /// Addresses to report in `ADD_ADDRESS` frames
+541    pub(super) add_address: BTreeSet<AddAddress>,
+542    /// Address IDs to remove in `REMOVE_ADDRESS` frames
+543    pub(super) remove_address: BTreeSet<RemoveAddress>,
+544    /// Round and local addresses to advertise in `REACH_OUT` frames
+545    pub(super) reach_out: Option<(VarInt, Vec<(IpAddr, u16)>)>,
+546}
+547
+548impl Retransmits {
+549    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+550        !self.max_data
+551            && !self.max_stream_id.into_iter().any(|x| x)
+552            && self.reset_stream.is_empty()
+553            && self.stop_sending.is_empty()
+554            && self
+555                .max_stream_data
+556                .iter()
+557                .all(|&id| !streams.can_send_flow_control(id))
+558            && self.crypto.is_empty()
+559            && self.new_cids.is_empty()
+560            && self.retire_cids.is_empty()
+561            && !self.ack_frequency
+562            && !self.handshake_done
+563            && !self.observed_addr
+564            && self.new_tokens.is_empty()
+565            && self.path_abandon.is_empty()
+566            && self.path_status.is_empty()
+567            && !self.max_path_id
+568            && !self.paths_blocked
+569            && self.add_address.is_empty()
+570            && self.remove_address.is_empty()
+571            && self.reach_out.is_none()
+572    }
+573}
+574
+575impl ::std::ops::BitOrAssign for Retransmits {
+576    fn bitor_assign(&mut self, mut rhs: Self) {
+577        // We reduce in-stream head-of-line blocking by queueing retransmits before other data for
+578        // STREAM and CRYPTO frames.
+579        self.max_data |= rhs.max_data;
+580        for dir in Dir::iter() {
+581            self.max_stream_id[dir as usize] |= rhs.max_stream_id[dir as usize];
+582        }
+583        self.reset_stream.extend_from_slice(&rhs.reset_stream);
+584        self.stop_sending.extend_from_slice(&rhs.stop_sending);
+585        self.max_stream_data.extend(&rhs.max_stream_data);
+586        for crypto in rhs.crypto.into_iter().rev() {
+587            self.crypto.push_front(crypto);
+588        }
+589        self.new_cids.extend(&rhs.new_cids);
+590        self.retire_cids.extend(rhs.retire_cids);
+591        self.ack_frequency |= rhs.ack_frequency;
+592        self.handshake_done |= rhs.handshake_done;
+593        self.observed_addr |= rhs.observed_addr;
+594        self.new_tokens.extend_from_slice(&rhs.new_tokens);
+595        self.path_abandon.append(&mut rhs.path_abandon);
+596        self.max_path_id |= rhs.max_path_id;
+597        self.paths_blocked |= rhs.paths_blocked;
+598        self.add_address.extend(rhs.add_address.iter().copied());
+599        self.remove_address
+600            .extend(rhs.remove_address.iter().copied());
+601        // if there are two rounds, prefer the most recent reach out set
+602        let lhs_round = self.reach_out.as_ref().map(|(round, _)| *round);
+603        let rhs_round = rhs.reach_out.as_ref().map(|(round, _)| *round);
+604        match (lhs_round, rhs_round) {
+605            (None, Some(_)) => self.reach_out = rhs.reach_out.clone(),
+606            (Some(lhs_round), Some(rhs_round)) if rhs_round > lhs_round => {
+607                self.reach_out = rhs.reach_out.clone()
+608            }
+609            _ => {}
+610        }
+611    }
+612}
+613
+614impl ::std::ops::BitOrAssign<ThinRetransmits> for Retransmits {
+615    fn bitor_assign(&mut self, rhs: ThinRetransmits) {
+616        if let Some(retransmits) = rhs.retransmits {
+617            self.bitor_assign(*retransmits)
+618        }
+619    }
+620}
+621
+622impl ::std::iter::FromIterator<Self> for Retransmits {
+623    fn from_iter<T>(iter: T) -> Self
+624    where
+625        T: IntoIterator<Item = Self>,
+626    {
+627        let mut result = Self::default();
+628        for packet in iter {
+629            result |= packet;
+630        }
+631        result
+632    }
+633}
+634
+635/// A variant of `Retransmits` which only allocates storage when required
+636#[derive(Debug, Default, Clone)]
+637pub(super) struct ThinRetransmits {
+638    retransmits: Option<Box<Retransmits>>,
+639}
+640
+641impl ThinRetransmits {
+642    /// Returns `true` if no retransmits are necessary
+643    pub(super) fn is_empty(&self, streams: &StreamsState) -> bool {
+644        match &self.retransmits {
+645            Some(retransmits) => retransmits.is_empty(streams),
+646            None => true,
+647        }
+648    }
+649
+650    /// Returns a reference to the retransmits stored in this box
+651    pub(super) fn get(&self) -> Option<&Retransmits> {
+652        self.retransmits.as_deref()
+653    }
+654
+655    /// Returns a mutable reference to the retransmits stored in this box
+656    pub(super) fn get_mut(&mut self) -> Option<&mut Retransmits> {
+657        self.retransmits.as_deref_mut()
+658    }
+659
+660    /// Returns a mutable reference to the stored retransmits
+661    ///
+662    /// This function will allocate a backing storage if required.
+663    pub(super) fn get_or_create(&mut self) -> &mut Retransmits {
+664        if self.retransmits.is_none() {
+665            self.retransmits = Some(Box::default());
+666        }
+667        self.retransmits.as_deref_mut().unwrap()
+668    }
+669}
+670
+671/// RFC4303-style sliding window packet number deduplicator.
+672///
+673/// A contiguous bitfield, where each bit corresponds to a packet number and the rightmost bit is
+674/// always set. A set bit represents a packet that has been successfully authenticated. Bits left of
+675/// the window are assumed to be set.
+676///
+677/// ```text
+678/// ...xxxxxxxxx 1 0
+679///     ^        ^ ^
+680/// window highest next
+681/// ```
+682#[derive(Debug, Default)]
+683pub(super) struct Dedup {
+684    window: Window,
+685    /// Lowest packet number higher than all yet authenticated.
+686    next: u64,
+687}
+688
+689/// Inner bitfield type.
+690///
+691/// Because QUIC never reuses packet numbers, this only needs to be large enough to deal with
+692/// packets that are reordered but still delivered in a timely manner.
+693type Window = u128;
+694
+695/// Number of packets tracked by `Dedup`.
+696const WINDOW_SIZE: u64 = 1 + mem::size_of::<Window>() as u64 * 8;
+697
+698impl Dedup {
+699    /// Construct an empty window positioned at the start.
+700    #[cfg(test)]
+701    pub(super) fn new() -> Self {
+702        Self { window: 0, next: 0 }
+703    }
+704
+705    /// Highest packet number authenticated.
+706    fn highest(&self) -> u64 {
+707        self.next - 1
+708    }
+709
+710    /// Record a newly authenticated packet number.
+711    ///
+712    /// Returns whether the packet might be a duplicate.
+713    pub(super) fn insert(&mut self, packet: u64) -> bool {
+714        if let Some(diff) = packet.checked_sub(self.next) {
+715            // Right of window
+716            self.window = ((self.window << 1) | 1)
+717                .checked_shl(cmp::min(diff, u64::from(u32::MAX)) as u32)
+718                .unwrap_or(0);
+719            self.next = packet + 1;
+720            false
+721        } else if self.highest() - packet < WINDOW_SIZE {
+722            // Within window
+723            if let Some(bit) = (self.highest() - packet).checked_sub(1) {
+724                // < highest
+725                let mask = 1 << bit;
+726                let duplicate = self.window & mask != 0;
+727                self.window |= mask;
+728                duplicate
+729            } else {
+730                // == highest
+731                true
+732            }
+733        } else {
+734            // Left of window
+735            true
+736        }
+737    }
+738
+739    /// Returns the packet number of the smallest packet missing between the provided interval
+740    ///
+741    /// If there are no missing packets, returns `None`
+742    fn smallest_missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> Option<u64> {
+743        debug_assert!(lower_bound <= upper_bound);
+744        debug_assert!(upper_bound <= self.highest());
+745        const BITFIELD_SIZE: u64 = (mem::size_of::<Window>() * 8) as u64;
+746
+747        // Since we already know the packets at the boundaries have been received, we only need to
+748        // check those in between them (this removes the necessity of extra logic to deal with the
+749        // highest packet, which is stored outside the bitfield)
+750        let lower_bound = lower_bound + 1;
+751        let upper_bound = upper_bound.saturating_sub(1);
+752
+753        // Note: the offsets are counted from the right
+754        // The highest packet is not included in the bitfield, so we subtract 1 to account for that
+755        let start_offset = (self.highest() - upper_bound).max(1) - 1;
+756        if start_offset >= BITFIELD_SIZE {
+757            // The start offset is outside of the window. All packets outside of the window are
+758            // considered to be received.
+759            return None;
+760        }
+761
+762        let end_offset_exclusive = self.highest().saturating_sub(lower_bound);
+763
+764        // The range is clamped at the edge of the window, because any earlier packets are
+765        // considered to be received
+766        let range_len = end_offset_exclusive
+767            .saturating_sub(start_offset)
+768            .min(BITFIELD_SIZE);
+769        if range_len == 0 {
+770            return None;
+771        }
+772
+773        // Ensure the shift is within bounds (we already know start_offset < BITFIELD_SIZE,
+774        // because of the early return)
+775        let mask = if range_len == BITFIELD_SIZE {
+776            u128::MAX
+777        } else {
+778            ((1u128 << range_len) - 1) << start_offset
+779        };
+780        let gaps = !self.window & mask;
+781
+782        let smallest_missing_offset = 128 - gaps.leading_zeros() as u64;
+783        let smallest_missing_packet = self.highest() - smallest_missing_offset;
+784
+785        if smallest_missing_packet <= upper_bound {
+786            Some(smallest_missing_packet)
+787        } else {
+788            None
+789        }
+790    }
+791
+792    /// Returns true if there are any missing packets between the provided interval
+793    ///
+794    /// The provided packet numbers must have been received before calling this function
+795    fn missing_in_interval(&self, lower_bound: u64, upper_bound: u64) -> bool {
+796        self.smallest_missing_in_interval(lower_bound, upper_bound)
+797            .is_some()
+798    }
+799}
+800
+801/// Indicates which data is available for sending
+802#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+803pub(super) struct SendableFrames {
+804    /// Whether there ACK frames to send, these are not ack-eliciting
+805    pub(super) acks: bool,
+806    /// Whether there are any other frames to send, these are ack-eliciting
+807    pub(super) other: bool,
+808    /// Whether there is a CONNECTION_CLOSE to send, this is not ack-eliciting
+809    pub(super) close: bool,
+810    /// Whether there are frames to send, which can only be sent on the path queried
+811    ///
+812    /// These are ack-eliciting, and a subset of [`SendableFrames::other`].  This is useful
+813    /// for QUIC-MULTIPATH, which may desire not to send any frames on a backup path, which
+814    /// can also be sent on an active path.
+815    pub(super) path_exclusive: bool,
+816}
+817
+818impl SendableFrames {
+819    /// Returns that no data is available for sending
+820    pub(super) fn empty() -> Self {
+821        Self {
+822            acks: false,
+823            other: false,
+824            close: false,
+825            path_exclusive: false,
+826        }
+827    }
+828
+829    /// Whether no data is sendable
+830    pub(super) fn is_empty(&self) -> bool {
+831        !self.acks && !self.other && !self.close && !self.path_exclusive
+832    }
+833}
+834
+835impl ::std::ops::BitOrAssign for SendableFrames {
+836    fn bitor_assign(&mut self, rhs: Self) {
+837        self.acks |= rhs.acks;
+838        self.other |= rhs.other;
+839        self.close |= rhs.close;
+840        self.path_exclusive |= rhs.path_exclusive;
+841    }
+842}
+843
+844#[derive(Debug)]
+845pub(super) struct PendingAcks {
+846    /// Whether we should send an ACK immediately, even if that means sending an ACK-only packet
+847    ///
+848    /// When `immediate_ack_required` is false, the normal behavior is to send ACK frames only when
+849    /// there is other data to send, or when the `MaxAckDelay` timer expires.
+850    immediate_ack_required: bool,
+851    /// The number of ack-eliciting packets received since the last ACK frame was sent
+852    ///
+853    /// Once the count _exceeds_ `ack_eliciting_threshold`, an immediate ACK is required
+854    ack_eliciting_since_last_ack_sent: u64,
+855    non_ack_eliciting_since_last_ack_sent: u64,
+856    ack_eliciting_threshold: u64,
+857    /// The reordering threshold, controlling how we respond to out-of-order ack-eliciting packets
+858    ///
+859    /// Different values enable different behavior:
+860    ///
+861    /// * `0`: no special action is taken
+862    /// * `1`: an ACK is immediately sent if it is out-of-order according to RFC 9000
+863    /// * `>1`: an ACK is immediately sent if it is out-of-order according to the ACK frequency draft
+864    reordering_threshold: u64,
+865    /// The earliest ack-eliciting packet since the last ACK was sent, used to calculate the moment
+866    /// upon which `max_ack_delay` elapses
+867    earliest_ack_eliciting_since_last_ack_sent: Option<Instant>,
+868    /// Packet number ranges for which to still send acknowledgements.
+869    ///
+870    /// These are packet number ranges of ack-eliciting packets the peer has sent and which
+871    /// need to be acknowledged.  Packet numbers are only removed from here once the peer has
+872    /// acknowledged the ACKs for them.
+873    ranges: ArrayRangeSet,
+874    /// The largest packet number received and the time it was received
+875    ///
+876    /// Used to calculate ACK delay in [`PendingAcks::ack_delay`].
+877    largest_packet: Option<(u64, Instant)>,
+878    /// The ack-eliciting packet we have received with the largest packet number
+879    largest_ack_eliciting_packet: Option<u64>,
+880    /// The largest acknowledged packet number sent in an ACK frame
+881    largest_acked: Option<u64>,
+882}
+883
+884impl PendingAcks {
+885    fn new() -> Self {
+886        Self {
+887            immediate_ack_required: false,
+888            ack_eliciting_since_last_ack_sent: 0,
+889            non_ack_eliciting_since_last_ack_sent: 0,
+890            ack_eliciting_threshold: 1,
+891            reordering_threshold: 1,
+892            earliest_ack_eliciting_since_last_ack_sent: None,
+893            ranges: Default::default(),
+894            largest_packet: Default::default(),
+895            largest_ack_eliciting_packet: Default::default(),
+896            largest_acked: Default::default(),
+897        }
+898    }
+899
+900    pub(super) fn set_ack_frequency_params(&mut self, frame: &frame::AckFrequency) {
+901        self.ack_eliciting_threshold = frame.ack_eliciting_threshold.into_inner();
+902        self.reordering_threshold = frame.reordering_threshold.into_inner();
+903    }
+904
+905    pub(super) fn set_immediate_ack_required(&mut self) {
+906        self.immediate_ack_required = true;
+907    }
+908
+909    pub(super) fn on_max_ack_delay_timeout(&mut self) {
+910        self.immediate_ack_required = self.ack_eliciting_since_last_ack_sent > 0;
+911    }
+912
+913    pub(super) fn max_ack_delay_timeout(&self, max_ack_delay: Duration) -> Option<Instant> {
+914        self.earliest_ack_eliciting_since_last_ack_sent
+915            .map(|earliest_unacked| earliest_unacked + max_ack_delay)
+916    }
+917
+918    /// Whether any ACK frames SHOULD be sent
+919    ///
+920    /// This is used in the top-level [`Connection::space_can_send`], so determines if a
+921    /// packet will be built. It is often possible to construct new ACK ranges to send
+922    /// before this returns `true`. This results in more ACK frames being sent, and
+923    /// processing those at the receiver costs CPU for very little improvements.
+924    ///
+925    /// [`Connection::space_can_send`]: super::Connection::space_can_send
+926    pub(super) fn can_send(&self) -> bool {
+927        self.immediate_ack_required && !self.ranges.is_empty()
+928    }
+929
+930    /// Returns the delay since the packet with the largest packet number was received
+931    pub(super) fn ack_delay(&self, now: Instant) -> Duration {
+932        self.largest_packet
+933            .map_or(Duration::default(), |(_, received)| now - received)
+934    }
+935
+936    /// Handle receipt of a new packet
+937    ///
+938    /// Returns true if the max ack delay timer should be armed
+939    pub(super) fn packet_received(
+940        &mut self,
+941        now: Instant,
+942        packet_number: u64,
+943        ack_eliciting: bool,
+944        dedup: &Dedup,
+945    ) -> bool {
+946        if !ack_eliciting {
+947            self.non_ack_eliciting_since_last_ack_sent += 1;
+948            return false;
+949        }
+950
+951        let prev_largest_ack_eliciting = self.largest_ack_eliciting_packet.unwrap_or(0);
+952
+953        // Track largest ack-eliciting packet
+954        self.largest_ack_eliciting_packet = self
+955            .largest_ack_eliciting_packet
+956            .map(|pn| pn.max(packet_number))
+957            .or(Some(packet_number));
+958
+959        // Handle ack_eliciting_threshold
+960        self.ack_eliciting_since_last_ack_sent += 1;
+961        self.immediate_ack_required |=
+962            self.ack_eliciting_since_last_ack_sent > self.ack_eliciting_threshold;
+963
+964        // Handle out-of-order packets
+965        self.immediate_ack_required |=
+966            self.is_out_of_order(packet_number, prev_largest_ack_eliciting, dedup);
+967
+968        // Arm max_ack_delay timer if necessary
+969        if self.earliest_ack_eliciting_since_last_ack_sent.is_none() && !self.can_send() {
+970            self.earliest_ack_eliciting_since_last_ack_sent = Some(now);
+971            return true;
+972        }
+973
+974        false
+975    }
+976
+977    fn is_out_of_order(
+978        &self,
+979        packet_number: u64,
+980        prev_largest_ack_eliciting: u64,
+981        dedup: &Dedup,
+982    ) -> bool {
+983        match self.reordering_threshold {
+984            0 => false,
+985            1 => {
+986                // From https://www.rfc-editor.org/rfc/rfc9000#section-13.2.1-7
+987                packet_number < prev_largest_ack_eliciting
+988                    || dedup.missing_in_interval(prev_largest_ack_eliciting, packet_number)
+989            }
+990            _ => {
+991                // From acknowledgement frequency draft, section 6.1: send an ACK immediately if
+992                // doing so would cause the sender to detect a new packet loss
+993                let Some((largest_acked, largest_unacked)) =
+994                    self.largest_acked.zip(self.largest_ack_eliciting_packet)
+995                else {
+996                    return false;
+997                };
+998                if self.reordering_threshold > largest_acked {
+999                    return false;
+1000                }
+1001                // The largest packet number that could be declared lost without a new ACK being
+1002                // sent
+1003                let largest_reported = largest_acked - self.reordering_threshold + 1;
+1004                let Some(smallest_missing_unreported) =
+1005                    dedup.smallest_missing_in_interval(largest_reported, largest_unacked)
+1006                else {
+1007                    return false;
+1008                };
+1009                largest_unacked - smallest_missing_unreported >= self.reordering_threshold
+1010            }
+1011        }
+1012    }
+1013
+1014    /// Should be called whenever ACKs have been sent
+1015    ///
+1016    /// This will suppress sending further ACKs until additional ACK eliciting frames arrive
+1017    pub(super) fn acks_sent(&mut self) {
+1018        // It is possible (though unlikely) that the ACKs we just sent do not cover all the
+1019        // ACK-eliciting packets we have received (e.g. if there is not enough room in the packet to
+1020        // fit all the ranges). To keep things simple, however, we assume they do. If there are
+1021        // indeed some ACKs that weren't covered, the packets might be ACKed later anyway, because
+1022        // they are still contained in `self.ranges`. If we somehow fail to send the ACKs at a later
+1023        // moment, the peer will assume the packets got lost and will retransmit their frames in a
+1024        // new packet, which is suboptimal, because we already received them. Our assumption here is
+1025        // that simplicity results in code that is more performant, even in the presence of
+1026        // occasional redundant retransmits.
+1027        self.immediate_ack_required = false;
+1028        self.ack_eliciting_since_last_ack_sent = 0;
+1029        self.non_ack_eliciting_since_last_ack_sent = 0;
+1030        self.earliest_ack_eliciting_since_last_ack_sent = None;
+1031        self.largest_acked = self.largest_ack_eliciting_packet;
+1032    }
+1033
+1034    /// Insert one packet that needs to be acknowledged
+1035    pub(super) fn insert_one(&mut self, packet: u64, now: Instant) {
+1036        self.ranges.insert_one(packet);
+1037
+1038        if self.largest_packet.is_none_or(|(pn, _)| packet > pn) {
+1039            self.largest_packet = Some((packet, now));
+1040        }
+1041
+1042        if self.ranges.len() > MAX_ACK_BLOCKS {
+1043            self.ranges.pop_min();
+1044        }
+1045    }
+1046
+1047    /// Remove ACKs of packets numbered at or below `max` from the set of pending ACKs
+1048    pub(super) fn subtract_below(&mut self, max: u64) {
+1049        self.ranges.remove(0..(max + 1));
+1050    }
+1051
+1052    /// Returns the set of currently pending ACK ranges
+1053    pub(super) fn ranges(&self) -> &ArrayRangeSet {
+1054        &self.ranges
+1055    }
+1056
+1057    /// Queue an ACK if a significant number of non-ACK-eliciting packets have not yet been
+1058    /// acknowledged
+1059    ///
+1060    /// Should be called immediately before a non-probing packet is composed, when we've already
+1061    /// committed to sending a packet regardless.
+1062    pub(super) fn maybe_ack_non_eliciting(&mut self) {
+1063        // If we're going to send a packet anyway, and we've received a significant number of
+1064        // non-ACK-eliciting packets, then include an ACK to help the peer perform timely loss
+1065        // detection even if they're not sending any ACK-eliciting packets themselves. Exact
+1066        // threshold chosen somewhat arbitrarily.
+1067        const LAZY_ACK_THRESHOLD: u64 = 10;
+1068        if self.non_ack_eliciting_since_last_ack_sent > LAZY_ACK_THRESHOLD {
+1069            self.immediate_ack_required = true;
+1070        }
+1071    }
+1072}
+1073
+1074/// Helper for mitigating [optimistic ACK attacks]
+1075///
+1076/// A malicious peer could prompt the local application to begin a large data transfer, and then
+1077/// send ACKs without first waiting for data to be received. This could defeat congestion control,
+1078/// allowing the connection to consume disproportionate resources. We therefore occasionally skip
+1079/// packet numbers, and classify any ACK referencing a skipped packet number as a transport error.
+1080///
+1081/// Skipped packet numbers occur only in the application data space (where costly transfers might
+1082/// take place) and are distributed exponentially to reflect the reduced likelihood and impact of
+1083/// bad behavior from a peer that has been well-behaved for an extended period.
+1084///
+1085/// ACKs for packet numbers that have not yet been allocated are also a transport error, but an
+1086/// attacker with knowledge of the congestion control algorithm in use could time falsified ACKs to
+1087/// arrive after the packets they reference are sent.
+1088///
+1089/// [optimistic ACK attacks]: https://www.rfc-editor.org/rfc/rfc9000.html#name-optimistic-ack-attack
+1090pub(super) struct PacketNumberFilter {
+1091    /// Next outgoing packet number to skip
+1092    next_skipped_packet_number: u64,
+1093    /// Most recently skipped packet number
+1094    prev_skipped_packet_number: Option<u64>,
+1095    /// Next packet number to skip is randomly selected from 2^n..2^n+1
+1096    exponent: u32,
+1097}
+1098
+1099impl PacketNumberFilter {
+1100    pub(super) fn new(rng: &mut (impl Rng + ?Sized)) -> Self {
+1101        // First skipped PN is in 0..64
+1102        let exponent = 6;
+1103        Self {
+1104            next_skipped_packet_number: rng.random_range(0..2u64.saturating_pow(exponent)),
+1105            prev_skipped_packet_number: None,
+1106            exponent,
+1107        }
+1108    }
+1109
+1110    #[cfg(test)]
+1111    pub(super) fn disabled() -> Self {
+1112        Self {
+1113            next_skipped_packet_number: u64::MAX,
+1114            prev_skipped_packet_number: None,
+1115            exponent: u32::MAX,
+1116        }
+1117    }
+1118
+1119    /// Whether to use the provided packet number (false) or to skip it (true)
+1120    pub(super) fn skip_pn(&mut self, n: u64, rng: &mut (impl Rng + ?Sized)) -> bool {
+1121        if n != self.next_skipped_packet_number {
+1122            return false;
+1123        }
+1124
+1125        trace!("skipping pn {n}");
+1126        // Skip this packet number, and choose the next one to skip
+1127        self.prev_skipped_packet_number = Some(self.next_skipped_packet_number);
+1128        let next_exponent = self.exponent.saturating_add(1);
+1129        self.next_skipped_packet_number = rng
+1130            .random_range(2u64.saturating_pow(self.exponent)..2u64.saturating_pow(next_exponent));
+1131        self.exponent = next_exponent;
+1132        true
+1133    }
+1134}
+1135
+1136/// Ensures we can always fit all our ACKs in a single minimum-MTU packet with room to spare
+1137const MAX_ACK_BLOCKS: usize = 64;
+1138
+1139#[cfg(test)]
+1140mod test {
+1141    use super::*;
+1142
+1143    #[test]
+1144    fn sanity() {
+1145        let mut dedup = Dedup::new();
+1146        assert!(!dedup.insert(0));
+1147        assert_eq!(dedup.next, 1);
+1148        assert_eq!(dedup.window, 0b1);
+1149        assert!(dedup.insert(0));
+1150        assert_eq!(dedup.next, 1);
+1151        assert_eq!(dedup.window, 0b1);
+1152        assert!(!dedup.insert(1));
+1153        assert_eq!(dedup.next, 2);
+1154        assert_eq!(dedup.window, 0b11);
+1155        assert!(!dedup.insert(2));
+1156        assert_eq!(dedup.next, 3);
+1157        assert_eq!(dedup.window, 0b111);
+1158        assert!(!dedup.insert(4));
+1159        assert_eq!(dedup.next, 5);
+1160        assert_eq!(dedup.window, 0b11110);
+1161        assert!(!dedup.insert(7));
+1162        assert_eq!(dedup.next, 8);
+1163        assert_eq!(dedup.window, 0b1111_0100);
+1164        assert!(dedup.insert(4));
+1165        assert!(!dedup.insert(3));
+1166        assert_eq!(dedup.next, 8);
+1167        assert_eq!(dedup.window, 0b1111_1100);
+1168        assert!(!dedup.insert(6));
+1169        assert_eq!(dedup.next, 8);
+1170        assert_eq!(dedup.window, 0b1111_1101);
+1171        assert!(!dedup.insert(5));
+1172        assert_eq!(dedup.next, 8);
+1173        assert_eq!(dedup.window, 0b1111_1111);
+1174    }
+1175
+1176    #[test]
+1177    fn happypath() {
+1178        let mut dedup = Dedup::new();
+1179        for i in 0..(2 * WINDOW_SIZE) {
+1180            assert!(!dedup.insert(i));
+1181            for j in 0..=i {
+1182                assert!(dedup.insert(j));
+1183            }
+1184        }
+1185    }
+1186
+1187    #[test]
+1188    fn jump() {
+1189        let mut dedup = Dedup::new();
+1190        dedup.insert(2 * WINDOW_SIZE);
+1191        assert!(dedup.insert(WINDOW_SIZE));
+1192        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1193        assert_eq!(dedup.window, 0);
+1194        assert!(!dedup.insert(WINDOW_SIZE + 1));
+1195        assert_eq!(dedup.next, 2 * WINDOW_SIZE + 1);
+1196        assert_eq!(dedup.window, 1 << (WINDOW_SIZE - 2));
+1197    }
+1198
+1199    #[test]
+1200    fn dedup_has_missing() {
+1201        let mut dedup = Dedup::new();
+1202
+1203        dedup.insert(0);
+1204        assert!(!dedup.missing_in_interval(0, 0));
+1205
+1206        dedup.insert(1);
+1207        assert!(!dedup.missing_in_interval(0, 1));
+1208
+1209        dedup.insert(3);
+1210        assert!(dedup.missing_in_interval(1, 3));
+1211
+1212        dedup.insert(4);
+1213        assert!(!dedup.missing_in_interval(3, 4));
+1214        assert!(dedup.missing_in_interval(0, 4));
+1215
+1216        dedup.insert(2);
+1217        assert!(!dedup.missing_in_interval(0, 4));
+1218    }
+1219
+1220    #[test]
+1221    fn dedup_outside_of_window_has_missing() {
+1222        let mut dedup = Dedup::new();
+1223
+1224        for i in 0..140 {
+1225            dedup.insert(i);
+1226        }
+1227
+1228        // 0 and 4 are outside of the window
+1229        assert!(!dedup.missing_in_interval(0, 4));
+1230        dedup.insert(160);
+1231        assert!(!dedup.missing_in_interval(0, 4));
+1232        assert!(!dedup.missing_in_interval(0, 140));
+1233        assert!(dedup.missing_in_interval(0, 160));
+1234    }
+1235
+1236    #[test]
+1237    fn dedup_smallest_missing() {
+1238        let mut dedup = Dedup::new();
+1239
+1240        dedup.insert(0);
+1241        assert_eq!(dedup.smallest_missing_in_interval(0, 0), None);
+1242
+1243        dedup.insert(1);
+1244        assert_eq!(dedup.smallest_missing_in_interval(0, 1), None);
+1245
+1246        dedup.insert(5);
+1247        dedup.insert(7);
+1248        assert_eq!(dedup.smallest_missing_in_interval(0, 7), Some(2));
+1249        assert_eq!(dedup.smallest_missing_in_interval(5, 7), Some(6));
+1250
+1251        dedup.insert(2);
+1252        assert_eq!(dedup.smallest_missing_in_interval(1, 7), Some(3));
+1253
+1254        dedup.insert(170);
+1255        dedup.insert(172);
+1256        dedup.insert(300);
+1257        assert_eq!(dedup.smallest_missing_in_interval(170, 172), None);
+1258
+1259        dedup.insert(500);
+1260        assert_eq!(dedup.smallest_missing_in_interval(0, 500), Some(372));
+1261        assert_eq!(dedup.smallest_missing_in_interval(0, 373), Some(372));
+1262        assert_eq!(dedup.smallest_missing_in_interval(0, 372), None);
+1263    }
+1264
+1265    #[test]
+1266    fn pending_acks_first_packet_is_not_considered_reordered() {
+1267        let mut acks = PendingAcks::new();
+1268        let mut dedup = Dedup::new();
+1269        dedup.insert(0);
+1270        acks.packet_received(Instant::now(), 0, true, &dedup);
+1271        assert!(!acks.immediate_ack_required);
+1272    }
+1273
+1274    #[test]
+1275    fn pending_acks_after_immediate_ack_set() {
+1276        let mut acks = PendingAcks::new();
+1277        let mut dedup = Dedup::new();
+1278
+1279        // Receive ack-eliciting packet
+1280        dedup.insert(0);
+1281        let now = Instant::now();
+1282        acks.insert_one(0, now);
+1283        acks.packet_received(now, 0, true, &dedup);
+1284
+1285        // Sanity check
+1286        assert!(!acks.ranges.is_empty());
+1287        assert!(!acks.can_send());
+1288
+1289        // Can send ACK after max_ack_delay exceeded
+1290        acks.set_immediate_ack_required();
+1291        assert!(acks.can_send());
+1292    }
+1293
+1294    #[test]
+1295    fn pending_acks_ack_delay() {
+1296        let mut acks = PendingAcks::new();
+1297        let mut dedup = Dedup::new();
+1298
+1299        let t1 = Instant::now();
+1300        let t2 = t1 + Duration::from_millis(2);
+1301        let t3 = t2 + Duration::from_millis(5);
+1302        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1303        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1304        assert_eq!(acks.ack_delay(t3), Duration::from_millis(0));
+1305
+1306        // In-order packet
+1307        dedup.insert(0);
+1308        acks.insert_one(0, t1);
+1309        acks.packet_received(t1, 0, true, &dedup);
+1310        assert_eq!(acks.ack_delay(t1), Duration::from_millis(0));
+1311        assert_eq!(acks.ack_delay(t2), Duration::from_millis(2));
+1312        assert_eq!(acks.ack_delay(t3), Duration::from_millis(7));
+1313
+1314        // Out of order (higher than expected)
+1315        dedup.insert(3);
+1316        acks.insert_one(3, t2);
+1317        acks.packet_received(t2, 3, true, &dedup);
+1318        assert_eq!(acks.ack_delay(t2), Duration::from_millis(0));
+1319        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1320
+1321        // Out of order (lower than expected, so previous instant is kept)
+1322        dedup.insert(2);
+1323        acks.insert_one(2, t3);
+1324        acks.packet_received(t3, 2, true, &dedup);
+1325        assert_eq!(acks.ack_delay(t3), Duration::from_millis(5));
+1326    }
+1327
+1328    #[test]
+1329    fn sent_packet_size() {
+1330        // The tracking state of sent packets should be minimal, and not grow
+1331        // over time.
+1332        assert!(std::mem::size_of::<SentPacket>() <= 128);
+1333    }
+1334}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/state.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/state.rs.html new file mode 100644 index 000000000..9b02e5fc6 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/state.rs.html @@ -0,0 +1,346 @@ +state.rs - source

iroh_quinn_proto/connection/
state.rs

1use bytes::Bytes;
+2
+3use crate::frame::Close;
+4use crate::{ApplicationClose, ConnectionClose, ConnectionError, TransportError, TransportErrorCode};
+5
+6#[allow(unreachable_pub)] // fuzzing only
+7#[derive(Debug, Clone)]
+8pub struct State {
+9    /// Nested [`InnerState`] to enforce all state transitions are done in this module.
+10    inner: InnerState,
+11}
+12
+13impl State {
+14    pub(super) fn as_handshake_mut(&mut self) -> Option<&mut Handshake> {
+15        if let InnerState::Handshake(ref mut hs) = self.inner {
+16            Some(hs)
+17        } else {
+18            None
+19        }
+20    }
+21
+22    pub(super) fn as_handshake(&self) -> Option<&Handshake> {
+23        if let InnerState::Handshake(ref hs) = self.inner {
+24            Some(hs)
+25        } else {
+26            None
+27        }
+28    }
+29
+30    pub(super) fn as_closed(&self) -> Option<&CloseReason> {
+31        if let InnerState::Closed {
+32            ref remote_reason, ..
+33        } = self.inner
+34        {
+35            Some(remote_reason)
+36        } else {
+37            None
+38        }
+39    }
+40
+41    #[allow(unreachable_pub)] // fuzzing only
+42    #[cfg(any(test, fuzzing))]
+43    pub fn established() -> Self {
+44        Self {
+45            inner: InnerState::Established,
+46        }
+47    }
+48
+49    pub(super) fn handshake(hs: Handshake) -> Self {
+50        Self {
+51            inner: InnerState::Handshake(hs),
+52        }
+53    }
+54
+55    pub(super) fn move_to_handshake(&mut self, hs: Handshake) {
+56        self.inner = InnerState::Handshake(hs);
+57    }
+58
+59    pub(super) fn move_to_established(&mut self) {
+60        self.inner = InnerState::Established;
+61    }
+62
+63    /// Moves to a draining state.
+64    ///
+65    /// Panics if the state was already drained.
+66    pub(super) fn move_to_drained(&mut self, error: Option<ConnectionError>) {
+67        let (error, is_local) = if let Some(error) = error {
+68            (Some(error), false)
+69        } else {
+70            let error = match &mut self.inner {
+71                InnerState::Draining { error, .. } => error.take(),
+72                InnerState::Drained { .. } => panic!("invalid state transition drained -> drained"),
+73                InnerState::Closed { error_read, .. } if *error_read => None,
+74                InnerState::Closed { remote_reason, .. } => {
+75                    let error = match remote_reason.clone().into() {
+76                        ConnectionError::ConnectionClosed(close) => {
+77                            if close.error_code == TransportErrorCode::PROTOCOL_VIOLATION {
+78                                ConnectionError::TransportError(TransportError::new(
+79                                    close.error_code,
+80                                    std::string::String::from_utf8_lossy(&close.reason[..])
+81                                        .to_string(),
+82                                ))
+83                            } else {
+84                                ConnectionError::ConnectionClosed(close)
+85                            }
+86                        }
+87                        e => e,
+88                    };
+89                    Some(error)
+90                }
+91                InnerState::Handshake(_) | InnerState::Established => None,
+92            };
+93            (error, self.is_local_close())
+94        };
+95        self.inner = InnerState::Drained { error, is_local };
+96    }
+97
+98    /// Moves to a draining state.
+99    ///
+100    /// Panics if the state is already draining or drained.
+101    pub(super) fn move_to_draining(&mut self, error: Option<ConnectionError>) {
+102        assert!(
+103            matches!(
+104                self.inner,
+105                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+106            ),
+107            "invalid state transition {:?} -> draining",
+108            self.as_type()
+109        );
+110        let is_local = self.is_local_close();
+111        self.inner = InnerState::Draining { error, is_local };
+112    }
+113
+114    fn is_local_close(&self) -> bool {
+115        match self.inner {
+116            InnerState::Handshake(_) => false,
+117            InnerState::Established => false,
+118            InnerState::Closed { is_local, .. } => is_local,
+119            InnerState::Draining { is_local, .. } => is_local,
+120            InnerState::Drained { is_local, .. } => is_local,
+121        }
+122    }
+123
+124    /// Moves to a closed state after a remote error is received.
+125    ///
+126    /// Panics if the state is later than established.
+127    pub(super) fn move_to_closed<R: Into<CloseReason>>(&mut self, reason: R) {
+128        assert!(
+129            matches!(
+130                self.inner,
+131                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+132            ),
+133            "invalid state transition {:?} -> closed",
+134            self.as_type()
+135        );
+136        self.inner = InnerState::Closed {
+137            error_read: false,
+138            remote_reason: reason.into(),
+139            is_local: false,
+140        };
+141    }
+142
+143    /// Moves to a closed state after a local error.
+144    ///
+145    /// Panics if the state is later than established.
+146    pub(super) fn move_to_closed_local<R: Into<CloseReason>>(&mut self, reason: R) {
+147        assert!(
+148            matches!(
+149                self.inner,
+150                InnerState::Handshake(_) | InnerState::Established | InnerState::Closed { .. }
+151            ),
+152            "invalid state transition {:?} -> closed (local)",
+153            self.as_type()
+154        );
+155        self.inner = InnerState::Closed {
+156            error_read: false,
+157            remote_reason: reason.into(),
+158            is_local: true,
+159        };
+160    }
+161
+162    pub(super) fn is_handshake(&self) -> bool {
+163        matches!(self.inner, InnerState::Handshake(_))
+164    }
+165
+166    pub(super) fn is_established(&self) -> bool {
+167        matches!(self.inner, InnerState::Established)
+168    }
+169
+170    pub(super) fn is_closed(&self) -> bool {
+171        matches!(
+172            self.inner,
+173            InnerState::Closed { .. } | InnerState::Draining { .. } | InnerState::Drained { .. }
+174        )
+175    }
+176
+177    pub(super) fn is_drained(&self) -> bool {
+178        matches!(self.inner, InnerState::Drained { .. })
+179    }
+180
+181    pub(super) fn take_error(&mut self) -> Option<ConnectionError> {
+182        match &mut self.inner {
+183            InnerState::Draining { error, is_local } => {
+184                if !*is_local {
+185                    error.take()
+186                } else {
+187                    None
+188                }
+189            }
+190            InnerState::Drained { error, is_local } => {
+191                if !*is_local {
+192                    error.take()
+193                } else {
+194                    None
+195                }
+196            }
+197            InnerState::Closed {
+198                remote_reason,
+199                is_local: local_reason,
+200                error_read,
+201            } => {
+202                if *error_read {
+203                    None
+204                } else {
+205                    *error_read = true;
+206                    if *local_reason {
+207                        None
+208                    } else {
+209                        Some(remote_reason.clone().into())
+210                    }
+211                }
+212            }
+213            InnerState::Handshake(_) | InnerState::Established => None,
+214        }
+215    }
+216
+217    pub(super) fn as_type(&self) -> StateType {
+218        match self.inner {
+219            InnerState::Handshake(_) => StateType::Handshake,
+220            InnerState::Established => StateType::Established,
+221            InnerState::Closed { .. } => StateType::Closed,
+222            InnerState::Draining { .. } => StateType::Draining,
+223            InnerState::Drained { .. } => StateType::Drained,
+224        }
+225    }
+226}
+227
+228#[derive(Debug, Clone)]
+229pub(super) enum StateType {
+230    Handshake,
+231    Established,
+232    Closed,
+233    Draining,
+234    Drained,
+235}
+236
+237#[derive(Debug, Clone)]
+238pub(super) enum CloseReason {
+239    TransportError(TransportError),
+240    Connection(ConnectionClose),
+241    Application(ApplicationClose),
+242}
+243
+244impl From<TransportError> for CloseReason {
+245    fn from(x: TransportError) -> Self {
+246        Self::TransportError(x)
+247    }
+248}
+249impl From<ConnectionClose> for CloseReason {
+250    fn from(x: ConnectionClose) -> Self {
+251        Self::Connection(x)
+252    }
+253}
+254impl From<ApplicationClose> for CloseReason {
+255    fn from(x: ApplicationClose) -> Self {
+256        Self::Application(x)
+257    }
+258}
+259
+260impl From<Close> for CloseReason {
+261    fn from(value: Close) -> Self {
+262        match value {
+263            Close::Application(reason) => Self::Application(reason),
+264            Close::Connection(reason) => Self::Connection(reason),
+265        }
+266    }
+267}
+268
+269impl From<CloseReason> for ConnectionError {
+270    fn from(value: CloseReason) -> Self {
+271        match value {
+272            CloseReason::TransportError(err) => Self::TransportError(err),
+273            CloseReason::Connection(reason) => Self::ConnectionClosed(reason),
+274            CloseReason::Application(reason) => Self::ApplicationClosed(reason),
+275        }
+276    }
+277}
+278
+279impl From<CloseReason> for Close {
+280    fn from(value: CloseReason) -> Self {
+281        match value {
+282            CloseReason::TransportError(err) => Self::Connection(err.into()),
+283            CloseReason::Connection(reason) => Self::Connection(reason),
+284            CloseReason::Application(reason) => Self::Application(reason),
+285        }
+286    }
+287}
+288
+289#[derive(Debug, Clone)]
+290enum InnerState {
+291    Handshake(Handshake),
+292    Established,
+293    Closed {
+294        /// The reason the remote closed the connection, or the reason we are sending to the remote.
+295        remote_reason: CloseReason,
+296        /// Set to true if we closed the connection locally.
+297        is_local: bool,
+298        /// Did we read this as error already?
+299        error_read: bool,
+300    },
+301    Draining {
+302        /// Why the connection was lost, if it has been.
+303        error: Option<ConnectionError>,
+304        /// Set to true if we closed the connection locally.
+305        is_local: bool,
+306    },
+307    /// Waiting for application to call close so we can dispose of the resources.
+308    Drained {
+309        /// Why the connection was lost, if it has been.
+310        error: Option<ConnectionError>,
+311        /// Set to true if we closed the connection locally.
+312        is_local: bool,
+313    },
+314}
+315
+316#[allow(unreachable_pub)] // fuzzing only
+317#[derive(Debug, Clone)]
+318pub struct Handshake {
+319    /// Whether the remote CID has been set by the peer yet.
+320    ///
+321    /// Always set for servers.
+322    pub(super) rem_cid_set: bool,
+323    /// Stateless retry token received in the first Initial by a server.
+324    ///
+325    /// Must be present in every Initial. Always empty for clients.
+326    pub(super) expected_token: Bytes,
+327    /// First cryptographic message.
+328    ///
+329    /// Only set for clients.
+330    pub(super) client_hello: Option<Bytes>,
+331    /// Whether the server address is allowed to migrate.
+332    ///
+333    /// We allow the server to migrate during the handshake as long as we have not
+334    /// received an authenticated handshake packet: it can send a response from a
+335    /// different address than we sent the initial to.  This allows us to send the
+336    /// initial packet over multiple paths - by means of an IPv6 ULA address that copies
+337    /// the packets sent to it to multiple destinations - and accept one response.
+338    ///
+339    /// This is only ever set to true if for a client which hasn't yet received an
+340    /// authenticated handshake packet.  It is set back to false in
+341    /// [`super::Connection::on_packet_authenticated`].
+342    ///
+343    /// THIS IS NOT RFC 9000 COMPLIANT!  A server is not allowed to migrate addresses,
+344    /// other than using the preferred-address transport parameter.
+345    pub(super) allow_server_migration: bool,
+346}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/stats.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/stats.rs.html new file mode 100644 index 000000000..2bed339e0 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/stats.rs.html @@ -0,0 +1,219 @@ +stats.rs - source

iroh_quinn_proto/connection/
stats.rs

1//! Connection statistics
+2
+3use crate::FrameType;
+4use crate::{Dir, Duration, frame::Frame};
+5
+6/// Statistics about UDP datagrams transmitted or received on a connection
+7///
+8/// All QUIC packets are carried by UDP datagrams. Hence, these statistics cover all traffic on a connection.
+9#[derive(Default, Debug, Copy, Clone)]
+10#[non_exhaustive]
+11pub struct UdpStats {
+12    /// The amount of UDP datagrams observed
+13    pub datagrams: u64,
+14    /// The total amount of bytes which have been transferred inside UDP datagrams
+15    pub bytes: u64,
+16    /// The amount of I/O operations executed
+17    ///
+18    /// Can be less than `datagrams` when GSO, GRO, and/or batched system calls are in use.
+19    pub ios: u64,
+20}
+21
+22impl UdpStats {
+23    pub(crate) fn on_sent(&mut self, datagrams: u64, bytes: usize) {
+24        self.datagrams += datagrams;
+25        self.bytes += bytes as u64;
+26        self.ios += 1;
+27    }
+28}
+29
+30/// Number of frames transmitted or received of each frame type
+31#[derive(Default, Copy, Clone)]
+32#[non_exhaustive]
+33#[allow(missing_docs)]
+34pub struct FrameStats {
+35    pub acks: u64,
+36    pub path_acks: u64,
+37    pub ack_frequency: u64,
+38    pub crypto: u64,
+39    pub connection_close: u64,
+40    pub data_blocked: u64,
+41    pub datagram: u64,
+42    pub handshake_done: u8,
+43    pub immediate_ack: u64,
+44    pub max_data: u64,
+45    pub max_stream_data: u64,
+46    pub max_streams_bidi: u64,
+47    pub max_streams_uni: u64,
+48    pub new_connection_id: u64,
+49    pub path_new_connection_id: u64,
+50    pub new_token: u64,
+51    pub path_challenge: u64,
+52    pub path_response: u64,
+53    pub ping: u64,
+54    pub reset_stream: u64,
+55    pub retire_connection_id: u64,
+56    pub path_retire_connection_id: u64,
+57    pub stream_data_blocked: u64,
+58    pub streams_blocked_bidi: u64,
+59    pub streams_blocked_uni: u64,
+60    pub stop_sending: u64,
+61    pub stream: u64,
+62    pub observed_addr: u64,
+63    pub path_abandon: u64,
+64    pub path_status_available: u64,
+65    pub path_status_backup: u64,
+66    pub max_path_id: u64,
+67    pub paths_blocked: u64,
+68    pub path_cids_blocked: u64,
+69    pub add_address: u64,
+70    pub reach_out: u64,
+71    pub remove_address: u64,
+72}
+73
+74impl FrameStats {
+75    pub(crate) fn record(&mut self, frame: &Frame) {
+76        match frame {
+77            Frame::Padding => {}
+78            Frame::Ping => self.ping += 1,
+79            Frame::Ack(_) => self.acks += 1,
+80            Frame::PathAck(_) => self.path_acks += 1,
+81            Frame::ResetStream(_) => self.reset_stream += 1,
+82            Frame::StopSending(_) => self.stop_sending += 1,
+83            Frame::Crypto(_) => self.crypto += 1,
+84            Frame::Datagram(_) => self.datagram += 1,
+85            Frame::NewToken(_) => self.new_token += 1,
+86            Frame::MaxData(_) => self.max_data += 1,
+87            Frame::MaxStreamData { .. } => self.max_stream_data += 1,
+88            Frame::MaxStreams { dir, .. } => {
+89                if *dir == Dir::Bi {
+90                    self.max_streams_bidi += 1;
+91                } else {
+92                    self.max_streams_uni += 1;
+93                }
+94            }
+95            Frame::DataBlocked { .. } => self.data_blocked += 1,
+96            Frame::Stream(_) => self.stream += 1,
+97            Frame::StreamDataBlocked { .. } => self.stream_data_blocked += 1,
+98            Frame::StreamsBlocked { dir, .. } => {
+99                if *dir == Dir::Bi {
+100                    self.streams_blocked_bidi += 1;
+101                } else {
+102                    self.streams_blocked_uni += 1;
+103                }
+104            }
+105            Frame::NewConnectionId(frame) => match frame.path_id {
+106                Some(_) => self.path_new_connection_id += 1,
+107                None => self.new_connection_id += 1,
+108            },
+109            Frame::RetireConnectionId(frame) => match frame.get_type() {
+110                FrameType::RETIRE_CONNECTION_ID => self.retire_connection_id += 1,
+111                FrameType::PATH_RETIRE_CONNECTION_ID => self.path_retire_connection_id += 1,
+112                _ => unreachable!(),
+113            },
+114            Frame::PathChallenge(_) => self.path_challenge += 1,
+115            Frame::PathResponse(_) => self.path_response += 1,
+116            Frame::Close(_) => self.connection_close += 1,
+117            Frame::AckFrequency(_) => self.ack_frequency += 1,
+118            Frame::ImmediateAck => self.immediate_ack += 1,
+119            Frame::HandshakeDone => self.handshake_done = self.handshake_done.saturating_add(1),
+120            Frame::ObservedAddr(_) => self.observed_addr += 1,
+121            Frame::PathAbandon(_) => self.path_abandon = self.path_abandon.saturating_add(1),
+122            Frame::PathStatusAvailable(_) => {
+123                self.path_status_available = self.path_status_available.saturating_add(1)
+124            }
+125            Frame::PathStatusBackup(_) => {
+126                self.path_status_backup = self.path_status_backup.saturating_add(1)
+127            }
+128            Frame::MaxPathId(_) => self.max_path_id = self.max_path_id.saturating_add(1),
+129            Frame::PathsBlocked(_) => self.paths_blocked = self.paths_blocked.saturating_add(1),
+130            Frame::PathCidsBlocked(_) => {
+131                self.path_cids_blocked = self.path_cids_blocked.saturating_add(1)
+132            }
+133            Frame::AddAddress(_) => self.add_address = self.add_address.saturating_add(1),
+134            Frame::ReachOut(_) => self.reach_out = self.reach_out.saturating_add(1),
+135            Frame::RemoveAddress(_) => self.remove_address = self.remove_address.saturating_add(1),
+136        }
+137    }
+138}
+139
+140impl std::fmt::Debug for FrameStats {
+141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+142        f.debug_struct("FrameStats")
+143            .field("ACK", &self.acks)
+144            .field("ACK_FREQUENCY", &self.ack_frequency)
+145            .field("CONNECTION_CLOSE", &self.connection_close)
+146            .field("CRYPTO", &self.crypto)
+147            .field("DATA_BLOCKED", &self.data_blocked)
+148            .field("DATAGRAM", &self.datagram)
+149            .field("HANDSHAKE_DONE", &self.handshake_done)
+150            .field("IMMEDIATE_ACK", &self.immediate_ack)
+151            .field("MAX_DATA", &self.max_data)
+152            .field("MAX_PATH_ID", &self.max_path_id)
+153            .field("MAX_STREAM_DATA", &self.max_stream_data)
+154            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
+155            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
+156            .field("NEW_CONNECTION_ID", &self.new_connection_id)
+157            .field("NEW_TOKEN", &self.new_token)
+158            .field("PATHS_BLOCKED", &self.paths_blocked)
+159            .field("PATH_ABANDON", &self.path_abandon)
+160            .field("PATH_ACK", &self.path_acks)
+161            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
+162            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
+163            .field("PATH_CHALLENGE", &self.path_challenge)
+164            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
+165            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
+166            .field("PATH_RESPONSE", &self.path_response)
+167            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
+168            .field("PING", &self.ping)
+169            .field("RESET_STREAM", &self.reset_stream)
+170            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
+171            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
+172            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
+173            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
+174            .field("STOP_SENDING", &self.stop_sending)
+175            .field("STREAM", &self.stream)
+176            .finish()
+177    }
+178}
+179
+180/// Statistics related to a transmission path
+181#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
+182#[non_exhaustive]
+183pub struct PathStats {
+184    /// Current best estimate of this connection's latency (round-trip-time)
+185    pub rtt: Duration,
+186    /// Current congestion window of the connection
+187    pub cwnd: u64,
+188    /// Congestion events on the connection
+189    pub congestion_events: u64,
+190    /// The amount of packets lost on this path
+191    pub lost_packets: u64,
+192    /// The amount of bytes lost on this path
+193    pub lost_bytes: u64,
+194    /// The amount of packets sent on this path
+195    pub sent_packets: u64,
+196    /// The amount of PLPMTUD probe packets sent on this path (also counted by `sent_packets`)
+197    pub sent_plpmtud_probes: u64,
+198    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
+199    /// `lost_bytes`)
+200    pub lost_plpmtud_probes: u64,
+201    /// The number of times a black hole was detected in the path
+202    pub black_holes_detected: u64,
+203    /// Largest UDP payload size the path currently supports
+204    pub current_mtu: u16,
+205}
+206
+207/// Connection statistics
+208#[derive(Debug, Default, Clone)]
+209#[non_exhaustive]
+210pub struct ConnectionStats {
+211    /// Statistics about UDP datagrams transmitted on a connection
+212    pub udp_tx: UdpStats,
+213    /// Statistics about UDP datagrams received on a connection
+214    pub udp_rx: UdpStats,
+215    /// Statistics about frames transmitted on a connection
+216    pub frame_tx: FrameStats,
+217    /// Statistics about frames received on a connection
+218    pub frame_rx: FrameStats,
+219}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html new file mode 100644 index 000000000..cd8a6a6fe --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html @@ -0,0 +1,528 @@ +mod.rs - source

iroh_quinn_proto/connection/streams/
mod.rs

1use std::{
+2    collections::{BinaryHeap, hash_map},
+3    io,
+4};
+5
+6use bytes::Bytes;
+7use thiserror::Error;
+8use tracing::trace;
+9
+10use super::spaces::{Retransmits, ThinRetransmits};
+11use crate::{
+12    Dir, StreamId, VarInt,
+13    connection::streams::state::{get_or_insert_recv, get_or_insert_send},
+14    frame,
+15};
+16
+17mod recv;
+18use recv::Recv;
+19pub use recv::{Chunks, ReadError, ReadableError};
+20
+21mod send;
+22pub(crate) use send::{ByteSlice, BytesArray};
+23use send::{BytesSource, Send, SendState};
+24pub use send::{FinishError, WriteError, Written};
+25
+26mod state;
+27#[allow(unreachable_pub)] // fuzzing only
+28pub use state::StreamsState;
+29
+30/// Access to streams
+31pub struct Streams<'a> {
+32    pub(super) state: &'a mut StreamsState,
+33    pub(super) conn_state: &'a super::State,
+34}
+35
+36#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+37impl<'a> Streams<'a> {
+38    #[cfg(fuzzing)]
+39    pub fn new(state: &'a mut StreamsState, conn_state: &'a super::State) -> Self {
+40        Self { state, conn_state }
+41    }
+42
+43    /// Open a single stream if possible
+44    ///
+45    /// Returns `None` if the streams in the given direction are currently exhausted.
+46    pub fn open(&mut self, dir: Dir) -> Option<StreamId> {
+47        if self.conn_state.is_closed() {
+48            return None;
+49        }
+50
+51        // TODO: Queue STREAM_ID_BLOCKED if this fails
+52        if self.state.next[dir as usize] >= self.state.max[dir as usize] {
+53            return None;
+54        }
+55
+56        self.state.next[dir as usize] += 1;
+57        let id = StreamId::new(self.state.side, dir, self.state.next[dir as usize] - 1);
+58        self.state.insert(false, id);
+59        self.state.send_streams += 1;
+60        Some(id)
+61    }
+62
+63    /// Accept a remotely initiated stream of a certain directionality, if possible
+64    ///
+65    /// Returns `None` if there are no new incoming streams for this connection.
+66    /// Has no impact on the data flow-control or stream concurrency limits.
+67    pub fn accept(&mut self, dir: Dir) -> Option<StreamId> {
+68        if self.state.next_remote[dir as usize] == self.state.next_reported_remote[dir as usize] {
+69            return None;
+70        }
+71
+72        let x = self.state.next_reported_remote[dir as usize];
+73        self.state.next_reported_remote[dir as usize] = x + 1;
+74        if dir == Dir::Bi {
+75            self.state.send_streams += 1;
+76        }
+77
+78        Some(StreamId::new(!self.state.side, dir, x))
+79    }
+80
+81    #[cfg(fuzzing)]
+82    pub fn state(&mut self) -> &mut StreamsState {
+83        self.state
+84    }
+85
+86    /// The number of streams that may have unacknowledged data.
+87    pub fn send_streams(&self) -> usize {
+88        self.state.send_streams
+89    }
+90
+91    /// The number of remotely initiated open streams of a certain directionality.
+92    ///
+93    /// Includes remotely initiated streams, which have not been accepted via [`accept`](Self::accept).
+94    /// These streams count against the respective concurrency limit reported by
+95    /// [`Connection::max_concurrent_streams`](super::Connection::max_concurrent_streams).
+96    pub fn remote_open_streams(&self, dir: Dir) -> u64 {
+97        // total opened - total closed = total opened - ( total permitted - total permitted unclosed )
+98        self.state.next_remote[dir as usize]
+99            - (self.state.max_remote[dir as usize]
+100                - self.state.allocated_remote_count[dir as usize])
+101    }
+102}
+103
+104/// Access to streams
+105pub struct RecvStream<'a> {
+106    pub(super) id: StreamId,
+107    pub(super) state: &'a mut StreamsState,
+108    pub(super) pending: &'a mut Retransmits,
+109}
+110
+111impl RecvStream<'_> {
+112    /// Read from the given recv stream
+113    ///
+114    /// `max_length` limits the maximum size of the returned `Bytes` value; passing `usize::MAX`
+115    /// will yield the best performance. `ordered` will make sure the returned chunk's offset will
+116    /// have an offset exactly equal to the previously returned offset plus the previously returned
+117    /// bytes' length.
+118    ///
+119    /// Yields `Ok(None)` if the stream was finished. Otherwise, yields a segment of data and its
+120    /// offset in the stream. If `ordered` is `false`, segments may be received in any order, and
+121    /// the `Chunk`'s `offset` field can be used to determine ordering in the caller.
+122    ///
+123    /// While most applications will prefer to consume stream data in order, unordered reads can
+124    /// improve performance when packet loss occurs and data cannot be retransmitted before the flow
+125    /// control window is filled. On any given stream, you can switch from ordered to unordered
+126    /// reads, but ordered reads on streams that have seen previous unordered reads will return
+127    /// `ReadError::IllegalOrderedRead`.
+128    pub fn read(&mut self, ordered: bool) -> Result<Chunks<'_>, ReadableError> {
+129        Chunks::new(self.id, ordered, self.state, self.pending)
+130    }
+131
+132    /// Stop accepting data on the given receive stream
+133    ///
+134    /// Discards unread data and notifies the peer to stop transmitting. Once stopped, further
+135    /// attempts to operate on a stream will yield `ClosedStream` errors.
+136    pub fn stop(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+137        let mut entry = match self.state.recv.entry(self.id) {
+138            hash_map::Entry::Occupied(s) => s,
+139            hash_map::Entry::Vacant(_) => return Err(ClosedStream { _private: () }),
+140        };
+141        let stream = get_or_insert_recv(self.state.stream_receive_window)(entry.get_mut());
+142
+143        let (read_credits, stop_sending) = stream.stop()?;
+144        if stop_sending.should_transmit() {
+145            self.pending.stop_sending.push(frame::StopSending {
+146                id: self.id,
+147                error_code,
+148            });
+149        }
+150
+151        // We need to keep stopped streams around until they're finished or reset so we can update
+152        // connection-level flow control to account for discarded data. Otherwise, we can discard
+153        // state immediately.
+154        if !stream.final_offset_unknown() {
+155            let recv = entry.remove().expect("must have recv when stopping");
+156            self.state.stream_recv_freed(self.id, recv);
+157        }
+158
+159        if self.state.add_read_credits(read_credits).should_transmit() {
+160            self.pending.max_data = true;
+161        }
+162
+163        Ok(())
+164    }
+165
+166    /// Check whether this stream has been reset by the peer, returning the reset error code if so
+167    ///
+168    /// After returning `Ok(Some(_))` once, stream state will be discarded and all future calls will
+169    /// return `Err(ClosedStream)`.
+170    pub fn received_reset(&mut self) -> Result<Option<VarInt>, ClosedStream> {
+171        let hash_map::Entry::Occupied(entry) = self.state.recv.entry(self.id) else {
+172            return Err(ClosedStream { _private: () });
+173        };
+174        let Some(s) = entry.get().as_ref().and_then(|s| s.as_open_recv()) else {
+175            return Ok(None);
+176        };
+177        if s.stopped {
+178            return Err(ClosedStream { _private: () });
+179        }
+180        let Some(code) = s.reset_code() else {
+181            return Ok(None);
+182        };
+183
+184        // Clean up state after application observes the reset, since there's no reason for the
+185        // application to attempt to read or stop the stream once it knows it's reset
+186        let (_, recv) = entry.remove_entry();
+187        self.state
+188            .stream_recv_freed(self.id, recv.expect("must have recv on reset"));
+189        self.state.queue_max_stream_id(self.pending);
+190
+191        Ok(Some(code))
+192    }
+193}
+194
+195/// Access to streams
+196pub struct SendStream<'a> {
+197    pub(super) id: StreamId,
+198    pub(super) state: &'a mut StreamsState,
+199    pub(super) pending: &'a mut Retransmits,
+200    pub(super) conn_state: &'a super::State,
+201}
+202
+203#[allow(clippy::needless_lifetimes)] // Needed for cfg(fuzzing)
+204impl<'a> SendStream<'a> {
+205    #[cfg(fuzzing)]
+206    pub fn new(
+207        id: StreamId,
+208        state: &'a mut StreamsState,
+209        pending: &'a mut Retransmits,
+210        conn_state: &'a super::State,
+211    ) -> Self {
+212        Self {
+213            id,
+214            state,
+215            pending,
+216            conn_state,
+217        }
+218    }
+219
+220    /// Send data on the given stream
+221    ///
+222    /// Returns the number of bytes successfully written.
+223    pub fn write(&mut self, data: &[u8]) -> Result<usize, WriteError> {
+224        Ok(self.write_source(&mut ByteSlice::from_slice(data))?.bytes)
+225    }
+226
+227    /// Send data on the given stream
+228    ///
+229    /// Returns the number of bytes and chunks successfully written.
+230    /// Note that this method might also write a partial chunk. In this case
+231    /// [`Written::chunks`] will not count this chunk as fully written. However
+232    /// the chunk will be advanced and contain only non-written data after the call.
+233    pub fn write_chunks(&mut self, data: &mut [Bytes]) -> Result<Written, WriteError> {
+234        self.write_source(&mut BytesArray::from_chunks(data))
+235    }
+236
+237    fn write_source<B: BytesSource>(&mut self, source: &mut B) -> Result<Written, WriteError> {
+238        if self.conn_state.is_closed() {
+239            trace!(%self.id, "write blocked; connection draining");
+240            return Err(WriteError::Blocked);
+241        }
+242
+243        let limit = self.state.write_limit();
+244
+245        let max_send_data = self.state.max_send_data(self.id);
+246
+247        let stream = self
+248            .state
+249            .send
+250            .get_mut(&self.id)
+251            .map(get_or_insert_send(max_send_data))
+252            .ok_or(WriteError::ClosedStream)?;
+253
+254        if limit == 0 {
+255            trace!(
+256                stream = %self.id, max_data = self.state.max_data, data_sent = self.state.data_sent,
+257                "write blocked by connection-level flow control or send window"
+258            );
+259            if !stream.connection_blocked {
+260                stream.connection_blocked = true;
+261                self.state.connection_blocked.push(self.id);
+262            }
+263            return Err(WriteError::Blocked);
+264        }
+265
+266        let was_pending = stream.is_pending();
+267        let written = stream.write(source, limit)?;
+268        self.state.data_sent += written.bytes as u64;
+269        self.state.unacked_data += written.bytes as u64;
+270        trace!(stream = %self.id, "wrote {} bytes", written.bytes);
+271        if !was_pending {
+272            self.state.pending.push_pending(self.id, stream.priority);
+273        }
+274        Ok(written)
+275    }
+276
+277    /// Check if this stream was stopped, get the reason if it was
+278    pub fn stopped(&self) -> Result<Option<VarInt>, ClosedStream> {
+279        match self.state.send.get(&self.id).as_ref() {
+280            Some(Some(s)) => Ok(s.stop_reason),
+281            Some(None) => Ok(None),
+282            None => Err(ClosedStream { _private: () }),
+283        }
+284    }
+285
+286    /// Finish a send stream, signalling that no more data will be sent.
+287    ///
+288    /// If this fails, no [`StreamEvent::Finished`] will be generated.
+289    ///
+290    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+291    pub fn finish(&mut self) -> Result<(), FinishError> {
+292        let max_send_data = self.state.max_send_data(self.id);
+293        let stream = self
+294            .state
+295            .send
+296            .get_mut(&self.id)
+297            .map(get_or_insert_send(max_send_data))
+298            .ok_or(FinishError::ClosedStream)?;
+299
+300        let was_pending = stream.is_pending();
+301        stream.finish()?;
+302        if !was_pending {
+303            self.state.pending.push_pending(self.id, stream.priority);
+304        }
+305
+306        Ok(())
+307    }
+308
+309    /// Abandon transmitting data on a stream
+310    ///
+311    /// # Panics
+312    /// - when applied to a receive stream
+313    pub fn reset(&mut self, error_code: VarInt) -> Result<(), ClosedStream> {
+314        let max_send_data = self.state.max_send_data(self.id);
+315        let stream = self
+316            .state
+317            .send
+318            .get_mut(&self.id)
+319            .map(get_or_insert_send(max_send_data))
+320            .ok_or(ClosedStream { _private: () })?;
+321
+322        if matches!(stream.state, SendState::ResetSent) {
+323            // Redundant reset call
+324            return Err(ClosedStream { _private: () });
+325        }
+326
+327        // Restore the portion of the send window consumed by the data that we aren't about to
+328        // send. We leave flow control alone because the peer's responsible for issuing additional
+329        // credit based on the final offset communicated in the RESET_STREAM frame we send.
+330        self.state.unacked_data -= stream.pending.unacked();
+331        stream.reset();
+332        self.pending.reset_stream.push((self.id, error_code));
+333
+334        // Don't reopen an already-closed stream we haven't forgotten yet
+335        Ok(())
+336    }
+337
+338    /// Set the priority of a stream
+339    ///
+340    /// # Panics
+341    /// - when applied to a receive stream
+342    pub fn set_priority(&mut self, priority: i32) -> Result<(), ClosedStream> {
+343        let max_send_data = self.state.max_send_data(self.id);
+344        let stream = self
+345            .state
+346            .send
+347            .get_mut(&self.id)
+348            .map(get_or_insert_send(max_send_data))
+349            .ok_or(ClosedStream { _private: () })?;
+350
+351        stream.priority = priority;
+352        Ok(())
+353    }
+354
+355    /// Get the priority of a stream
+356    ///
+357    /// # Panics
+358    /// - when applied to a receive stream
+359    pub fn priority(&self) -> Result<i32, ClosedStream> {
+360        let stream = self
+361            .state
+362            .send
+363            .get(&self.id)
+364            .ok_or(ClosedStream { _private: () })?;
+365
+366        Ok(stream.as_ref().map(|s| s.priority).unwrap_or_default())
+367    }
+368}
+369
+370/// A queue of streams with pending outgoing data, sorted by priority
+371struct PendingStreamsQueue {
+372    streams: BinaryHeap<PendingStream>,
+373    /// The next stream to write out. This is `Some` when `TransportConfig::send_fairness(false)` and writing a stream is
+374    /// interrupted while the stream still has some pending data. See `reinsert_pending()`.
+375    next: Option<PendingStream>,
+376    /// A monotonically decreasing counter, used to implement round-robin scheduling for streams of the same priority.
+377    /// Underflowing is not a practical concern, as it is initialized to u64::MAX and only decremented by 1 in `push_pending`
+378    recency: u64,
+379}
+380
+381impl PendingStreamsQueue {
+382    fn new() -> Self {
+383        Self {
+384            streams: BinaryHeap::new(),
+385            next: None,
+386            recency: u64::MAX,
+387        }
+388    }
+389
+390    /// Reinsert a stream that was pending and still contains unsent data.
+391    fn reinsert_pending(&mut self, id: StreamId, priority: i32) {
+392        assert!(self.next.is_none());
+393
+394        self.next = Some(PendingStream {
+395            priority,
+396            recency: self.recency, // the value here doesn't really matter
+397            id,
+398        });
+399    }
+400
+401    /// Push a pending stream ID with the given priority, queued after any already-queued streams for the priority
+402    fn push_pending(&mut self, id: StreamId, priority: i32) {
+403        // Note that in the case where fairness is disabled, if we have a reinserted stream we don't
+404        // bump it even if priority > next.priority. In order to minimize fragmentation we
+405        // always try to complete a stream once part of it has been written.
+406
+407        // As the recency counter is monotonically decreasing, we know that using its value to sort this stream will queue it
+408        // after all other queued streams of the same priority.
+409        // This is enough to implement round-robin scheduling for streams that are still pending even after being handled,
+410        // as in that case they are removed from the `BinaryHeap`, handled, and then immediately reinserted.
+411        self.recency -= 1;
+412        self.streams.push(PendingStream {
+413            priority,
+414            recency: self.recency,
+415            id,
+416        });
+417    }
+418
+419    fn pop(&mut self) -> Option<PendingStream> {
+420        self.next.take().or_else(|| self.streams.pop())
+421    }
+422
+423    fn clear(&mut self) {
+424        self.next = None;
+425        self.streams.clear();
+426    }
+427
+428    fn iter(&self) -> impl Iterator<Item = &PendingStream> {
+429        self.next.iter().chain(self.streams.iter())
+430    }
+431
+432    #[cfg(test)]
+433    fn len(&self) -> usize {
+434        self.streams.len() + self.next.is_some() as usize
+435    }
+436}
+437
+438/// The [`StreamId`] of a stream with pending data queued, ordered by its priority and recency
+439#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+440struct PendingStream {
+441    /// The priority of the stream
+442    // Note that this field should be kept above the `recency` field, in order for the `Ord` derive to be correct
+443    // (See https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html#derivable)
+444    priority: i32,
+445    /// A tie-breaker for streams of the same priority, used to improve fairness by implementing round-robin scheduling:
+446    /// Larger values are prioritized, so it is initialised to `u64::MAX`, and when a stream writes data, we know
+447    /// that it currently has the highest recency value, so it is deprioritized by setting its recency to 1 less than the
+448    /// previous lowest recency value, such that all other streams of this priority will get processed once before we get back
+449    /// round to this one
+450    recency: u64,
+451    /// The ID of the stream
+452    // The way this type is used ensures that every instance has a unique `recency` value, so this field should be kept below
+453    // the `priority` and `recency` fields, so that it does not interfere with the behaviour of the `Ord` derive
+454    id: StreamId,
+455}
+456
+457/// Application events about streams
+458#[derive(Debug, PartialEq, Eq)]
+459pub enum StreamEvent {
+460    /// One or more new streams has been opened and might be readable
+461    Opened {
+462        /// Directionality for which streams have been opened
+463        dir: Dir,
+464    },
+465    /// A currently open stream likely has data or errors waiting to be read
+466    Readable {
+467        /// Which stream is now readable
+468        id: StreamId,
+469    },
+470    /// A formerly write-blocked stream might be ready for a write or have been stopped
+471    ///
+472    /// Only generated for streams that are currently open.
+473    Writable {
+474        /// Which stream is now writable
+475        id: StreamId,
+476    },
+477    /// A finished stream has been fully acknowledged or stopped
+478    Finished {
+479        /// Which stream has been finished
+480        id: StreamId,
+481    },
+482    /// The peer asked us to stop sending on an outgoing stream
+483    Stopped {
+484        /// Which stream has been stopped
+485        id: StreamId,
+486        /// Error code supplied by the peer
+487        error_code: VarInt,
+488    },
+489    /// At least one new stream of a certain directionality may be opened
+490    Available {
+491        /// Directionality for which streams are newly available
+492        dir: Dir,
+493    },
+494}
+495
+496/// Indicates whether a frame needs to be transmitted
+497///
+498/// This type wraps around bool and uses the `#[must_use]` attribute in order
+499/// to prevent accidental loss of the frame transmission requirement.
+500#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+501#[must_use = "A frame might need to be enqueued"]
+502pub struct ShouldTransmit(bool);
+503
+504impl ShouldTransmit {
+505    /// Returns whether a frame should be transmitted
+506    pub fn should_transmit(self) -> bool {
+507        self.0
+508    }
+509}
+510
+511/// Error indicating that a stream has not been opened or has already been finished or reset
+512#[derive(Debug, Default, Error, Clone, PartialEq, Eq)]
+513#[error("closed stream")]
+514pub struct ClosedStream {
+515    _private: (),
+516}
+517
+518impl From<ClosedStream> for io::Error {
+519    fn from(x: ClosedStream) -> Self {
+520        Self::new(io::ErrorKind::NotConnected, x)
+521    }
+522}
+523
+524#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+525enum StreamHalf {
+526    Send,
+527    Recv,
+528}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html new file mode 100644 index 000000000..827e51f86 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/streams/recv.rs.html @@ -0,0 +1,543 @@ +recv.rs - source

iroh_quinn_proto/connection/streams/
recv.rs

1use std::collections::hash_map::Entry;
+2use std::mem;
+3
+4use thiserror::Error;
+5use tracing::debug;
+6
+7use super::state::get_or_insert_recv;
+8use super::{ClosedStream, Retransmits, ShouldTransmit, StreamId, StreamsState};
+9use crate::connection::assembler::{Assembler, Chunk, IllegalOrderedRead};
+10use crate::connection::streams::state::StreamRecv;
+11use crate::{TransportError, VarInt, frame};
+12
+13#[derive(Debug, Default)]
+14pub(super) struct Recv {
+15    // NB: when adding or removing fields, remember to update `reinit`.
+16    state: RecvState,
+17    pub(super) assembler: Assembler,
+18    sent_max_stream_data: u64,
+19    pub(super) end: u64,
+20    pub(super) stopped: bool,
+21}
+22
+23impl Recv {
+24    pub(super) fn new(initial_max_data: u64) -> Box<Self> {
+25        Box::new(Self {
+26            state: RecvState::default(),
+27            assembler: Assembler::new(),
+28            sent_max_stream_data: initial_max_data,
+29            end: 0,
+30            stopped: false,
+31        })
+32    }
+33
+34    /// Reset to the initial state
+35    pub(super) fn reinit(&mut self, initial_max_data: u64) {
+36        self.state = RecvState::default();
+37        self.assembler.reinit();
+38        self.sent_max_stream_data = initial_max_data;
+39        self.end = 0;
+40        self.stopped = false;
+41    }
+42
+43    /// Process a STREAM frame
+44    ///
+45    /// Return value is `(number_of_new_bytes_ingested, stream_is_closed)`
+46    pub(super) fn ingest(
+47        &mut self,
+48        frame: frame::Stream,
+49        payload_len: usize,
+50        received: u64,
+51        max_data: u64,
+52    ) -> Result<(u64, bool), TransportError> {
+53        let end = frame.offset + frame.data.len() as u64;
+54        if end >= 2u64.pow(62) {
+55            return Err(TransportError::FLOW_CONTROL_ERROR(
+56                "maximum stream offset too large",
+57            ));
+58        }
+59
+60        if let Some(final_offset) = self.final_offset() {
+61            if end > final_offset || (frame.fin && end != final_offset) {
+62                debug!(end, final_offset, "final size error");
+63                return Err(TransportError::FINAL_SIZE_ERROR(""));
+64            }
+65        }
+66
+67        let new_bytes = self.credit_consumed_by(end, received, max_data)?;
+68
+69        // Stopped streams don't need to wait for the actual data, they just need to know
+70        // how much there was.
+71        if frame.fin && !self.stopped {
+72            if let RecvState::Recv { ref mut size } = self.state {
+73                *size = Some(end);
+74            }
+75        }
+76
+77        self.end = self.end.max(end);
+78        // Don't bother storing data or releasing stream-level flow control credit if the stream's
+79        // already stopped
+80        if !self.stopped {
+81            self.assembler.insert(frame.offset, frame.data, payload_len);
+82        }
+83
+84        Ok((new_bytes, frame.fin && self.stopped))
+85    }
+86
+87    pub(super) fn stop(&mut self) -> Result<(u64, ShouldTransmit), ClosedStream> {
+88        if self.stopped {
+89            return Err(ClosedStream { _private: () });
+90        }
+91
+92        self.stopped = true;
+93        self.assembler.clear();
+94        // Issue flow control credit for unread data
+95        let read_credits = self.end - self.assembler.bytes_read();
+96        // This may send a spurious STOP_SENDING if we've already received all data, but it's a bit
+97        // fiddly to distinguish that from the case where we've received a FIN but are missing some
+98        // data that the peer might still be trying to retransmit, in which case a STOP_SENDING is
+99        // still useful.
+100        Ok((read_credits, ShouldTransmit(self.is_receiving())))
+101    }
+102
+103    /// Returns the window that should be advertised in a `MAX_STREAM_DATA` frame
+104    ///
+105    /// The method returns a tuple which consists of the window that should be
+106    /// announced, as well as a boolean parameter which indicates if a new
+107    /// transmission of the value is recommended. If the boolean value is
+108    /// `false` the new window should only be transmitted if a previous transmission
+109    /// had failed.
+110    pub(super) fn max_stream_data(&mut self, stream_receive_window: u64) -> (u64, ShouldTransmit) {
+111        let max_stream_data = self.assembler.bytes_read() + stream_receive_window;
+112
+113        // Only announce a window update if it's significant enough
+114        // to make it worthwhile sending a MAX_STREAM_DATA frame.
+115        // We use here a fraction of the configured stream receive window to make
+116        // the decision, and accommodate for streams using bigger windows requiring
+117        // less updates. A fixed size would also work - but it would need to be
+118        // smaller than `stream_receive_window` in order to make sure the stream
+119        // does not get stuck.
+120        let diff = max_stream_data - self.sent_max_stream_data;
+121        let transmit = self.can_send_flow_control() && diff >= (stream_receive_window / 8);
+122        (max_stream_data, ShouldTransmit(transmit))
+123    }
+124
+125    /// Records that a `MAX_STREAM_DATA` announcing a certain window was sent
+126    ///
+127    /// This will suppress enqueuing further `MAX_STREAM_DATA` frames unless
+128    /// either the previous transmission was not acknowledged or the window
+129    /// further increased.
+130    pub(super) fn record_sent_max_stream_data(&mut self, sent_value: u64) {
+131        if sent_value > self.sent_max_stream_data {
+132            self.sent_max_stream_data = sent_value;
+133        }
+134    }
+135
+136    /// Whether the total amount of data that the peer will send on this stream is unknown
+137    ///
+138    /// True until we've received either a reset or the final frame.
+139    ///
+140    /// Implies that the sender might benefit from stream-level flow control updates, and we might
+141    /// need to issue connection-level flow control updates due to flow control budget use by this
+142    /// stream in the future, even if it's been stopped.
+143    pub(super) fn final_offset_unknown(&self) -> bool {
+144        matches!(self.state, RecvState::Recv { size: None })
+145    }
+146
+147    /// Whether stream-level flow control updates should be sent for this stream
+148    pub(super) fn can_send_flow_control(&self) -> bool {
+149        // Stream-level flow control is redundant if the sender has already sent the whole stream,
+150        // and moot if we no longer want data on this stream.
+151        self.final_offset_unknown() && !self.stopped
+152    }
+153
+154    /// Whether data is still being accepted from the peer
+155    pub(super) fn is_receiving(&self) -> bool {
+156        matches!(self.state, RecvState::Recv { .. })
+157    }
+158
+159    fn final_offset(&self) -> Option<u64> {
+160        match self.state {
+161            RecvState::Recv { size } => size,
+162            RecvState::ResetRecvd { size, .. } => Some(size),
+163        }
+164    }
+165
+166    /// Returns `false` iff the reset was redundant
+167    pub(super) fn reset(
+168        &mut self,
+169        error_code: VarInt,
+170        final_offset: VarInt,
+171        received: u64,
+172        max_data: u64,
+173    ) -> Result<bool, TransportError> {
+174        // Validate final_offset
+175        if let Some(offset) = self.final_offset() {
+176            if offset != final_offset.into_inner() {
+177                return Err(TransportError::FINAL_SIZE_ERROR("inconsistent value"));
+178            }
+179        } else if self.end > u64::from(final_offset) {
+180            return Err(TransportError::FINAL_SIZE_ERROR(
+181                "lower than high water mark",
+182            ));
+183        }
+184        self.credit_consumed_by(final_offset.into(), received, max_data)?;
+185
+186        if matches!(self.state, RecvState::ResetRecvd { .. }) {
+187            return Ok(false);
+188        }
+189        self.state = RecvState::ResetRecvd {
+190            size: final_offset.into(),
+191            error_code,
+192        };
+193        // Nuke buffers so that future reads fail immediately, which ensures future reads don't
+194        // issue flow control credit redundant to that already issued. We could instead special-case
+195        // reset streams during read, but it's unclear if there's any benefit to retaining data for
+196        // reset streams.
+197        self.assembler.clear();
+198        Ok(true)
+199    }
+200
+201    pub(super) fn reset_code(&self) -> Option<VarInt> {
+202        match self.state {
+203            RecvState::ResetRecvd { error_code, .. } => Some(error_code),
+204            _ => None,
+205        }
+206    }
+207
+208    /// Compute the amount of flow control credit consumed, or return an error if more was consumed
+209    /// than issued
+210    fn credit_consumed_by(
+211        &self,
+212        offset: u64,
+213        received: u64,
+214        max_data: u64,
+215    ) -> Result<u64, TransportError> {
+216        let prev_end = self.end;
+217        let new_bytes = offset.saturating_sub(prev_end);
+218        if offset > self.sent_max_stream_data || received + new_bytes > max_data {
+219            debug!(
+220                received,
+221                new_bytes,
+222                max_data,
+223                offset,
+224                stream_max_data = self.sent_max_stream_data,
+225                "flow control error"
+226            );
+227            return Err(TransportError::FLOW_CONTROL_ERROR(""));
+228        }
+229
+230        Ok(new_bytes)
+231    }
+232}
+233
+234/// Chunks returned from [`RecvStream::read()`][crate::RecvStream::read].
+235///
+236/// ### Note: Finalization Needed
+237/// Bytes read from the stream are not released from the congestion window until
+238/// either [`Self::finalize()`] is called, or this type is dropped.
+239///
+240/// It is recommended that you call [`Self::finalize()`] because it returns a flag
+241/// telling you whether reading from the stream has resulted in the need to transmit a packet.
+242///
+243/// If this type is leaked, the stream will remain blocked on the remote peer until
+244/// another read from the stream is done.
+245pub struct Chunks<'a> {
+246    id: StreamId,
+247    ordered: bool,
+248    streams: &'a mut StreamsState,
+249    pending: &'a mut Retransmits,
+250    state: ChunksState,
+251    read: u64,
+252}
+253
+254impl<'a> Chunks<'a> {
+255    pub(super) fn new(
+256        id: StreamId,
+257        ordered: bool,
+258        streams: &'a mut StreamsState,
+259        pending: &'a mut Retransmits,
+260    ) -> Result<Self, ReadableError> {
+261        let mut entry = match streams.recv.entry(id) {
+262            Entry::Occupied(entry) => entry,
+263            Entry::Vacant(_) => return Err(ReadableError::ClosedStream),
+264        };
+265
+266        let mut recv =
+267            match get_or_insert_recv(streams.stream_receive_window)(entry.get_mut()).stopped {
+268                true => return Err(ReadableError::ClosedStream),
+269                false => entry.remove().unwrap().into_inner(), // this can't fail due to the previous get_or_insert_with
+270            };
+271
+272        recv.assembler.ensure_ordering(ordered)?;
+273        Ok(Self {
+274            id,
+275            ordered,
+276            streams,
+277            pending,
+278            state: ChunksState::Readable(recv),
+279            read: 0,
+280        })
+281    }
+282
+283    /// Next
+284    ///
+285    /// Should call finalize() when done calling this.
+286    pub fn next(&mut self, max_length: usize) -> Result<Option<Chunk>, ReadError> {
+287        let rs = match self.state {
+288            ChunksState::Readable(ref mut rs) => rs,
+289            ChunksState::Reset(error_code) => {
+290                return Err(ReadError::Reset(error_code));
+291            }
+292            ChunksState::Finished => {
+293                return Ok(None);
+294            }
+295            ChunksState::Finalized => panic!("must not call next() after finalize()"),
+296        };
+297
+298        if let Some(chunk) = rs.assembler.read(max_length, self.ordered) {
+299            self.read += chunk.bytes.len() as u64;
+300            return Ok(Some(chunk));
+301        }
+302
+303        match rs.state {
+304            RecvState::ResetRecvd { error_code, .. } => {
+305                debug_assert_eq!(self.read, 0, "reset streams have empty buffers");
+306                let state = mem::replace(&mut self.state, ChunksState::Reset(error_code));
+307                // At this point if we have `rs` self.state must be `ChunksState::Readable`
+308                let recv = match state {
+309                    ChunksState::Readable(recv) => StreamRecv::Open(recv),
+310                    _ => unreachable!("state must be ChunkState::Readable"),
+311                };
+312                self.streams.stream_recv_freed(self.id, recv);
+313                Err(ReadError::Reset(error_code))
+314            }
+315            RecvState::Recv { size } => {
+316                if size == Some(rs.end) && rs.assembler.bytes_read() == rs.end {
+317                    let state = mem::replace(&mut self.state, ChunksState::Finished);
+318                    // At this point if we have `rs` self.state must be `ChunksState::Readable`
+319                    let recv = match state {
+320                        ChunksState::Readable(recv) => StreamRecv::Open(recv),
+321                        _ => unreachable!("state must be ChunkState::Readable"),
+322                    };
+323                    self.streams.stream_recv_freed(self.id, recv);
+324                    Ok(None)
+325                } else {
+326                    // We don't need a distinct `ChunksState` variant for a blocked stream because
+327                    // retrying a read harmlessly re-traces our steps back to returning
+328                    // `Err(Blocked)` again. The buffers can't refill and the stream's own state
+329                    // can't change so long as this `Chunks` exists.
+330                    Err(ReadError::Blocked)
+331                }
+332            }
+333        }
+334    }
+335
+336    /// Mark the read data as consumed from the stream.
+337    ///
+338    /// The number of read bytes will be released from the congestion window,
+339    /// allowing the remote peer to send more data if it was previously blocked.
+340    ///
+341    /// If [`ShouldTransmit::should_transmit()`] returns `true`,
+342    /// a packet needs to be sent to the peer informing them that the stream is unblocked.
+343    /// This means that you should call [`Connection::poll_transmit()`][crate::Connection::poll_transmit]
+344    /// and send the returned packet as soon as is reasonable, to unblock the remote peer.
+345    pub fn finalize(mut self) -> ShouldTransmit {
+346        self.finalize_inner()
+347    }
+348
+349    fn finalize_inner(&mut self) -> ShouldTransmit {
+350        let state = mem::replace(&mut self.state, ChunksState::Finalized);
+351        if let ChunksState::Finalized = state {
+352            // Noop on repeated calls
+353            return ShouldTransmit(false);
+354        }
+355
+356        // We issue additional stream ID credit after the application is notified that a previously
+357        // open stream has finished or been reset and we've therefore disposed of its state, as
+358        // recorded by `stream_freed` calls in `next`.
+359        let mut should_transmit = self.streams.queue_max_stream_id(self.pending);
+360
+361        // If the stream hasn't finished, we may need to issue stream-level flow control credit
+362        if let ChunksState::Readable(mut rs) = state {
+363            let (_, max_stream_data) = rs.max_stream_data(self.streams.stream_receive_window);
+364            should_transmit |= max_stream_data.0;
+365            if max_stream_data.0 {
+366                self.pending.max_stream_data.insert(self.id);
+367            }
+368            // Return the stream to storage for future use
+369            self.streams
+370                .recv
+371                .insert(self.id, Some(StreamRecv::Open(rs)));
+372        }
+373
+374        // Issue connection-level flow control credit for any data we read regardless of state
+375        let max_data = self.streams.add_read_credits(self.read);
+376        self.pending.max_data |= max_data.0;
+377        should_transmit |= max_data.0;
+378        ShouldTransmit(should_transmit)
+379    }
+380}
+381
+382impl Drop for Chunks<'_> {
+383    fn drop(&mut self) {
+384        let _ = self.finalize_inner();
+385    }
+386}
+387
+388enum ChunksState {
+389    Readable(Box<Recv>),
+390    Reset(VarInt),
+391    Finished,
+392    Finalized,
+393}
+394
+395/// Errors triggered when reading from a recv stream
+396#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+397pub enum ReadError {
+398    /// No more data is currently available on this stream.
+399    ///
+400    /// If more data on this stream is received from the peer, an `Event::StreamReadable` will be
+401    /// generated for this stream, indicating that retrying the read might succeed.
+402    #[error("blocked")]
+403    Blocked,
+404    /// The peer abandoned transmitting data on this stream.
+405    ///
+406    /// Carries an application-defined error code.
+407    #[error("reset by peer: code {0}")]
+408    Reset(VarInt),
+409}
+410
+411/// Errors triggered when opening a recv stream for reading
+412#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+413pub enum ReadableError {
+414    /// The stream has not been opened or was already stopped, finished, or reset
+415    #[error("closed stream")]
+416    ClosedStream,
+417    /// Attempted an ordered read following an unordered read
+418    ///
+419    /// Performing an unordered read allows discontinuities to arise in the receive buffer of a
+420    /// stream which cannot be recovered, making further ordered reads impossible.
+421    #[error("ordered read after unordered read")]
+422    IllegalOrderedRead,
+423}
+424
+425impl From<IllegalOrderedRead> for ReadableError {
+426    fn from(_: IllegalOrderedRead) -> Self {
+427        Self::IllegalOrderedRead
+428    }
+429}
+430
+431#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+432enum RecvState {
+433    Recv { size: Option<u64> },
+434    ResetRecvd { size: u64, error_code: VarInt },
+435}
+436
+437impl Default for RecvState {
+438    fn default() -> Self {
+439        Self::Recv { size: None }
+440    }
+441}
+442
+443#[cfg(test)]
+444mod tests {
+445    use bytes::Bytes;
+446
+447    use crate::{Dir, Side};
+448
+449    use super::*;
+450
+451    #[test]
+452    fn reordered_frames_while_stopped() {
+453        const INITIAL_BYTES: u64 = 3;
+454        const INITIAL_OFFSET: u64 = 3;
+455        const RECV_WINDOW: u64 = 8;
+456        let mut s = Recv::new(RECV_WINDOW);
+457        let mut data_recvd = 0;
+458        // Receive bytes 3..6
+459        let (new_bytes, is_closed) = s
+460            .ingest(
+461                frame::Stream {
+462                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+463                    offset: INITIAL_OFFSET,
+464                    fin: false,
+465                    data: Bytes::from_static(&[0; INITIAL_BYTES as usize]),
+466                },
+467                123,
+468                data_recvd,
+469                data_recvd + 1024,
+470            )
+471            .unwrap();
+472        data_recvd += new_bytes;
+473        assert_eq!(new_bytes, INITIAL_OFFSET + INITIAL_BYTES);
+474        assert!(!is_closed);
+475
+476        let (credits, transmit) = s.stop().unwrap();
+477        assert!(transmit.should_transmit());
+478        assert_eq!(
+479            credits,
+480            INITIAL_OFFSET + INITIAL_BYTES,
+481            "full connection flow control credit is issued by stop"
+482        );
+483
+484        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+485        assert!(!transmit.should_transmit());
+486        assert_eq!(
+487            max_stream_data, RECV_WINDOW,
+488            "stream flow control credit isn't issued by stop"
+489        );
+490
+491        // Receive byte 7
+492        let (new_bytes, is_closed) = s
+493            .ingest(
+494                frame::Stream {
+495                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+496                    offset: RECV_WINDOW - 1,
+497                    fin: false,
+498                    data: Bytes::from_static(&[0; 1]),
+499                },
+500                123,
+501                data_recvd,
+502                data_recvd + 1024,
+503            )
+504            .unwrap();
+505        data_recvd += new_bytes;
+506        assert_eq!(new_bytes, RECV_WINDOW - (INITIAL_OFFSET + INITIAL_BYTES));
+507        assert!(!is_closed);
+508
+509        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+510        assert!(!transmit.should_transmit());
+511        assert_eq!(
+512            max_stream_data, RECV_WINDOW,
+513            "stream flow control credit isn't issued after stop"
+514        );
+515
+516        // Receive bytes 0..3
+517        let (new_bytes, is_closed) = s
+518            .ingest(
+519                frame::Stream {
+520                    id: StreamId::new(Side::Client, Dir::Uni, 0),
+521                    offset: 0,
+522                    fin: false,
+523                    data: Bytes::from_static(&[0; INITIAL_OFFSET as usize]),
+524                },
+525                123,
+526                data_recvd,
+527                data_recvd + 1024,
+528            )
+529            .unwrap();
+530        assert_eq!(
+531            new_bytes, 0,
+532            "reordered frames don't issue connection-level flow control for stopped streams"
+533        );
+534        assert!(!is_closed);
+535
+536        let (max_stream_data, transmit) = s.max_stream_data(RECV_WINDOW);
+537        assert!(!transmit.should_transmit());
+538        assert_eq!(
+539            max_stream_data, RECV_WINDOW,
+540            "stream flow control credit isn't issued after stop"
+541        );
+542    }
+543}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/streams/send.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/streams/send.rs.html new file mode 100644 index 000000000..ec091cda4 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/streams/send.rs.html @@ -0,0 +1,402 @@ +send.rs - source

iroh_quinn_proto/connection/streams/
send.rs

1use bytes::Bytes;
+2use thiserror::Error;
+3
+4use crate::{VarInt, connection::send_buffer::SendBuffer, frame};
+5
+6#[derive(Debug)]
+7pub(super) struct Send {
+8    pub(super) max_data: u64,
+9    pub(super) state: SendState,
+10    pub(super) pending: SendBuffer,
+11    pub(super) priority: i32,
+12    /// Whether a frame containing a FIN bit must be transmitted, even if we don't have any new data
+13    pub(super) fin_pending: bool,
+14    /// Whether this stream is in the `connection_blocked` list of `Streams`
+15    pub(super) connection_blocked: bool,
+16    /// The reason the peer wants us to stop, if `STOP_SENDING` was received
+17    pub(super) stop_reason: Option<VarInt>,
+18}
+19
+20impl Send {
+21    pub(super) fn new(max_data: VarInt) -> Box<Self> {
+22        Box::new(Self {
+23            max_data: max_data.into(),
+24            state: SendState::Ready,
+25            pending: SendBuffer::new(),
+26            priority: 0,
+27            fin_pending: false,
+28            connection_blocked: false,
+29            stop_reason: None,
+30        })
+31    }
+32
+33    /// Whether the stream has been reset
+34    pub(super) fn is_reset(&self) -> bool {
+35        matches!(self.state, SendState::ResetSent)
+36    }
+37
+38    pub(super) fn finish(&mut self) -> Result<(), FinishError> {
+39        if let Some(error_code) = self.stop_reason {
+40            Err(FinishError::Stopped(error_code))
+41        } else if self.state == SendState::Ready {
+42            self.state = SendState::DataSent {
+43                finish_acked: false,
+44            };
+45            self.fin_pending = true;
+46            Ok(())
+47        } else {
+48            Err(FinishError::ClosedStream)
+49        }
+50    }
+51
+52    pub(super) fn write<S: BytesSource>(
+53        &mut self,
+54        source: &mut S,
+55        limit: u64,
+56    ) -> Result<Written, WriteError> {
+57        if !self.is_writable() {
+58            return Err(WriteError::ClosedStream);
+59        }
+60        if let Some(error_code) = self.stop_reason {
+61            return Err(WriteError::Stopped(error_code));
+62        }
+63        let budget = self.max_data - self.pending.offset();
+64        if budget == 0 {
+65            return Err(WriteError::Blocked);
+66        }
+67        let mut limit = limit.min(budget) as usize;
+68
+69        let mut result = Written::default();
+70        loop {
+71            let (chunk, chunks_consumed) = source.pop_chunk(limit);
+72            result.chunks += chunks_consumed;
+73            result.bytes += chunk.len();
+74
+75            if chunk.is_empty() {
+76                break;
+77            }
+78
+79            limit -= chunk.len();
+80            self.pending.write(chunk);
+81        }
+82
+83        Ok(result)
+84    }
+85
+86    /// Update stream state due to a reset sent by the local application
+87    pub(super) fn reset(&mut self) {
+88        use SendState::*;
+89        if let DataSent { .. } | Ready = self.state {
+90            self.state = ResetSent;
+91        }
+92    }
+93
+94    /// Handle STOP_SENDING
+95    ///
+96    /// Returns true if the stream was stopped due to this frame, and false
+97    /// if it had been stopped before
+98    pub(super) fn try_stop(&mut self, error_code: VarInt) -> bool {
+99        if self.stop_reason.is_none() {
+100            self.stop_reason = Some(error_code);
+101            true
+102        } else {
+103            false
+104        }
+105    }
+106
+107    /// Returns whether the stream has been finished and all data has been acknowledged by the peer
+108    pub(super) fn ack(&mut self, frame: frame::StreamMeta) -> bool {
+109        self.pending.ack(frame.offsets);
+110        match self.state {
+111            SendState::DataSent {
+112                ref mut finish_acked,
+113            } => {
+114                *finish_acked |= frame.fin;
+115                *finish_acked && self.pending.is_fully_acked()
+116            }
+117            _ => false,
+118        }
+119    }
+120
+121    /// Handle increase to stream-level flow control limit
+122    ///
+123    /// Returns whether the stream was unblocked
+124    pub(super) fn increase_max_data(&mut self, offset: u64) -> bool {
+125        if offset <= self.max_data || self.state != SendState::Ready {
+126            return false;
+127        }
+128        let was_blocked = self.pending.offset() == self.max_data;
+129        self.max_data = offset;
+130        was_blocked
+131    }
+132
+133    pub(super) fn offset(&self) -> u64 {
+134        self.pending.offset()
+135    }
+136
+137    pub(super) fn is_pending(&self) -> bool {
+138        self.pending.has_unsent_data() || self.fin_pending
+139    }
+140
+141    pub(super) fn is_writable(&self) -> bool {
+142        matches!(self.state, SendState::Ready)
+143    }
+144}
+145
+146/// A [`BytesSource`] implementation for `&'a mut [Bytes]`
+147///
+148/// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to
+149/// a configured limit.
+150pub(crate) struct BytesArray<'a> {
+151    /// The wrapped slice of `Bytes`
+152    chunks: &'a mut [Bytes],
+153    /// The amount of chunks consumed from this source
+154    consumed: usize,
+155}
+156
+157impl<'a> BytesArray<'a> {
+158    pub(crate) fn from_chunks(chunks: &'a mut [Bytes]) -> Self {
+159        Self {
+160            chunks,
+161            consumed: 0,
+162        }
+163    }
+164}
+165
+166impl BytesSource for BytesArray<'_> {
+167    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) {
+168        // The loop exists to skip empty chunks while still marking them as
+169        // consumed
+170        let mut chunks_consumed = 0;
+171
+172        while self.consumed < self.chunks.len() {
+173            let chunk = &mut self.chunks[self.consumed];
+174
+175            if chunk.len() <= limit {
+176                let chunk = std::mem::take(chunk);
+177                self.consumed += 1;
+178                chunks_consumed += 1;
+179                if chunk.is_empty() {
+180                    continue;
+181                }
+182                return (chunk, chunks_consumed);
+183            } else if limit > 0 {
+184                let chunk = chunk.split_to(limit);
+185                return (chunk, chunks_consumed);
+186            } else {
+187                break;
+188            }
+189        }
+190
+191        (Bytes::new(), chunks_consumed)
+192    }
+193}
+194
+195/// A [`BytesSource`] implementation for `&[u8]`
+196///
+197/// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily
+198/// created from a reference. This allows to defer the allocation until it is
+199/// known how much data needs to be copied.
+200pub(crate) struct ByteSlice<'a> {
+201    /// The wrapped byte slice
+202    data: &'a [u8],
+203}
+204
+205impl<'a> ByteSlice<'a> {
+206    pub(crate) fn from_slice(data: &'a [u8]) -> Self {
+207        Self { data }
+208    }
+209}
+210
+211impl BytesSource for ByteSlice<'_> {
+212    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) {
+213        let limit = limit.min(self.data.len());
+214        if limit == 0 {
+215            return (Bytes::new(), 0);
+216        }
+217
+218        let chunk = Bytes::from(self.data[..limit].to_owned());
+219        self.data = &self.data[chunk.len()..];
+220
+221        let chunks_consumed = usize::from(self.data.is_empty());
+222        (chunk, chunks_consumed)
+223    }
+224}
+225
+226/// A source of one or more buffers which can be converted into `Bytes` buffers on demand
+227///
+228/// The purpose of this data type is to defer conversion as long as possible,
+229/// so that no heap allocation is required in case no data is writable.
+230pub(super) trait BytesSource {
+231    /// Returns the next chunk from the source of owned chunks.
+232    ///
+233    /// This method will consume parts of the source.
+234    /// Calling it will yield `Bytes` elements up to the configured `limit`.
+235    ///
+236    /// The method returns a tuple:
+237    /// - The first item is the yielded `Bytes` element. The element will be
+238    ///   empty if the limit is zero or no more data is available.
+239    /// - The second item returns how many complete chunks inside the source had
+240    ///   had been consumed. This can be less than 1, if a chunk inside the
+241    ///   source had been truncated in order to adhere to the limit. It can also
+242    ///   be more than 1, if zero-length chunks had been skipped.
+243    fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize);
+244}
+245
+246/// Indicates how many bytes and chunks had been transferred in a write operation
+247#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
+248pub struct Written {
+249    /// The amount of bytes which had been written
+250    pub bytes: usize,
+251    /// The amount of full chunks which had been written
+252    ///
+253    /// If a chunk was only partially written, it will not be counted by this field.
+254    pub chunks: usize,
+255}
+256
+257/// Errors triggered while writing to a send stream
+258#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+259pub enum WriteError {
+260    /// The peer is not able to accept additional data, or the connection is congested.
+261    ///
+262    /// If the peer issues additional flow control credit, a [`StreamEvent::Writable`] event will
+263    /// be generated, indicating that retrying the write might succeed.
+264    ///
+265    /// [`StreamEvent::Writable`]: crate::StreamEvent::Writable
+266    #[error("unable to accept further writes")]
+267    Blocked,
+268    /// The peer is no longer accepting data on this stream, and it has been implicitly reset. The
+269    /// stream cannot be finished or further written to.
+270    ///
+271    /// Carries an application-defined error code.
+272    ///
+273    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+274    #[error("stopped by peer: code {0}")]
+275    Stopped(VarInt),
+276    /// The stream has not been opened or has already been finished or reset
+277    #[error("closed stream")]
+278    ClosedStream,
+279}
+280
+281#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+282pub(super) enum SendState {
+283    /// Sending new data
+284    Ready,
+285    /// Stream was finished; now sending retransmits only
+286    DataSent { finish_acked: bool },
+287    /// Sent RESET
+288    ResetSent,
+289}
+290
+291/// Reasons why attempting to finish a stream might fail
+292#[derive(Debug, Error, Clone, PartialEq, Eq)]
+293pub enum FinishError {
+294    /// The peer is no longer accepting data on this stream. No
+295    /// [`StreamEvent::Finished`] event will be emitted for this stream.
+296    ///
+297    /// Carries an application-defined error code.
+298    ///
+299    /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished
+300    #[error("stopped by peer: code {0}")]
+301    Stopped(VarInt),
+302    /// The stream has not been opened or was already finished or reset
+303    #[error("closed stream")]
+304    ClosedStream,
+305}
+306
+307#[cfg(test)]
+308mod tests {
+309    use super::*;
+310
+311    #[test]
+312    fn bytes_array() {
+313        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+314        for limit in 0..full.len() {
+315            let mut chunks = [
+316                Bytes::from_static(b""),
+317                Bytes::from_static(b"Hello "),
+318                Bytes::from_static(b"Wo"),
+319                Bytes::from_static(b""),
+320                Bytes::from_static(b"r"),
+321                Bytes::from_static(b"ld"),
+322                Bytes::from_static(b""),
+323                Bytes::from_static(b" 12345678"),
+324                Bytes::from_static(b"9 ABCDE"),
+325                Bytes::from_static(b"F"),
+326                Bytes::from_static(b"GHJIJKLMNOPQRSTUVWXYZ"),
+327            ];
+328            let num_chunks = chunks.len();
+329            let last_chunk_len = chunks[chunks.len() - 1].len();
+330
+331            let mut array = BytesArray::from_chunks(&mut chunks);
+332
+333            let mut buf = Vec::new();
+334            let mut chunks_popped = 0;
+335            let mut chunks_consumed = 0;
+336            let mut remaining = limit;
+337            loop {
+338                let (chunk, consumed) = array.pop_chunk(remaining);
+339                chunks_consumed += consumed;
+340
+341                if !chunk.is_empty() {
+342                    buf.extend_from_slice(&chunk);
+343                    remaining -= chunk.len();
+344                    chunks_popped += 1;
+345                } else {
+346                    break;
+347                }
+348            }
+349
+350            assert_eq!(&buf[..], &full[..limit]);
+351
+352            if limit == full.len() {
+353                // Full consumption of the last chunk
+354                assert_eq!(chunks_consumed, num_chunks);
+355                // Since there are empty chunks, we consume more than there are popped
+356                assert_eq!(chunks_consumed, chunks_popped + 3);
+357            } else if limit > full.len() - last_chunk_len {
+358                // Partial consumption of the last chunk
+359                assert_eq!(chunks_consumed, num_chunks - 1);
+360                assert_eq!(chunks_consumed, chunks_popped + 2);
+361            }
+362        }
+363    }
+364
+365    #[test]
+366    fn byte_slice() {
+367        let full = b"Hello World 123456789 ABCDEFGHJIJKLMNOPQRSTUVWXYZ".to_owned();
+368        for limit in 0..full.len() {
+369            let mut array = ByteSlice::from_slice(&full[..]);
+370
+371            let mut buf = Vec::new();
+372            let mut chunks_popped = 0;
+373            let mut chunks_consumed = 0;
+374            let mut remaining = limit;
+375            loop {
+376                let (chunk, consumed) = array.pop_chunk(remaining);
+377                chunks_consumed += consumed;
+378
+379                if !chunk.is_empty() {
+380                    buf.extend_from_slice(&chunk);
+381                    remaining -= chunk.len();
+382                    chunks_popped += 1;
+383                } else {
+384                    break;
+385                }
+386            }
+387
+388            assert_eq!(&buf[..], &full[..limit]);
+389            if limit != 0 {
+390                assert_eq!(chunks_popped, 1);
+391            } else {
+392                assert_eq!(chunks_popped, 0);
+393            }
+394
+395            if limit == full.len() {
+396                assert_eq!(chunks_consumed, 1);
+397            } else {
+398                assert_eq!(chunks_consumed, 0);
+399            }
+400        }
+401    }
+402}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/streams/state.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/streams/state.rs.html new file mode 100644 index 000000000..de34e0c88 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/streams/state.rs.html @@ -0,0 +1,2107 @@ +state.rs - source

iroh_quinn_proto/connection/streams/
state.rs

1use std::{
+2    collections::{VecDeque, hash_map},
+3    convert::TryFrom,
+4    mem,
+5};
+6
+7use bytes::BufMut;
+8use rustc_hash::FxHashMap;
+9use tracing::{debug, trace};
+10
+11use super::{
+12    PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent,
+13    StreamHalf, ThinRetransmits,
+14};
+15use crate::{
+16    Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt,
+17    coding::BufMutExt,
+18    connection::{qlog::QlogSentPacket, stats::FrameStats},
+19    frame::{self, Frame, FrameStruct, StreamMetaVec},
+20    transport_parameters::TransportParameters,
+21};
+22
+23/// Wrapper around `Recv` that facilitates reusing `Recv` instances
+24#[derive(Debug)]
+25pub(super) enum StreamRecv {
+26    /// A `Recv` that is ready to be opened
+27    Free(Box<Recv>),
+28    /// A `Recv` that has been opened
+29    Open(Box<Recv>),
+30}
+31
+32impl StreamRecv {
+33    /// Returns a reference to the inner `Recv` if the stream is open
+34    pub(super) fn as_open_recv(&self) -> Option<&Recv> {
+35        match self {
+36            Self::Open(r) => Some(r),
+37            _ => None,
+38        }
+39    }
+40
+41    // Returns a mutable reference to the inner `Recv` if the stream is open
+42    pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> {
+43        match self {
+44            Self::Open(r) => Some(r),
+45            _ => None,
+46        }
+47    }
+48
+49    // Returns the inner `Recv`
+50    pub(super) fn into_inner(self) -> Box<Recv> {
+51        match self {
+52            Self::Free(r) | Self::Open(r) => r,
+53        }
+54    }
+55
+56    // Reinitialize the stream so the inner `Recv` can be reused
+57    pub(super) fn free(self, initial_max_data: u64) -> Self {
+58        match self {
+59            Self::Free(_) => unreachable!("Self::Free on reinit()"),
+60            Self::Open(mut recv) => {
+61                recv.reinit(initial_max_data);
+62                Self::Free(recv)
+63            }
+64        }
+65    }
+66}
+67
+68#[allow(unreachable_pub)] // fuzzing only
+69pub struct StreamsState {
+70    pub(super) side: Side,
+71    // Set of streams that are currently open, or could be immediately opened by the peer
+72    pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>,
+73    pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>,
+74    pub(super) free_recv: Vec<StreamRecv>,
+75    pub(super) next: [u64; 2],
+76    /// Maximum number of locally-initiated streams that may be opened over the lifetime of the
+77    /// connection so far, per direction
+78    pub(super) max: [u64; 2],
+79    /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the
+80    /// connection so far, per direction
+81    pub(super) max_remote: [u64; 2],
+82    /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame
+83    sent_max_remote: [u64; 2],
+84    /// Number of streams that we've given the peer permission to open and which aren't fully closed
+85    pub(super) allocated_remote_count: [u64; 2],
+86    /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count`
+87    /// due to `set_max_concurrent` calls.
+88    max_concurrent_remote_count: [u64; 2],
+89    /// Whether `max_concurrent_remote_count` has ever changed
+90    flow_control_adjusted: bool,
+91    /// Lowest remotely-initiated stream index that haven't actually been opened by the peer
+92    pub(super) next_remote: [u64; 2],
+93    /// Whether the remote endpoint has opened any streams the application doesn't know about yet,
+94    /// per directionality
+95    opened: [bool; 2],
+96    // Next to report to the application, once opened
+97    pub(super) next_reported_remote: [u64; 2],
+98    /// Number of outbound streams
+99    ///
+100    /// This differs from `self.send.len()` in that it does not include streams that the peer is
+101    /// permitted to open but which have not yet been opened.
+102    pub(super) send_streams: usize,
+103    /// Streams with outgoing data queued, sorted by priority
+104    pub(super) pending: PendingStreamsQueue,
+105
+106    events: VecDeque<StreamEvent>,
+107    /// Streams blocked on connection-level flow control or stream window space
+108    ///
+109    /// Streams are only added to this list when a write fails.
+110    pub(super) connection_blocked: Vec<StreamId>,
+111    /// Connection-level flow control budget dictated by the peer
+112    pub(super) max_data: u64,
+113    /// The initial receive window
+114    receive_window: u64,
+115    /// Limit on incoming data, which is transmitted through `MAX_DATA` frames
+116    local_max_data: u64,
+117    /// The last value of `MAX_DATA` which had been queued for transmission in
+118    /// an outgoing `MAX_DATA` frame
+119    sent_max_data: VarInt,
+120    /// Sum of current offsets of all send streams.
+121    pub(super) data_sent: u64,
+122    /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound.
+123    data_recvd: u64,
+124    /// Total quantity of unacknowledged outgoing data
+125    pub(super) unacked_data: u64,
+126    /// Configured upper bound for `unacked_data`.
+127    ///
+128    /// Note this may be less than `unacked_data` if the user has set a new value.
+129    pub(super) send_window: u64,
+130    /// Configured upper bound for how much unacked data the peer can send us per stream
+131    pub(super) stream_receive_window: u64,
+132
+133    // Pertinent state from the TransportParameters supplied by the peer
+134    initial_max_stream_data_uni: VarInt,
+135    initial_max_stream_data_bidi_local: VarInt,
+136    initial_max_stream_data_bidi_remote: VarInt,
+137
+138    /// The shrink to be applied to local_max_data when receive_window is shrunk
+139    receive_window_shrink_debt: u64,
+140}
+141
+142impl StreamsState {
+143    #[allow(unreachable_pub)] // fuzzing only
+144    pub fn new(
+145        side: Side,
+146        max_remote_uni: VarInt,
+147        max_remote_bi: VarInt,
+148        send_window: u64,
+149        receive_window: VarInt,
+150        stream_receive_window: VarInt,
+151    ) -> Self {
+152        let mut this = Self {
+153            side,
+154            send: FxHashMap::default(),
+155            recv: FxHashMap::default(),
+156            free_recv: Vec::new(),
+157            next: [0, 0],
+158            max: [0, 0],
+159            max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+160            sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()],
+161            allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+162            max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()],
+163            flow_control_adjusted: false,
+164            next_remote: [0, 0],
+165            opened: [false, false],
+166            next_reported_remote: [0, 0],
+167            send_streams: 0,
+168            pending: PendingStreamsQueue::new(),
+169            events: VecDeque::new(),
+170            connection_blocked: Vec::new(),
+171            max_data: 0,
+172            receive_window: receive_window.into(),
+173            local_max_data: receive_window.into(),
+174            sent_max_data: receive_window,
+175            data_sent: 0,
+176            data_recvd: 0,
+177            unacked_data: 0,
+178            send_window,
+179            stream_receive_window: stream_receive_window.into(),
+180            initial_max_stream_data_uni: 0u32.into(),
+181            initial_max_stream_data_bidi_local: 0u32.into(),
+182            initial_max_stream_data_bidi_remote: 0u32.into(),
+183            receive_window_shrink_debt: 0,
+184        };
+185
+186        for dir in Dir::iter() {
+187            for i in 0..this.max_remote[dir as usize] {
+188                this.insert(true, StreamId::new(!side, dir, i));
+189            }
+190        }
+191
+192        this
+193    }
+194
+195    pub(crate) fn set_params(&mut self, params: &TransportParameters) {
+196        self.initial_max_stream_data_uni = params.initial_max_stream_data_uni;
+197        self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local;
+198        self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote;
+199        self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into();
+200        self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into();
+201        self.received_max_data(params.initial_max_data);
+202        for i in 0..self.max_remote[Dir::Bi as usize] {
+203            let id = StreamId::new(!self.side, Dir::Bi, i);
+204            if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+205                s.max_data = params.initial_max_stream_data_bidi_local.into();
+206            }
+207        }
+208    }
+209
+210    /// Ensure we have space for at least a full flow control window of remotely-initiated streams
+211    /// to be open, and notify the peer if the window has moved
+212    fn ensure_remote_streams(&mut self, dir: Dir) {
+213        let new_count = self.max_concurrent_remote_count[dir as usize]
+214            .saturating_sub(self.allocated_remote_count[dir as usize]);
+215        for i in 0..new_count {
+216            let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i);
+217            self.insert(true, id);
+218        }
+219        self.allocated_remote_count[dir as usize] += new_count;
+220        self.max_remote[dir as usize] += new_count;
+221    }
+222
+223    pub(crate) fn zero_rtt_rejected(&mut self) {
+224        // Revert to initial state for outgoing streams
+225        for dir in Dir::iter() {
+226            for i in 0..self.next[dir as usize] {
+227                // We don't bother calling `stream_freed` here because we explicitly reset affected
+228                // counters below.
+229                let id = StreamId::new(self.side, dir, i);
+230                self.send.remove(&id).unwrap();
+231                if let Dir::Bi = dir {
+232                    self.recv.remove(&id).unwrap();
+233                }
+234            }
+235            self.next[dir as usize] = 0;
+236
+237            // If 0-RTT was rejected, any flow control frames we sent were lost.
+238            if self.flow_control_adjusted {
+239                // Conservative approximation of whatever we sent in transport parameters
+240                self.sent_max_remote[dir as usize] = 0;
+241            }
+242        }
+243
+244        self.pending.clear();
+245        self.send_streams = 0;
+246        self.data_sent = 0;
+247        self.connection_blocked.clear();
+248    }
+249
+250    /// Process incoming stream frame
+251    ///
+252    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+253    pub(crate) fn received(
+254        &mut self,
+255        frame: frame::Stream,
+256        payload_len: usize,
+257    ) -> Result<ShouldTransmit, TransportError> {
+258        let id = frame.id;
+259        self.validate_receive_id(id).inspect_err(|_e| {
+260            debug!("received illegal STREAM frame");
+261        })?;
+262
+263        let rs = match self
+264            .recv
+265            .get_mut(&id)
+266            .map(get_or_insert_recv(self.stream_receive_window))
+267        {
+268            Some(rs) => rs,
+269            None => {
+270                trace!("dropping frame for closed stream");
+271                return Ok(ShouldTransmit(false));
+272            }
+273        };
+274
+275        if !rs.is_receiving() {
+276            trace!("dropping frame for finished stream");
+277            return Ok(ShouldTransmit(false));
+278        }
+279
+280        let (new_bytes, closed) =
+281            rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?;
+282        self.data_recvd = self.data_recvd.saturating_add(new_bytes);
+283
+284        if !rs.stopped {
+285            self.on_stream_frame(true, id);
+286            return Ok(ShouldTransmit(false));
+287        }
+288
+289        // Stopped streams become closed instantly on FIN, so check whether we need to clean up
+290        if closed {
+291            let rs = self.recv.remove(&id).flatten().unwrap();
+292            self.stream_recv_freed(id, rs);
+293        }
+294
+295        // We don't buffer data on stopped streams, so issue flow control credit immediately
+296        Ok(self.add_read_credits(new_bytes))
+297    }
+298
+299    /// Process incoming RESET_STREAM frame
+300    ///
+301    /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted
+302    #[allow(unreachable_pub)] // fuzzing only
+303    pub fn received_reset(
+304        &mut self,
+305        frame: frame::ResetStream,
+306    ) -> Result<ShouldTransmit, TransportError> {
+307        let frame::ResetStream {
+308            id,
+309            error_code,
+310            final_offset,
+311        } = frame;
+312        self.validate_receive_id(id).inspect_err(|_e| {
+313            debug!("received illegal RESET_STREAM frame");
+314        })?;
+315
+316        let rs = match self
+317            .recv
+318            .get_mut(&id)
+319            .map(get_or_insert_recv(self.stream_receive_window))
+320        {
+321            Some(stream) => stream,
+322            None => {
+323                trace!("received RESET_STREAM on closed stream");
+324                return Ok(ShouldTransmit(false));
+325            }
+326        };
+327
+328        // State transition
+329        if !rs.reset(
+330            error_code,
+331            final_offset,
+332            self.data_recvd,
+333            self.local_max_data,
+334        )? {
+335            // Redundant reset
+336            return Ok(ShouldTransmit(false));
+337        }
+338        let bytes_read = rs.assembler.bytes_read();
+339        let stopped = rs.stopped;
+340        let end = rs.end;
+341        if stopped {
+342            // Stopped streams should be disposed immediately on reset
+343            let rs = self.recv.remove(&id).flatten().unwrap();
+344            self.stream_recv_freed(id, rs);
+345        }
+346        self.on_stream_frame(!stopped, id);
+347
+348        // Update connection-level flow control
+349        Ok(if bytes_read != final_offset.into_inner() {
+350            // bytes_read is always <= end, so this won't underflow.
+351            self.data_recvd = self
+352                .data_recvd
+353                .saturating_add(u64::from(final_offset) - end);
+354            self.add_read_credits(u64::from(final_offset) - bytes_read)
+355        } else {
+356            ShouldTransmit(false)
+357        })
+358    }
+359
+360    /// Process incoming `STOP_SENDING` frame
+361    #[allow(unreachable_pub)] // fuzzing only
+362    pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) {
+363        let max_send_data = self.max_send_data(id);
+364        let stream = match self
+365            .send
+366            .get_mut(&id)
+367            .map(get_or_insert_send(max_send_data))
+368        {
+369            Some(ss) => ss,
+370            None => return,
+371        };
+372
+373        if stream.try_stop(error_code) {
+374            self.events
+375                .push_back(StreamEvent::Stopped { id, error_code });
+376            self.on_stream_frame(false, id);
+377        }
+378    }
+379
+380    pub(crate) fn reset_acked(&mut self, id: StreamId) {
+381        match self.send.entry(id) {
+382            hash_map::Entry::Vacant(_) => {}
+383            hash_map::Entry::Occupied(e) => {
+384                if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) {
+385                    e.remove_entry();
+386                    self.stream_freed(id, StreamHalf::Send);
+387                }
+388            }
+389        }
+390    }
+391
+392    /// Whether any stream data is queued, regardless of control frames
+393    pub(crate) fn can_send_stream_data(&self) -> bool {
+394        // Reset streams may linger in the pending stream list, but will never produce stream frames
+395        self.pending.iter().any(|stream| {
+396            self.send
+397                .get(&stream.id)
+398                .and_then(|s| s.as_ref())
+399                .is_some_and(|s| !s.is_reset())
+400        })
+401    }
+402
+403    /// Whether MAX_STREAM_DATA frames could be sent for stream `id`
+404    pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool {
+405        self.recv
+406            .get(&id)
+407            .and_then(|s| s.as_ref())
+408            .and_then(|s| s.as_open_recv())
+409            .is_some_and(|s| s.can_send_flow_control())
+410    }
+411
+412    pub(in crate::connection) fn write_control_frames(
+413        &mut self,
+414        buf: &mut impl BufMut,
+415        pending: &mut Retransmits,
+416        retransmits: &mut ThinRetransmits,
+417        stats: &mut FrameStats,
+418        #[allow(unused)] qlog: &mut QlogSentPacket,
+419    ) {
+420        // RESET_STREAM
+421        while buf.remaining_mut() > frame::ResetStream::SIZE_BOUND {
+422            let (id, error_code) = match pending.reset_stream.pop() {
+423                Some(x) => x,
+424                None => break,
+425            };
+426            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+427                Some(x) => x,
+428                None => continue,
+429            };
+430            trace!(stream = %id, "RESET_STREAM");
+431            retransmits
+432                .get_or_create()
+433                .reset_stream
+434                .push((id, error_code));
+435            let frame = frame::ResetStream {
+436                id,
+437                error_code,
+438                final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"),
+439            };
+440            frame.encode(buf);
+441            qlog.frame(&Frame::ResetStream(frame));
+442            stats.reset_stream += 1;
+443        }
+444
+445        // STOP_SENDING
+446        while buf.remaining_mut() > frame::StopSending::SIZE_BOUND {
+447            let frame = match pending.stop_sending.pop() {
+448                Some(x) => x,
+449                None => break,
+450            };
+451            // We may need to transmit STOP_SENDING even for streams whose state we have discarded,
+452            // because we are able to discard local state for stopped streams immediately upon
+453            // receiving FIN, even if the peer still has arbitrarily large amounts of data to
+454            // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this
+455            // a little by dropping the frame if we specifically know the stream's been reset by the
+456            // peer, but we discard that information as soon as the application consumes it, so it
+457            // can't be relied upon regardless.
+458            trace!(stream = %frame.id, "STOP_SENDING");
+459            frame.encode(buf);
+460            qlog.frame(&Frame::StopSending(frame));
+461            retransmits.get_or_create().stop_sending.push(frame);
+462            stats.stop_sending += 1;
+463        }
+464
+465        // MAX_DATA
+466        if pending.max_data && buf.remaining_mut() > 9 {
+467            pending.max_data = false;
+468
+469            // `local_max_data` can grow bigger than `VarInt`.
+470            // For transmission inside QUIC frames we need to clamp it to the
+471            // maximum allowed `VarInt` size.
+472            let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX);
+473
+474            trace!(value = max.into_inner(), "MAX_DATA");
+475            if max > self.sent_max_data {
+476                // Record that a `MAX_DATA` announcing a certain window was sent. This will
+477                // suppress enqueuing further `MAX_DATA` frames unless either the previous
+478                // transmission was not acknowledged or the window further increased.
+479                self.sent_max_data = max;
+480            }
+481
+482            retransmits.get_or_create().max_data = true;
+483            buf.write(frame::FrameType::MAX_DATA);
+484            buf.write(max);
+485            qlog.frame(&Frame::MaxData(max));
+486            stats.max_data += 1;
+487        }
+488
+489        // MAX_STREAM_DATA
+490        while buf.remaining_mut() > 17 {
+491            let id = match pending.max_stream_data.iter().next() {
+492                Some(x) => *x,
+493                None => break,
+494            };
+495            pending.max_stream_data.remove(&id);
+496            let rs = match self
+497                .recv
+498                .get_mut(&id)
+499                .and_then(|s| s.as_mut())
+500                .and_then(|s| s.as_open_recv_mut())
+501            {
+502                Some(x) => x,
+503                None => continue,
+504            };
+505            if !rs.can_send_flow_control() {
+506                continue;
+507            }
+508            retransmits.get_or_create().max_stream_data.insert(id);
+509
+510            let (max, _) = rs.max_stream_data(self.stream_receive_window);
+511            rs.record_sent_max_stream_data(max);
+512
+513            trace!(stream = %id, max = max, "MAX_STREAM_DATA");
+514            buf.write(frame::FrameType::MAX_STREAM_DATA);
+515            buf.write(id);
+516            buf.write_var(max);
+517            qlog.frame(&Frame::MaxStreamData { id, offset: max });
+518            stats.max_stream_data += 1;
+519        }
+520
+521        // MAX_STREAMS
+522        for dir in Dir::iter() {
+523            if !pending.max_stream_id[dir as usize] || buf.remaining_mut() <= 9 {
+524                continue;
+525            }
+526
+527            pending.max_stream_id[dir as usize] = false;
+528            retransmits.get_or_create().max_stream_id[dir as usize] = true;
+529            self.sent_max_remote[dir as usize] = self.max_remote[dir as usize];
+530            trace!(
+531                value = self.max_remote[dir as usize],
+532                "MAX_STREAMS ({:?})", dir
+533            );
+534            buf.write(match dir {
+535                Dir::Uni => frame::FrameType::MAX_STREAMS_UNI,
+536                Dir::Bi => frame::FrameType::MAX_STREAMS_BIDI,
+537            });
+538            let count = self.max_remote[dir as usize];
+539            buf.write_var(count);
+540            qlog.frame(&Frame::MaxStreams { dir, count });
+541            match dir {
+542                Dir::Uni => stats.max_streams_uni += 1,
+543                Dir::Bi => stats.max_streams_bidi += 1,
+544            }
+545        }
+546    }
+547
+548    pub(crate) fn write_stream_frames(
+549        &mut self,
+550        buf: &mut impl BufMut,
+551        fair: bool,
+552        #[allow(unused)] qlog: &mut QlogSentPacket,
+553    ) -> StreamMetaVec {
+554        let mut stream_frames = StreamMetaVec::new();
+555        while buf.remaining_mut() > frame::Stream::SIZE_BOUND {
+556            // Pop the stream of the highest priority that currently has pending data. If
+557            // the stream still has some pending data left after writing, it will be
+558            // reinserted, otherwise not
+559            let Some(stream) = self.pending.pop() else {
+560                break;
+561            };
+562
+563            let id = stream.id;
+564
+565            let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+566                Some(s) => s,
+567                // Stream was reset with pending data and the reset was acknowledged
+568                None => continue,
+569            };
+570
+571            // Reset streams aren't removed from the pending list and still exist while the peer
+572            // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to
+573            // check for them explicitly.
+574            if stream.is_reset() {
+575                continue;
+576            }
+577
+578            // Now that we know the `StreamId`, we can better account for how many bytes
+579            // are required to encode it.
+580            let max_buf_size = buf.remaining_mut() - 1 - VarInt::size(id.into());
+581            let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size);
+582            let fin = offsets.end == stream.pending.offset()
+583                && matches!(stream.state, SendState::DataSent { .. });
+584            if fin {
+585                stream.fin_pending = false;
+586            }
+587
+588            if stream.is_pending() {
+589                // If the stream still has pending data, reinsert it, possibly with an updated priority value
+590                // Fairness with other streams is achieved by implementing round-robin scheduling,
+591                // so that the other streams will have a chance to write data
+592                // before we touch this stream again.
+593                if fair {
+594                    self.pending.push_pending(id, stream.priority);
+595                } else {
+596                    self.pending.reinsert_pending(id, stream.priority);
+597                }
+598            }
+599
+600            let meta = frame::StreamMeta { id, offsets, fin };
+601            trace!(id = %meta.id, off = meta.offsets.start, len = meta.offsets.end - meta.offsets.start, fin = meta.fin, "STREAM");
+602            meta.encode(encode_length, buf);
+603            qlog.frame_stream(&meta);
+604
+605            // The range might not be retrievable in a single `get` if it is
+606            // stored in noncontiguous fashion. Therefore this loop iterates
+607            // until the range is fully copied into the frame.
+608            let mut offsets = meta.offsets.clone();
+609            while offsets.start != offsets.end {
+610                let data = stream.pending.get(offsets.clone());
+611                offsets.start += data.len() as u64;
+612                buf.put_slice(data);
+613            }
+614            stream_frames.push(meta);
+615        }
+616
+617        stream_frames
+618    }
+619
+620    /// Notify the application that new streams were opened or a stream became readable.
+621    fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) {
+622        if stream.initiator() == self.side {
+623            // Notifying about the opening of locally-initiated streams would be redundant.
+624            if notify_readable {
+625                self.events.push_back(StreamEvent::Readable { id: stream });
+626            }
+627            return;
+628        }
+629        let next = &mut self.next_remote[stream.dir() as usize];
+630        if stream.index() >= *next {
+631            *next = stream.index() + 1;
+632            self.opened[stream.dir() as usize] = true;
+633        } else if notify_readable {
+634            self.events.push_back(StreamEvent::Readable { id: stream });
+635        }
+636    }
+637
+638    pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) {
+639        let mut entry = match self.send.entry(frame.id) {
+640            hash_map::Entry::Vacant(_) => return,
+641            hash_map::Entry::Occupied(e) => e,
+642        };
+643
+644        let stream = match entry.get_mut().as_mut() {
+645            Some(s) => s,
+646            None => {
+647                // Because we only call this after sending data on this stream,
+648                // this closure should be unreachable. If we did somehow screw that up,
+649                // then we might hit an underflow below with unpredictable effects down
+650                // the line. Best to short-circuit.
+651                return;
+652            }
+653        };
+654
+655        if stream.is_reset() {
+656            // We account for outstanding data on reset streams at time of reset
+657            return;
+658        }
+659        let id = frame.id;
+660        self.unacked_data -= frame.offsets.end - frame.offsets.start;
+661        if !stream.ack(frame) {
+662            // The stream is unfinished or may still need retransmits
+663            return;
+664        }
+665
+666        entry.remove_entry();
+667        self.stream_freed(id, StreamHalf::Send);
+668        self.events.push_back(StreamEvent::Finished { id });
+669    }
+670
+671    pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) {
+672        let stream = match self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) {
+673            // Loss of data on a closed stream is a noop
+674            None => return,
+675            Some(x) => x,
+676        };
+677        if !stream.is_pending() {
+678            self.pending.push_pending(frame.id, stream.priority);
+679        }
+680        stream.fin_pending |= frame.fin;
+681        stream.pending.retransmit(frame.offsets);
+682    }
+683
+684    pub(crate) fn retransmit_all_for_0rtt(&mut self) {
+685        for dir in Dir::iter() {
+686            for index in 0..self.next[dir as usize] {
+687                let id = StreamId::new(Side::Client, dir, index);
+688                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+689                    Some(stream) => stream,
+690                    None => continue,
+691                };
+692                if stream.pending.is_fully_acked() && !stream.fin_pending {
+693                    // Stream data can't be acked in 0-RTT, so we must not have sent anything on
+694                    // this stream
+695                    continue;
+696                }
+697                if !stream.is_pending() {
+698                    self.pending.push_pending(id, stream.priority);
+699                }
+700                stream.pending.retransmit_all_for_0rtt();
+701            }
+702        }
+703    }
+704
+705    pub(crate) fn received_max_streams(
+706        &mut self,
+707        dir: Dir,
+708        count: u64,
+709    ) -> Result<(), TransportError> {
+710        if count > MAX_STREAM_COUNT {
+711            return Err(TransportError::FRAME_ENCODING_ERROR(
+712                "unrepresentable stream limit",
+713            ));
+714        }
+715
+716        let current = &mut self.max[dir as usize];
+717        if count > *current {
+718            *current = count;
+719            self.events.push_back(StreamEvent::Available { dir });
+720        }
+721
+722        Ok(())
+723    }
+724
+725    /// Handle increase to connection-level flow control limit
+726    pub(crate) fn received_max_data(&mut self, n: VarInt) {
+727        self.max_data = self.max_data.max(n.into());
+728    }
+729
+730    pub(crate) fn received_max_stream_data(
+731        &mut self,
+732        id: StreamId,
+733        offset: u64,
+734    ) -> Result<(), TransportError> {
+735        if id.initiator() != self.side && id.dir() == Dir::Uni {
+736            debug!("got MAX_STREAM_DATA on recv-only {}", id);
+737            return Err(TransportError::STREAM_STATE_ERROR(
+738                "MAX_STREAM_DATA on recv-only stream",
+739            ));
+740        }
+741
+742        let write_limit = self.write_limit();
+743        let max_send_data = self.max_send_data(id);
+744        if let Some(ss) = self
+745            .send
+746            .get_mut(&id)
+747            .map(get_or_insert_send(max_send_data))
+748        {
+749            if ss.increase_max_data(offset) {
+750                if write_limit > 0 {
+751                    self.events.push_back(StreamEvent::Writable { id });
+752                } else if !ss.connection_blocked {
+753                    // The stream is still blocked on the connection flow control
+754                    // window. In order to get unblocked when the window relaxes
+755                    // it needs to be in the connection blocked list.
+756                    ss.connection_blocked = true;
+757                    self.connection_blocked.push(id);
+758                }
+759            }
+760        } else if id.initiator() == self.side && self.is_local_unopened(id) {
+761            debug!("got MAX_STREAM_DATA on unopened {}", id);
+762            return Err(TransportError::STREAM_STATE_ERROR(
+763                "MAX_STREAM_DATA on unopened stream",
+764            ));
+765        }
+766
+767        self.on_stream_frame(false, id);
+768        Ok(())
+769    }
+770
+771    /// Returns the maximum amount of data this is allowed to be written on the connection
+772    pub(crate) fn write_limit(&self) -> u64 {
+773        (self.max_data - self.data_sent)
+774            // `send_window` can be set after construction to something *less* than `unacked_data`
+775            .min(self.send_window.saturating_sub(self.unacked_data))
+776    }
+777
+778    /// Yield stream events
+779    pub(crate) fn poll(&mut self) -> Option<StreamEvent> {
+780        if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false))
+781        {
+782            return Some(StreamEvent::Opened { dir });
+783        }
+784
+785        if self.write_limit() > 0 {
+786            while let Some(id) = self.connection_blocked.pop() {
+787                let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) {
+788                    None => continue,
+789                    Some(s) => s,
+790                };
+791
+792                debug_assert!(stream.connection_blocked);
+793                stream.connection_blocked = false;
+794
+795                // If it's no longer sensible to write to a stream (even to detect an error) then don't
+796                // report it.
+797                if stream.is_writable() && stream.max_data > stream.offset() {
+798                    return Some(StreamEvent::Writable { id });
+799                }
+800            }
+801        }
+802
+803        self.events.pop_front()
+804    }
+805
+806    /// Queues MAX_STREAM_ID frames in `pending` if needed
+807    ///
+808    /// Returns whether any frames were queued.
+809    pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool {
+810        let mut queued = false;
+811        for dir in Dir::iter() {
+812            let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize];
+813            // To reduce traffic, only announce updates if at least 1/8 of the flow control window
+814            // has been consumed.
+815            if diff > self.max_concurrent_remote_count[dir as usize] / 8 {
+816                pending.max_stream_id[dir as usize] = true;
+817                queued = true;
+818            }
+819        }
+820        queued
+821    }
+822
+823    /// Check for errors entailed by the peer's use of `id` as a send stream
+824    fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> {
+825        if self.side == id.initiator() {
+826            match id.dir() {
+827                Dir::Uni => {
+828                    return Err(TransportError::STREAM_STATE_ERROR(
+829                        "illegal operation on send-only stream",
+830                    ));
+831                }
+832                Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => {
+833                    return Err(TransportError::STREAM_STATE_ERROR(
+834                        "operation on unopened stream",
+835                    ));
+836                }
+837                Dir::Bi => {}
+838            };
+839        } else {
+840            let limit = self.max_remote[id.dir() as usize];
+841            if id.index() >= limit {
+842                return Err(TransportError::STREAM_LIMIT_ERROR(""));
+843            }
+844        }
+845        Ok(())
+846    }
+847
+848    /// Whether a locally initiated stream has never been open
+849    pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool {
+850        id.index() >= self.next[id.dir() as usize]
+851    }
+852
+853    pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) {
+854        self.flow_control_adjusted = true;
+855        self.max_concurrent_remote_count[dir as usize] = count.into();
+856        self.ensure_remote_streams(dir);
+857    }
+858
+859    pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 {
+860        self.allocated_remote_count[dir as usize]
+861    }
+862
+863    pub(crate) fn set_send_window(&mut self, send_window: u64) {
+864        self.send_window = send_window;
+865    }
+866
+867    /// Set the receive_window and returns whether the receive_window has been
+868    /// expanded or shrunk: true if expanded, false if shrunk.
+869    pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool {
+870        let receive_window = receive_window.into();
+871        let mut expanded = false;
+872        if receive_window > self.receive_window {
+873            self.local_max_data = self
+874                .local_max_data
+875                .saturating_add(receive_window - self.receive_window);
+876            expanded = true;
+877        } else {
+878            let diff = self.receive_window - receive_window;
+879            self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff);
+880        }
+881        self.receive_window = receive_window;
+882        expanded
+883    }
+884
+885    pub(super) fn insert(&mut self, remote: bool, id: StreamId) {
+886        let bi = id.dir() == Dir::Bi;
+887        // bidirectional OR (unidirectional AND NOT remote)
+888        if bi || !remote {
+889            assert!(self.send.insert(id, None).is_none());
+890        }
+891        // bidirectional OR (unidirectional AND remote)
+892        if bi || remote {
+893            let recv = self.free_recv.pop();
+894            assert!(self.recv.insert(id, recv).is_none());
+895        }
+896    }
+897
+898    /// Adds credits to the connection flow control window
+899    ///
+900    /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible.
+901    /// This will only be the case if the window update would is significant
+902    /// enough. As soon as a window update with a `MAX_DATA` frame has been
+903    /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to
+904    /// suppress sending further updates until the window increases significantly
+905    /// again.
+906    pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit {
+907        if credits > self.receive_window_shrink_debt {
+908            let net_credits = credits - self.receive_window_shrink_debt;
+909            self.local_max_data = self.local_max_data.saturating_add(net_credits);
+910            self.receive_window_shrink_debt = 0;
+911        } else {
+912            self.receive_window_shrink_debt -= credits;
+913        }
+914
+915        if self.local_max_data > VarInt::MAX.into_inner() {
+916            return ShouldTransmit(false);
+917        }
+918
+919        // Only announce a window update if it's significant enough
+920        // to make it worthwhile sending a MAX_DATA frame.
+921        // We use a fraction of the configured connection receive window to make
+922        // the decision, to accommodate for connection using bigger windows requiring
+923        // less updates.
+924        let diff = self.local_max_data - self.sent_max_data.into_inner();
+925        ShouldTransmit(diff >= (self.receive_window / 8))
+926    }
+927
+928    /// Update counters for removal of a stream
+929    pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) {
+930        if id.initiator() != self.side {
+931            let fully_free = id.dir() == Dir::Uni
+932                || match half {
+933                    StreamHalf::Send => !self.recv.contains_key(&id),
+934                    StreamHalf::Recv => !self.send.contains_key(&id),
+935                };
+936            if fully_free {
+937                self.allocated_remote_count[id.dir() as usize] -= 1;
+938                self.ensure_remote_streams(id.dir());
+939            }
+940        }
+941        if half == StreamHalf::Send {
+942            self.send_streams -= 1;
+943        }
+944    }
+945
+946    pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) {
+947        self.free_recv.push(recv.free(self.stream_receive_window));
+948        self.stream_freed(id, StreamHalf::Recv);
+949    }
+950
+951    pub(super) fn max_send_data(&self, id: StreamId) -> VarInt {
+952        let remote = self.side != id.initiator();
+953        match id.dir() {
+954            Dir::Uni => self.initial_max_stream_data_uni,
+955            // Remote/local appear reversed here because the transport parameters are named from
+956            // the perspective of the peer.
+957            Dir::Bi if remote => self.initial_max_stream_data_bidi_local,
+958            Dir::Bi => self.initial_max_stream_data_bidi_remote,
+959        }
+960    }
+961}
+962
+963#[inline]
+964pub(super) fn get_or_insert_send(
+965    max_data: VarInt,
+966) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> {
+967    move |opt| opt.get_or_insert_with(|| Send::new(max_data))
+968}
+969
+970#[inline]
+971pub(super) fn get_or_insert_recv(
+972    initial_max_data: u64,
+973) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv {
+974    move |opt| {
+975        *opt = opt.take().map(|s| match s {
+976            StreamRecv::Free(recv) => StreamRecv::Open(recv),
+977            s => s,
+978        });
+979        opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data)))
+980            .as_open_recv_mut()
+981            .unwrap()
+982    }
+983}
+984
+985#[cfg(test)]
+986mod tests {
+987    use super::*;
+988    use crate::{
+989        ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError,
+990        connection::State as ConnState, connection::Streams,
+991    };
+992    use bytes::Bytes;
+993
+994    fn make(side: Side) -> StreamsState {
+995        StreamsState::new(
+996            side,
+997            128u32.into(),
+998            128u32.into(),
+999            1024 * 1024,
+1000            (1024 * 1024u32).into(),
+1001            (1024 * 1024u32).into(),
+1002        )
+1003    }
+1004
+1005    #[test]
+1006    fn trivial_flow_control() {
+1007        let mut client = StreamsState::new(
+1008            Side::Client,
+1009            1u32.into(),
+1010            1u32.into(),
+1011            1024 * 1024,
+1012            (1024 * 1024u32).into(),
+1013            (1024 * 1024u32).into(),
+1014        );
+1015        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1016        let initial_max = client.local_max_data;
+1017        const MESSAGE_SIZE: usize = 2048;
+1018        assert_eq!(
+1019            client
+1020                .received(
+1021                    frame::Stream {
+1022                        id,
+1023                        offset: 0,
+1024                        fin: true,
+1025                        data: Bytes::from_static(&[0; MESSAGE_SIZE]),
+1026                    },
+1027                    2048
+1028                )
+1029                .unwrap(),
+1030            ShouldTransmit(false)
+1031        );
+1032        assert_eq!(client.data_recvd, 2048);
+1033        assert_eq!(client.local_max_data - initial_max, 0);
+1034
+1035        let mut pending = Retransmits::default();
+1036        let mut recv = RecvStream {
+1037            id,
+1038            state: &mut client,
+1039            pending: &mut pending,
+1040        };
+1041
+1042        let mut chunks = recv.read(true).unwrap();
+1043        assert_eq!(
+1044            chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(),
+1045            MESSAGE_SIZE
+1046        );
+1047        assert!(chunks.next(0).unwrap().is_none());
+1048        let should_transmit = chunks.finalize();
+1049        assert!(should_transmit.0);
+1050        assert!(pending.max_stream_id[Dir::Uni as usize]);
+1051        assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64);
+1052    }
+1053
+1054    #[test]
+1055    fn reset_flow_control() {
+1056        let mut client = make(Side::Client);
+1057        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1058        let initial_max = client.local_max_data;
+1059        assert_eq!(
+1060            client
+1061                .received(
+1062                    frame::Stream {
+1063                        id,
+1064                        offset: 0,
+1065                        fin: false,
+1066                        data: Bytes::from_static(&[0; 2048]),
+1067                    },
+1068                    2048
+1069                )
+1070                .unwrap(),
+1071            ShouldTransmit(false)
+1072        );
+1073        assert_eq!(client.data_recvd, 2048);
+1074        assert_eq!(client.local_max_data - initial_max, 0);
+1075
+1076        let mut pending = Retransmits::default();
+1077        let mut recv = RecvStream {
+1078            id,
+1079            state: &mut client,
+1080            pending: &mut pending,
+1081        };
+1082
+1083        let mut chunks = recv.read(true).unwrap();
+1084        chunks.next(1024).unwrap();
+1085        let _ = chunks.finalize();
+1086        assert_eq!(client.local_max_data - initial_max, 1024);
+1087        assert_eq!(
+1088            client
+1089                .received_reset(frame::ResetStream {
+1090                    id,
+1091                    error_code: 0u32.into(),
+1092                    final_offset: 4096u32.into(),
+1093                })
+1094                .unwrap(),
+1095            ShouldTransmit(false)
+1096        );
+1097
+1098        assert_eq!(client.data_recvd, 4096);
+1099        assert_eq!(client.local_max_data - initial_max, 4096);
+1100
+1101        // Ensure reading after a reset doesn't issue redundant credit
+1102        let mut recv = RecvStream {
+1103            id,
+1104            state: &mut client,
+1105            pending: &mut pending,
+1106        };
+1107        let mut chunks = recv.read(true).unwrap();
+1108        assert_eq!(
+1109            chunks.next(1024).unwrap_err(),
+1110            crate::ReadError::Reset(0u32.into())
+1111        );
+1112        let _ = chunks.finalize();
+1113        assert_eq!(client.data_recvd, 4096);
+1114        assert_eq!(client.local_max_data - initial_max, 4096);
+1115    }
+1116
+1117    #[test]
+1118    fn reset_after_empty_frame_flow_control() {
+1119        let mut client = make(Side::Client);
+1120        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1121        let initial_max = client.local_max_data;
+1122        assert_eq!(
+1123            client
+1124                .received(
+1125                    frame::Stream {
+1126                        id,
+1127                        offset: 4096,
+1128                        fin: false,
+1129                        data: Bytes::from_static(&[0; 0]),
+1130                    },
+1131                    0
+1132                )
+1133                .unwrap(),
+1134            ShouldTransmit(false)
+1135        );
+1136        assert_eq!(client.data_recvd, 4096);
+1137        assert_eq!(client.local_max_data - initial_max, 0);
+1138        assert_eq!(
+1139            client
+1140                .received_reset(frame::ResetStream {
+1141                    id,
+1142                    error_code: 0u32.into(),
+1143                    final_offset: 4096u32.into(),
+1144                })
+1145                .unwrap(),
+1146            ShouldTransmit(false)
+1147        );
+1148        assert_eq!(client.data_recvd, 4096);
+1149        assert_eq!(client.local_max_data - initial_max, 4096);
+1150    }
+1151
+1152    #[test]
+1153    fn duplicate_reset_flow_control() {
+1154        let mut client = make(Side::Client);
+1155        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1156        assert_eq!(
+1157            client
+1158                .received_reset(frame::ResetStream {
+1159                    id,
+1160                    error_code: 0u32.into(),
+1161                    final_offset: 4096u32.into(),
+1162                })
+1163                .unwrap(),
+1164            ShouldTransmit(false)
+1165        );
+1166        assert_eq!(client.data_recvd, 4096);
+1167        assert_eq!(
+1168            client
+1169                .received_reset(frame::ResetStream {
+1170                    id,
+1171                    error_code: 0u32.into(),
+1172                    final_offset: 4096u32.into(),
+1173                })
+1174                .unwrap(),
+1175            ShouldTransmit(false)
+1176        );
+1177        assert_eq!(client.data_recvd, 4096);
+1178    }
+1179
+1180    #[test]
+1181    fn recv_stopped() {
+1182        let mut client = make(Side::Client);
+1183        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1184        let initial_max = client.local_max_data;
+1185        assert_eq!(
+1186            client
+1187                .received(
+1188                    frame::Stream {
+1189                        id,
+1190                        offset: 0,
+1191                        fin: false,
+1192                        data: Bytes::from_static(&[0; 32]),
+1193                    },
+1194                    32
+1195                )
+1196                .unwrap(),
+1197            ShouldTransmit(false)
+1198        );
+1199        assert_eq!(client.local_max_data, initial_max);
+1200
+1201        let mut pending = Retransmits::default();
+1202        let mut recv = RecvStream {
+1203            id,
+1204            state: &mut client,
+1205            pending: &mut pending,
+1206        };
+1207
+1208        recv.stop(0u32.into()).unwrap();
+1209        assert_eq!(recv.pending.stop_sending.len(), 1);
+1210        assert!(!recv.pending.max_data);
+1211
+1212        assert!(recv.stop(0u32.into()).is_err());
+1213        assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream));
+1214        assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream));
+1215
+1216        assert_eq!(client.local_max_data - initial_max, 32);
+1217        assert_eq!(
+1218            client
+1219                .received(
+1220                    frame::Stream {
+1221                        id,
+1222                        offset: 32,
+1223                        fin: true,
+1224                        data: Bytes::from_static(&[0; 16]),
+1225                    },
+1226                    16
+1227                )
+1228                .unwrap(),
+1229            ShouldTransmit(false)
+1230        );
+1231        assert_eq!(client.local_max_data - initial_max, 48);
+1232        assert!(!client.recv.contains_key(&id));
+1233    }
+1234
+1235    #[test]
+1236    fn stopped_reset() {
+1237        let mut client = make(Side::Client);
+1238        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1239        // Server opens stream
+1240        assert_eq!(
+1241            client
+1242                .received(
+1243                    frame::Stream {
+1244                        id,
+1245                        offset: 0,
+1246                        fin: false,
+1247                        data: Bytes::from_static(&[0; 32])
+1248                    },
+1249                    32
+1250                )
+1251                .unwrap(),
+1252            ShouldTransmit(false)
+1253        );
+1254
+1255        let mut pending = Retransmits::default();
+1256        let mut recv = RecvStream {
+1257            id,
+1258            state: &mut client,
+1259            pending: &mut pending,
+1260        };
+1261
+1262        recv.stop(0u32.into()).unwrap();
+1263        assert_eq!(pending.stop_sending.len(), 1);
+1264        assert!(!pending.max_data);
+1265
+1266        // Server complies
+1267        let prev_max = client.max_remote[Dir::Uni as usize];
+1268        assert_eq!(
+1269            client
+1270                .received_reset(frame::ResetStream {
+1271                    id,
+1272                    error_code: 0u32.into(),
+1273                    final_offset: 32u32.into(),
+1274                })
+1275                .unwrap(),
+1276            ShouldTransmit(false)
+1277        );
+1278        assert!(!client.recv.contains_key(&id), "stream state is freed");
+1279        assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1);
+1280    }
+1281
+1282    #[test]
+1283    fn send_stopped() {
+1284        let mut server = make(Side::Server);
+1285        server.set_params(&TransportParameters {
+1286            initial_max_streams_uni: 1u32.into(),
+1287            initial_max_data: 42u32.into(),
+1288            initial_max_stream_data_uni: 42u32.into(),
+1289            ..TransportParameters::default()
+1290        });
+1291
+1292        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1293        let id = Streams {
+1294            state: &mut server,
+1295            conn_state: &state,
+1296        }
+1297        .open(Dir::Uni)
+1298        .unwrap();
+1299
+1300        let mut stream = SendStream {
+1301            id,
+1302            state: &mut server,
+1303            pending: &mut pending,
+1304            conn_state: &state,
+1305        };
+1306
+1307        let error_code = 0u32.into();
+1308        stream.state.received_stop_sending(id, error_code);
+1309        assert!(
+1310            stream
+1311                .state
+1312                .events
+1313                .contains(&StreamEvent::Stopped { id, error_code })
+1314        );
+1315        stream.state.events.clear();
+1316
+1317        assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code)));
+1318
+1319        stream.reset(0u32.into()).unwrap();
+1320        assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream));
+1321
+1322        // A duplicate frame is a no-op
+1323        stream.state.received_stop_sending(id, error_code);
+1324        assert!(stream.state.events.is_empty());
+1325    }
+1326
+1327    #[test]
+1328    fn final_offset_flow_control() {
+1329        let mut client = make(Side::Client);
+1330        assert_eq!(
+1331            client
+1332                .received_reset(frame::ResetStream {
+1333                    id: StreamId::new(Side::Server, Dir::Uni, 0),
+1334                    error_code: 0u32.into(),
+1335                    final_offset: VarInt::MAX,
+1336                })
+1337                .unwrap_err()
+1338                .code,
+1339            TransportErrorCode::FLOW_CONTROL_ERROR
+1340        );
+1341    }
+1342
+1343    #[test]
+1344    fn stream_priority() {
+1345        let mut server = make(Side::Server);
+1346        server.set_params(&TransportParameters {
+1347            initial_max_streams_bidi: 3u32.into(),
+1348            initial_max_data: 10u32.into(),
+1349            initial_max_stream_data_bidi_remote: 10u32.into(),
+1350            ..TransportParameters::default()
+1351        });
+1352
+1353        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1354        let mut streams = Streams {
+1355            state: &mut server,
+1356            conn_state: &state,
+1357        };
+1358
+1359        let id_high = streams.open(Dir::Bi).unwrap();
+1360        let id_mid = streams.open(Dir::Bi).unwrap();
+1361        let id_low = streams.open(Dir::Bi).unwrap();
+1362
+1363        let mut mid = SendStream {
+1364            id: id_mid,
+1365            state: &mut server,
+1366            pending: &mut pending,
+1367            conn_state: &state,
+1368        };
+1369        mid.write(b"mid").unwrap();
+1370
+1371        let mut low = SendStream {
+1372            id: id_low,
+1373            state: &mut server,
+1374            pending: &mut pending,
+1375            conn_state: &state,
+1376        };
+1377        low.set_priority(-1).unwrap();
+1378        low.write(b"low").unwrap();
+1379
+1380        let mut high = SendStream {
+1381            id: id_high,
+1382            state: &mut server,
+1383            pending: &mut pending,
+1384            conn_state: &state,
+1385        };
+1386        high.set_priority(1).unwrap();
+1387        high.write(b"high").unwrap();
+1388
+1389        let mut buf = Vec::with_capacity(40);
+1390        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1391        assert_eq!(meta[0].id, id_high);
+1392        assert_eq!(meta[1].id, id_mid);
+1393        assert_eq!(meta[2].id, id_low);
+1394
+1395        assert!(!server.can_send_stream_data());
+1396        assert_eq!(server.pending.len(), 0);
+1397    }
+1398
+1399    #[test]
+1400    fn requeue_stream_priority() {
+1401        let mut server = make(Side::Server);
+1402        server.set_params(&TransportParameters {
+1403            initial_max_streams_bidi: 3u32.into(),
+1404            initial_max_data: 1000u32.into(),
+1405            initial_max_stream_data_bidi_remote: 1000u32.into(),
+1406            ..TransportParameters::default()
+1407        });
+1408
+1409        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1410        let mut streams = Streams {
+1411            state: &mut server,
+1412            conn_state: &state,
+1413        };
+1414
+1415        let id_high = streams.open(Dir::Bi).unwrap();
+1416        let id_mid = streams.open(Dir::Bi).unwrap();
+1417
+1418        let mut mid = SendStream {
+1419            id: id_mid,
+1420            state: &mut server,
+1421            pending: &mut pending,
+1422            conn_state: &state,
+1423        };
+1424        assert_eq!(mid.write(b"mid").unwrap(), 3);
+1425        assert_eq!(server.pending.len(), 1);
+1426
+1427        let mut high = SendStream {
+1428            id: id_high,
+1429            state: &mut server,
+1430            pending: &mut pending,
+1431            conn_state: &state,
+1432        };
+1433        high.set_priority(1).unwrap();
+1434        assert_eq!(high.write(&[0; 200]).unwrap(), 200);
+1435        assert_eq!(server.pending.len(), 2);
+1436
+1437        // Requeue the high priority stream to lowest priority. The initial send
+1438        // still uses high priority since it's queued that way. After that it will
+1439        // switch to low priority
+1440        let mut high = SendStream {
+1441            id: id_high,
+1442            state: &mut server,
+1443            pending: &mut pending,
+1444            conn_state: &state,
+1445        };
+1446        high.set_priority(-1).unwrap();
+1447
+1448        let mut buf = Vec::with_capacity(1000).limit(40);
+1449        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1450        assert_eq!(meta.len(), 1);
+1451        assert_eq!(meta[0].id, id_high);
+1452
+1453        // After requeuing we should end up with 2 priorities - not 3
+1454        assert_eq!(server.pending.len(), 2);
+1455
+1456        let mut buf = buf.into_inner();
+1457
+1458        // Send the remaining data. The initial mid priority one should go first now
+1459        let meta = server.write_stream_frames(&mut buf, true, &mut Default::default());
+1460        assert_eq!(meta.len(), 2);
+1461        assert_eq!(meta[0].id, id_mid);
+1462        assert_eq!(meta[1].id, id_high);
+1463
+1464        assert!(!server.can_send_stream_data());
+1465        assert_eq!(server.pending.len(), 0);
+1466    }
+1467
+1468    #[test]
+1469    fn same_stream_priority() {
+1470        for fair in [true, false] {
+1471            let mut server = make(Side::Server);
+1472            server.set_params(&TransportParameters {
+1473                initial_max_streams_bidi: 3u32.into(),
+1474                initial_max_data: 300u32.into(),
+1475                initial_max_stream_data_bidi_remote: 300u32.into(),
+1476                ..TransportParameters::default()
+1477            });
+1478
+1479            let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1480            let mut streams = Streams {
+1481                state: &mut server,
+1482                conn_state: &state,
+1483            };
+1484
+1485            // a, b and c all have the same priority
+1486            let id_a = streams.open(Dir::Bi).unwrap();
+1487            let id_b = streams.open(Dir::Bi).unwrap();
+1488            let id_c = streams.open(Dir::Bi).unwrap();
+1489
+1490            let mut stream_a = SendStream {
+1491                id: id_a,
+1492                state: &mut server,
+1493                pending: &mut pending,
+1494                conn_state: &state,
+1495            };
+1496            stream_a.write(&[b'a'; 100]).unwrap();
+1497
+1498            let mut stream_b = SendStream {
+1499                id: id_b,
+1500                state: &mut server,
+1501                pending: &mut pending,
+1502                conn_state: &state,
+1503            };
+1504            stream_b.write(&[b'b'; 100]).unwrap();
+1505
+1506            let mut stream_c = SendStream {
+1507                id: id_c,
+1508                state: &mut server,
+1509                pending: &mut pending,
+1510                conn_state: &state,
+1511            };
+1512            stream_c.write(&[b'c'; 100]).unwrap();
+1513
+1514            let mut metas = vec![];
+1515            let mut buf = Vec::with_capacity(1024);
+1516
+1517            // loop until all the streams are written
+1518            loop {
+1519                let mut chunk_buf = buf.limit(40);
+1520                let meta =
+1521                    server.write_stream_frames(&mut chunk_buf, fair, &mut Default::default());
+1522                if meta.is_empty() {
+1523                    break;
+1524                }
+1525                metas.extend(meta);
+1526                buf = chunk_buf.into_inner();
+1527            }
+1528
+1529            assert!(!server.can_send_stream_data());
+1530            assert_eq!(server.pending.len(), 0);
+1531
+1532            let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1533            if fair {
+1534                // When fairness is enabled, if we run out of buffer space to write out a stream,
+1535                // the stream is re-queued after all the streams with the same priority.
+1536                assert_eq!(
+1537                    stream_ids,
+1538                    vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c]
+1539                );
+1540            } else {
+1541                // When fairness is disabled the stream is re-queued before all the other streams
+1542                // with the same priority.
+1543                assert_eq!(
+1544                    stream_ids,
+1545                    vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c]
+1546                );
+1547            }
+1548        }
+1549    }
+1550
+1551    #[test]
+1552    fn unfair_priority_bump() {
+1553        let mut server = make(Side::Server);
+1554        server.set_params(&TransportParameters {
+1555            initial_max_streams_bidi: 3u32.into(),
+1556            initial_max_data: 300u32.into(),
+1557            initial_max_stream_data_bidi_remote: 300u32.into(),
+1558            ..TransportParameters::default()
+1559        });
+1560
+1561        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1562        let mut streams = Streams {
+1563            state: &mut server,
+1564            conn_state: &state,
+1565        };
+1566
+1567        // a, and b have the same priority, c has higher priority
+1568        let id_a = streams.open(Dir::Bi).unwrap();
+1569        let id_b = streams.open(Dir::Bi).unwrap();
+1570        let id_c = streams.open(Dir::Bi).unwrap();
+1571
+1572        let mut stream_a = SendStream {
+1573            id: id_a,
+1574            state: &mut server,
+1575            pending: &mut pending,
+1576            conn_state: &state,
+1577        };
+1578        stream_a.write(&[b'a'; 100]).unwrap();
+1579
+1580        let mut stream_b = SendStream {
+1581            id: id_b,
+1582            state: &mut server,
+1583            pending: &mut pending,
+1584            conn_state: &state,
+1585        };
+1586        stream_b.write(&[b'b'; 100]).unwrap();
+1587
+1588        let mut metas = vec![];
+1589        let buf = Vec::with_capacity(1024);
+1590
+1591        // Write the first chunk of stream_a
+1592        let mut chunk_buf = buf.limit(40);
+1593        let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default());
+1594        let mut buf = chunk_buf.into_inner();
+1595        assert!(!meta.is_empty());
+1596        metas.extend(meta);
+1597
+1598        // Queue stream_c which has higher priority
+1599        let mut stream_c = SendStream {
+1600            id: id_c,
+1601            state: &mut server,
+1602            pending: &mut pending,
+1603            conn_state: &state,
+1604        };
+1605        stream_c.set_priority(1).unwrap();
+1606        stream_c.write(&[b'b'; 100]).unwrap();
+1607
+1608        // loop until all the streams are written
+1609        loop {
+1610            let mut chunk_buf = buf.limit(40);
+1611            let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default());
+1612            buf = chunk_buf.into_inner();
+1613            if meta.is_empty() {
+1614                break;
+1615            }
+1616            metas.extend(meta);
+1617        }
+1618
+1619        assert!(!server.can_send_stream_data());
+1620        assert_eq!(server.pending.len(), 0);
+1621
+1622        let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>();
+1623        assert_eq!(
+1624            stream_ids,
+1625            // stream_c bumps stream_b but doesn't bump stream_a which had already been partly
+1626            // written out
+1627            vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b]
+1628        );
+1629    }
+1630
+1631    #[test]
+1632    fn stop_finished() {
+1633        let mut client = make(Side::Client);
+1634        let id = StreamId::new(Side::Server, Dir::Uni, 0);
+1635        // Server finishes stream
+1636        let _ = client
+1637            .received(
+1638                frame::Stream {
+1639                    id,
+1640                    offset: 0,
+1641                    fin: true,
+1642                    data: Bytes::from_static(&[0; 32]),
+1643                },
+1644                32,
+1645            )
+1646            .unwrap();
+1647        let mut pending = Retransmits::default();
+1648        let mut stream = RecvStream {
+1649            id,
+1650            state: &mut client,
+1651            pending: &mut pending,
+1652        };
+1653        stream.stop(0u32.into()).unwrap();
+1654        assert!(client.recv.get_mut(&id).is_none(), "stream is freed");
+1655    }
+1656
+1657    // Verify that a stream that's been reset doesn't cause the appearance of pending data
+1658    #[test]
+1659    fn reset_stream_cannot_send() {
+1660        let mut server = make(Side::Server);
+1661        server.set_params(&TransportParameters {
+1662            initial_max_streams_uni: 1u32.into(),
+1663            initial_max_data: 42u32.into(),
+1664            initial_max_stream_data_uni: 42u32.into(),
+1665            ..TransportParameters::default()
+1666        });
+1667        let (mut pending, state) = (Retransmits::default(), ConnState::established());
+1668        let mut streams = Streams {
+1669            state: &mut server,
+1670            conn_state: &state,
+1671        };
+1672
+1673        let id = streams.open(Dir::Uni).unwrap();
+1674        let mut stream = SendStream {
+1675            id,
+1676            state: &mut server,
+1677            pending: &mut pending,
+1678            conn_state: &state,
+1679        };
+1680        stream.write(b"hello").unwrap();
+1681        stream.reset(0u32.into()).unwrap();
+1682
+1683        assert_eq!(pending.reset_stream, &[(id, 0u32.into())]);
+1684        assert!(!server.can_send_stream_data());
+1685    }
+1686
+1687    #[test]
+1688    fn stream_limit_fixed() {
+1689        let mut client = make(Side::Client);
+1690        // Open streams 0-127
+1691        assert_eq!(
+1692            client.received(
+1693                frame::Stream {
+1694                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1695                    offset: 0,
+1696                    fin: true,
+1697                    data: Bytes::from_static(&[]),
+1698                },
+1699                0
+1700            ),
+1701            Ok(ShouldTransmit(false))
+1702        );
+1703        // Try to open stream 128, exceeding limit
+1704        assert_eq!(
+1705            client
+1706                .received(
+1707                    frame::Stream {
+1708                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1709                        offset: 0,
+1710                        fin: true,
+1711                        data: Bytes::from_static(&[]),
+1712                    },
+1713                    0
+1714                )
+1715                .unwrap_err()
+1716                .code,
+1717            TransportErrorCode::STREAM_LIMIT_ERROR
+1718        );
+1719
+1720        // Free stream 127
+1721        let mut pending = Retransmits::default();
+1722        let mut stream = RecvStream {
+1723            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1724            state: &mut client,
+1725            pending: &mut pending,
+1726        };
+1727        stream.stop(0u32.into()).unwrap();
+1728
+1729        // Open stream 128
+1730        assert_eq!(
+1731            client.received(
+1732                frame::Stream {
+1733                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1734                    offset: 0,
+1735                    fin: true,
+1736                    data: Bytes::from_static(&[]),
+1737                },
+1738                0
+1739            ),
+1740            Ok(ShouldTransmit(false))
+1741        );
+1742    }
+1743
+1744    #[test]
+1745    fn stream_limit_grows() {
+1746        let mut client = make(Side::Client);
+1747        // Open streams 0-127
+1748        assert_eq!(
+1749            client.received(
+1750                frame::Stream {
+1751                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1752                    offset: 0,
+1753                    fin: true,
+1754                    data: Bytes::from_static(&[]),
+1755                },
+1756                0
+1757            ),
+1758            Ok(ShouldTransmit(false))
+1759        );
+1760        // Try to open stream 128, exceeding limit
+1761        assert_eq!(
+1762            client
+1763                .received(
+1764                    frame::Stream {
+1765                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1766                        offset: 0,
+1767                        fin: true,
+1768                        data: Bytes::from_static(&[]),
+1769                    },
+1770                    0
+1771                )
+1772                .unwrap_err()
+1773                .code,
+1774            TransportErrorCode::STREAM_LIMIT_ERROR
+1775        );
+1776
+1777        // Relax limit by one
+1778        client.set_max_concurrent(Dir::Uni, 129u32.into());
+1779
+1780        // Open stream 128
+1781        assert_eq!(
+1782            client.received(
+1783                frame::Stream {
+1784                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1785                    offset: 0,
+1786                    fin: true,
+1787                    data: Bytes::from_static(&[]),
+1788                },
+1789                0
+1790            ),
+1791            Ok(ShouldTransmit(false))
+1792        );
+1793    }
+1794
+1795    #[test]
+1796    fn stream_limit_shrinks() {
+1797        let mut client = make(Side::Client);
+1798        // Open streams 0-127
+1799        assert_eq!(
+1800            client.received(
+1801                frame::Stream {
+1802                    id: StreamId::new(Side::Server, Dir::Uni, 127),
+1803                    offset: 0,
+1804                    fin: true,
+1805                    data: Bytes::from_static(&[]),
+1806                },
+1807                0
+1808            ),
+1809            Ok(ShouldTransmit(false))
+1810        );
+1811
+1812        // Tighten limit by one
+1813        client.set_max_concurrent(Dir::Uni, 127u32.into());
+1814
+1815        // Free stream 127
+1816        let mut pending = Retransmits::default();
+1817        let mut stream = RecvStream {
+1818            id: StreamId::new(Side::Server, Dir::Uni, 127),
+1819            state: &mut client,
+1820            pending: &mut pending,
+1821        };
+1822        stream.stop(0u32.into()).unwrap();
+1823
+1824        // Try to open stream 128, still exceeding limit
+1825        assert_eq!(
+1826            client
+1827                .received(
+1828                    frame::Stream {
+1829                        id: StreamId::new(Side::Server, Dir::Uni, 128),
+1830                        offset: 0,
+1831                        fin: true,
+1832                        data: Bytes::from_static(&[]),
+1833                    },
+1834                    0
+1835                )
+1836                .unwrap_err()
+1837                .code,
+1838            TransportErrorCode::STREAM_LIMIT_ERROR
+1839        );
+1840
+1841        // Free stream 126
+1842        assert_eq!(
+1843            client.received_reset(frame::ResetStream {
+1844                id: StreamId::new(Side::Server, Dir::Uni, 126),
+1845                error_code: 0u32.into(),
+1846                final_offset: 0u32.into(),
+1847            }),
+1848            Ok(ShouldTransmit(false))
+1849        );
+1850        let mut pending = Retransmits::default();
+1851        let mut stream = RecvStream {
+1852            id: StreamId::new(Side::Server, Dir::Uni, 126),
+1853            state: &mut client,
+1854            pending: &mut pending,
+1855        };
+1856        stream.stop(0u32.into()).unwrap();
+1857
+1858        // Open stream 128
+1859        assert_eq!(
+1860            client.received(
+1861                frame::Stream {
+1862                    id: StreamId::new(Side::Server, Dir::Uni, 128),
+1863                    offset: 0,
+1864                    fin: true,
+1865                    data: Bytes::from_static(&[]),
+1866                },
+1867                0
+1868            ),
+1869            Ok(ShouldTransmit(false))
+1870        );
+1871    }
+1872
+1873    #[test]
+1874    fn remote_stream_capacity() {
+1875        let mut client = make(Side::Client);
+1876        for _ in 0..2 {
+1877            client.set_max_concurrent(Dir::Uni, 200u32.into());
+1878            client.set_max_concurrent(Dir::Bi, 201u32.into());
+1879            assert_eq!(client.recv.len(), 200 + 201);
+1880            assert_eq!(client.max_remote[Dir::Uni as usize], 200);
+1881            assert_eq!(client.max_remote[Dir::Bi as usize], 201);
+1882        }
+1883    }
+1884
+1885    #[test]
+1886    fn expand_receive_window() {
+1887        let mut server = make(Side::Server);
+1888        let new_receive_window = 2 * server.receive_window as u32;
+1889        let expanded = server.set_receive_window(new_receive_window.into());
+1890        assert!(expanded);
+1891        assert_eq!(server.receive_window, new_receive_window as u64);
+1892        assert_eq!(server.local_max_data, new_receive_window as u64);
+1893        assert_eq!(server.receive_window_shrink_debt, 0);
+1894        let prev_local_max_data = server.local_max_data;
+1895
+1896        // credit, expecting all of them added to local_max_data
+1897        let credits = 1024u64;
+1898        let should_transmit = server.add_read_credits(credits);
+1899        assert_eq!(server.receive_window_shrink_debt, 0);
+1900        assert_eq!(server.local_max_data, prev_local_max_data + credits);
+1901        assert!(should_transmit.should_transmit());
+1902    }
+1903
+1904    #[test]
+1905    fn shrink_receive_window() {
+1906        let mut server = make(Side::Server);
+1907        let new_receive_window = server.receive_window as u32 / 2;
+1908        let prev_local_max_data = server.local_max_data;
+1909
+1910        // shrink the receive_winbow, local_max_data is not expected to be changed
+1911        let shrink_diff = server.receive_window - new_receive_window as u64;
+1912        let expanded = server.set_receive_window(new_receive_window.into());
+1913        assert!(!expanded);
+1914        assert_eq!(server.receive_window, new_receive_window as u64);
+1915        assert_eq!(server.local_max_data, prev_local_max_data);
+1916        assert_eq!(server.receive_window_shrink_debt, shrink_diff);
+1917        let prev_local_max_data = server.local_max_data;
+1918
+1919        // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt
+1920        let credits = 1024u64;
+1921        for _ in 0..2 {
+1922            let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits;
+1923            let should_transmit = server.add_read_credits(credits);
+1924            assert_eq!(
+1925                server.receive_window_shrink_debt,
+1926                expected_receive_window_shrink_debt
+1927            );
+1928            assert_eq!(server.local_max_data, prev_local_max_data);
+1929            assert!(!should_transmit.should_transmit());
+1930        }
+1931
+1932        // credit again which exceeds all remaining expected_receive_window_shrink_debt
+1933        let credits = 1024 * 512;
+1934        let prev_local_max_data = server.local_max_data;
+1935        let expected_local_max_data =
+1936            server.local_max_data + (credits - server.receive_window_shrink_debt);
+1937        let _should_transmit = server.add_read_credits(credits);
+1938        assert_eq!(server.receive_window_shrink_debt, 0);
+1939        assert_eq!(server.local_max_data, expected_local_max_data);
+1940        assert!(server.local_max_data > prev_local_max_data);
+1941
+1942        // credit again, all should be added to local_max_data
+1943        let credits = 1024 * 512;
+1944        let expected_local_max_data = server.local_max_data + credits;
+1945        let should_transmit = server.add_read_credits(credits);
+1946        assert_eq!(server.receive_window_shrink_debt, 0);
+1947        assert_eq!(server.local_max_data, expected_local_max_data);
+1948        assert!(should_transmit.should_transmit());
+1949    }
+1950
+1951    #[test]
+1952    fn expand_send_window() {
+1953        let mut server = make(Side::Server);
+1954
+1955        let initial_send_window = server.send_window;
+1956        let larger_send_window = initial_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        // Check that the stream accepts `initial_send_window` bytes
+1987        let initial_send_len = initial_send_window as usize;
+1988        let data = vec![0xFFu8; initial_send_len];
+1989
+1990        assert_eq!(stream.write(&data), Ok(initial_send_len));
+1991
+1992        // Try to write the same data again, observe that it's blocked
+1993        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+1994
+1995        // Check that we get a `Writable` event after increasing the send window
+1996        stream.state.set_send_window(larger_send_window);
+1997        assert_eq!(
+1998            stream.state.poll(),
+1999            Some(StreamEvent::Writable { id: stream_id })
+2000        );
+2001
+2002        // Check that the stream accepts the exact same amount of data again
+2003        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2004        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2005
+2006        assert_eq!(stream.state.poll(), None);
+2007
+2008        // Ack the data
+2009        stream.state.received_ack_of(frame::StreamMeta {
+2010            id: stream_id,
+2011            offsets: 0..larger_send_window,
+2012            fin: false,
+2013        });
+2014
+2015        assert_eq!(
+2016            stream.state.poll(),
+2017            Some(StreamEvent::Writable { id: stream_id })
+2018        );
+2019
+2020        // Check that our full send window is available again
+2021        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2022        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2023        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2024    }
+2025
+2026    #[test]
+2027    fn shrink_send_window() {
+2028        let mut server = make(Side::Server);
+2029
+2030        let initial_send_window = server.send_window;
+2031        let smaller_send_window = server.send_window / 2;
+2032
+2033        // Set `initial_max_data` larger than `send_window` so we're limited by local flow control
+2034        server.set_params(&TransportParameters {
+2035            initial_max_data: VarInt::MAX,
+2036            initial_max_stream_data_uni: VarInt::MAX,
+2037            initial_max_streams_uni: VarInt::from_u32(100),
+2038            ..TransportParameters::default()
+2039        });
+2040
+2041        assert_eq!(server.write_limit(), initial_send_window);
+2042        assert_eq!(server.poll(), None);
+2043
+2044        let mut retransmits = Retransmits::default();
+2045        let conn_state = ConnState::established();
+2046
+2047        let stream_id = Streams {
+2048            state: &mut server,
+2049            conn_state: &conn_state,
+2050        }
+2051        .open(Dir::Uni)
+2052        .expect("should be able to open a stream");
+2053
+2054        let mut stream = SendStream {
+2055            id: stream_id,
+2056            state: &mut server,
+2057            pending: &mut retransmits,
+2058            conn_state: &conn_state,
+2059        };
+2060
+2061        let initial_send_len = initial_send_window as usize;
+2062
+2063        let data = vec![0xFFu8; initial_send_len];
+2064
+2065        // Assert that the full send window is accepted
+2066        assert_eq!(stream.write(&data), Ok(initial_send_len));
+2067        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2068
+2069        assert_eq!(stream.state.write_limit(), 0);
+2070        assert_eq!(stream.state.poll(), None);
+2071
+2072        // Shrink our send window, assert that it's still not writable
+2073        stream.state.set_send_window(smaller_send_window);
+2074        assert_eq!(stream.state.write_limit(), 0);
+2075        assert_eq!(stream.state.poll(), None);
+2076
+2077        // Assert that data is still not accepted
+2078        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2079
+2080        // Ack some data, assert that writes are still not accepted due to outstanding sends
+2081        stream.state.received_ack_of(frame::StreamMeta {
+2082            id: stream_id,
+2083            offsets: 0..smaller_send_window,
+2084            fin: false,
+2085        });
+2086
+2087        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2088
+2089        // Ack the rest of the data
+2090        stream.state.received_ack_of(frame::StreamMeta {
+2091            id: stream_id,
+2092            offsets: smaller_send_window..initial_send_window,
+2093            fin: false,
+2094        });
+2095
+2096        // This should generate a `Writable` event
+2097        assert_eq!(
+2098            stream.state.poll(),
+2099            Some(StreamEvent::Writable { id: stream_id })
+2100        );
+2101        assert_eq!(stream.state.write_limit(), smaller_send_window);
+2102
+2103        // Assert that only `smaller_send_window` bytes are accepted
+2104        assert_eq!(stream.write(&data), Ok(smaller_send_window as usize));
+2105        assert_eq!(stream.write(&data), Err(WriteError::Blocked));
+2106    }
+2107}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/timer.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/timer.rs.html new file mode 100644 index 000000000..dda686a6b --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/timer.rs.html @@ -0,0 +1,484 @@ +timer.rs - source

iroh_quinn_proto/connection/
timer.rs

1use identity_hash::{IdentityHashable, IntMap};
+2
+3use crate::{
+4    Instant,
+5    connection::qlog::{QlogSink, QlogSinkWithTime},
+6};
+7
+8use super::PathId;
+9
+10#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+11pub(crate) enum Timer {
+12    /// Per connection timers.
+13    Conn(ConnTimer),
+14    /// Per path timers.
+15    PerPath(PathId, PathTimer),
+16}
+17
+18#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+19pub(crate) enum ConnTimer {
+20    /// When to close the connection after no activity
+21    Idle = 0,
+22    /// When the close timer expires, the connection has been gracefully terminated.
+23    Close = 1,
+24    /// When keys are discarded because they should not be needed anymore
+25    KeyDiscard = 2,
+26    /// When to send a `PING` frame to keep the connection alive
+27    KeepAlive = 3,
+28    /// When to invalidate old CID and proactively push new one via NEW_CONNECTION_ID frame
+29    PushNewCid = 4,
+30}
+31
+32impl ConnTimer {
+33    const VALUES: [Self; 5] = [
+34        Self::Idle,
+35        Self::Close,
+36        Self::KeyDiscard,
+37        Self::KeepAlive,
+38        Self::PushNewCid,
+39    ];
+40}
+41
+42#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
+43pub(crate) enum PathTimer {
+44    /// When to send an ack-eliciting probe packet or declare unacked packets lost
+45    LossDetection = 0,
+46    /// When to abandon a path after no activity
+47    PathIdle = 1,
+48    /// When to give up on validating a new path from RFC9000 migration
+49    PathValidation = 2,
+50    /// When to resend a path challenge deemed lost
+51    PathChallengeLost = 3,
+52    /// When to give up on validating a new (multi)path
+53    PathOpen = 4,
+54    /// When to send a `PING` frame to keep the path alive
+55    PathKeepAlive = 5,
+56    /// When pacing will allow us to send a packet
+57    Pacing = 6,
+58    /// When to send an immediate ACK if there are unacked ack-eliciting packets of the peer
+59    MaxAckDelay = 7,
+60    /// When to clean up state for an abandoned path
+61    DiscardPath = 8,
+62}
+63
+64impl PathTimer {
+65    const VALUES: [Self; 9] = [
+66        Self::LossDetection,
+67        Self::PathIdle,
+68        Self::PathValidation,
+69        Self::PathChallengeLost,
+70        Self::PathOpen,
+71        Self::PathKeepAlive,
+72        Self::Pacing,
+73        Self::MaxAckDelay,
+74        Self::DiscardPath,
+75    ];
+76}
+77
+78/// Keeps track of the nearest timeout for each `Timer`
+79///
+80/// The [`TimerTable`] is advanced with [`TimerTable::expire_before`].
+81#[derive(Debug, Clone, Default)]
+82pub(crate) struct TimerTable {
+83    generic: [Option<Instant>; ConnTimer::VALUES.len()],
+84    path_timers: SmallMap<PathId, PathTimerTable, STACK_TIMERS>,
+85}
+86
+87/// For how many paths we keep the timers on the stack, before spilling onto the heap.
+88const STACK_TIMERS: usize = 4;
+89
+90/// Works like a `HashMap` but stores up to `SIZE` items on the stack.
+91#[derive(Debug, Clone)]
+92struct SmallMap<K, V, const SIZE: usize> {
+93    stack: [Option<(K, V)>; SIZE],
+94    heap: Option<IntMap<K, V>>,
+95}
+96
+97impl<K, V, const SIZE: usize> Default for SmallMap<K, V, SIZE> {
+98    fn default() -> Self {
+99        Self {
+100            stack: [const { None }; SIZE],
+101            heap: None,
+102        }
+103    }
+104}
+105
+106impl<K, V, const SIZE: usize> SmallMap<K, V, SIZE>
+107where
+108    K: std::cmp::Eq + std::hash::Hash + IdentityHashable,
+109{
+110    fn insert(&mut self, key: K, value: V) -> Option<V> {
+111        // check stack for space
+112        for el in self.stack.iter_mut() {
+113            match el {
+114                Some((k, v)) => {
+115                    if *k == key {
+116                        let old_value = std::mem::replace(v, value);
+117                        return Some(old_value);
+118                    }
+119                }
+120                None => {
+121                    // make sure to remove a potentially old value from the heap
+122                    let old_heap = self.heap.as_mut().and_then(|h| h.remove(&key));
+123                    *el = Some((key, value));
+124
+125                    return old_heap;
+126                }
+127            }
+128        }
+129
+130        // No space on the stack, use the heap
+131        let heap = self.heap.get_or_insert_default();
+132        heap.insert(key, value)
+133    }
+134
+135    #[cfg(test)]
+136    fn remove(&mut self, key: &K) -> Option<V> {
+137        for el in self.stack.iter_mut() {
+138            if let Some((k, _)) = el {
+139                if key == k {
+140                    return el.take().map(|(_, v)| v);
+141                }
+142            }
+143        }
+144
+145        self.heap.as_mut().and_then(|h| h.remove(key))
+146    }
+147
+148    #[cfg(test)]
+149    fn get(&self, key: &K) -> Option<&V> {
+150        for (k, v) in self.stack.iter().filter_map(|v| v.as_ref()) {
+151            if k == key {
+152                return Some(v);
+153            }
+154        }
+155
+156        self.heap.as_ref().and_then(|h| h.get(key))
+157    }
+158
+159    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
+160        for (k, v) in self.stack.iter_mut().filter_map(|v| v.as_mut()) {
+161            if k == key {
+162                return Some(v);
+163            }
+164        }
+165
+166        self.heap.as_mut().and_then(|h| h.get_mut(key))
+167    }
+168
+169    #[cfg(test)]
+170    fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
+171        let a = self
+172            .stack
+173            .iter()
+174            .filter_map(|v| v.as_ref().map(|(k, v)| (k, v)));
+175        let b = self.heap.iter().flat_map(|h| h.iter());
+176        a.chain(b)
+177    }
+178
+179    fn values(&self) -> impl Iterator<Item = &V> {
+180        let a = self.stack.iter().filter_map(|v| v.as_ref().map(|(_, v)| v));
+181        let b = self.heap.iter().flat_map(|h| h.values());
+182        a.chain(b)
+183    }
+184
+185    fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut V)> {
+186        let a = self
+187            .stack
+188            .iter_mut()
+189            .filter_map(|v| v.as_mut().map(|(k, v)| (&*k, v)));
+190        let b = self.heap.iter_mut().flat_map(|h| h.iter_mut());
+191        a.chain(b)
+192    }
+193
+194    fn retain<F>(&mut self, mut f: F)
+195    where
+196        F: FnMut(&K, &mut V) -> bool,
+197    {
+198        let mut to_remove = [false; SIZE];
+199        for (i, el) in self.stack.iter_mut().enumerate() {
+200            if let Some((key, value)) = el {
+201                to_remove[i] = !f(key, value);
+202            }
+203        }
+204        for (i, to_remove) in to_remove.into_iter().enumerate() {
+205            if to_remove {
+206                self.stack[i] = None;
+207            }
+208        }
+209
+210        if let Some(ref mut heap) = self.heap {
+211            heap.retain(f);
+212        }
+213    }
+214
+215    fn clear(&mut self) {
+216        for el in self.stack.iter_mut() {
+217            *el = None;
+218        }
+219        self.heap = None;
+220    }
+221}
+222
+223#[derive(Debug, Clone, Copy, Default)]
+224struct PathTimerTable {
+225    timers: [Option<Instant>; PathTimer::VALUES.len()],
+226}
+227
+228impl PathTimerTable {
+229    fn set(&mut self, timer: PathTimer, time: Instant) {
+230        self.timers[timer as usize] = Some(time);
+231    }
+232
+233    fn stop(&mut self, timer: PathTimer) {
+234        self.timers[timer as usize] = None;
+235    }
+236
+237    /// Remove the next timer up until `now`, including it
+238    fn expire_before(&mut self, now: Instant) -> Option<(PathTimer, Instant)> {
+239        for timer in PathTimer::VALUES {
+240            if self.timers[timer as usize].is_some()
+241                && self.timers[timer as usize].expect("checked") <= now
+242            {
+243                return self.timers[timer as usize].take().map(|time| (timer, time));
+244            }
+245        }
+246
+247        None
+248    }
+249}
+250
+251impl TimerTable {
+252    /// Sets the timer unconditionally
+253    pub(super) fn set(&mut self, timer: Timer, time: Instant, qlog: QlogSinkWithTime<'_>) {
+254        match timer {
+255            Timer::Conn(timer) => {
+256                self.generic[timer as usize] = Some(time);
+257            }
+258            Timer::PerPath(path_id, timer) => match self.path_timers.get_mut(&path_id) {
+259                None => {
+260                    let mut table = PathTimerTable::default();
+261                    table.set(timer, time);
+262                    self.path_timers.insert(path_id, table);
+263                }
+264                Some(table) => {
+265                    table.set(timer, time);
+266                }
+267            },
+268        }
+269        qlog.emit_timer_set(timer, time);
+270    }
+271
+272    pub(super) fn stop(&mut self, timer: Timer, qlog: QlogSinkWithTime<'_>) {
+273        match timer {
+274            Timer::Conn(timer) => {
+275                self.generic[timer as usize] = None;
+276            }
+277            Timer::PerPath(path_id, timer) => {
+278                if let Some(e) = self.path_timers.get_mut(&path_id) {
+279                    e.stop(timer);
+280                }
+281            }
+282        }
+283        qlog.emit_timer_stop(timer);
+284    }
+285
+286    /// Stops all per-path timers
+287    pub(super) fn stop_per_path(&mut self, path_id: PathId, qlog: QlogSinkWithTime<'_>) {
+288        for timer in PathTimer::VALUES {
+289            if let Some(e) = self.path_timers.get_mut(&path_id) {
+290                e.stop(timer);
+291                qlog.emit_timer_stop(Timer::PerPath(path_id, timer));
+292            }
+293        }
+294    }
+295
+296    /// Get the next queued timeout
+297    pub(super) fn peek(&mut self) -> Option<Instant> {
+298        // TODO: this is currently linear in the number of paths
+299
+300        let min_generic = self.generic.iter().filter_map(|&x| x).min();
+301        let min_path = self
+302            .path_timers
+303            .values()
+304            .flat_map(|p| p.timers.iter().filter_map(|&x| x))
+305            .min();
+306
+307        match (min_generic, min_path) {
+308            (None, None) => None,
+309            (Some(val), None) => Some(val),
+310            (Some(a), Some(b)) => Some(a.min(b)),
+311            (None, Some(val)) => Some(val),
+312        }
+313    }
+314
+315    /// Remove the next timer up until `now`, including it
+316    pub(super) fn expire_before(
+317        &mut self,
+318        now: Instant,
+319        qlog: &QlogSink,
+320    ) -> Option<(Timer, Instant)> {
+321        let (timer, instant) = self.expire_before_inner(now)?;
+322        qlog.with_time(now).emit_timer_expire(timer);
+323        Some((timer, instant))
+324    }
+325
+326    fn expire_before_inner(&mut self, now: Instant) -> Option<(Timer, Instant)> {
+327        // TODO: this is currently linear in the number of paths
+328
+329        for timer in ConnTimer::VALUES {
+330            if self.generic[timer as usize].is_some()
+331                && self.generic[timer as usize].expect("checked") <= now
+332            {
+333                return self.generic[timer as usize]
+334                    .take()
+335                    .map(|time| (Timer::Conn(timer), time));
+336            }
+337        }
+338
+339        let mut res = None;
+340        for (path_id, timers) in self.path_timers.iter_mut() {
+341            if let Some((timer, time)) = timers.expire_before(now) {
+342                res = Some((Timer::PerPath(*path_id, timer), time));
+343                break;
+344            }
+345        }
+346
+347        // clear out old timers
+348        self.path_timers
+349            .retain(|_path_id, timers| timers.timers.iter().any(|t| t.is_some()));
+350        res
+351    }
+352
+353    pub(super) fn reset(&mut self) {
+354        for timer in ConnTimer::VALUES {
+355            self.generic[timer as usize] = None;
+356        }
+357        self.path_timers.clear();
+358    }
+359
+360    #[cfg(test)]
+361    pub(super) fn values(&self) -> Vec<(Timer, Instant)> {
+362        let mut values = Vec::new();
+363
+364        for timer in ConnTimer::VALUES {
+365            if let Some(time) = self.generic[timer as usize] {
+366                values.push((Timer::Conn(timer), time));
+367            }
+368        }
+369
+370        for timer in PathTimer::VALUES {
+371            for (path_id, timers) in self.path_timers.iter() {
+372                if let Some(time) = timers.timers[timer as usize] {
+373                    values.push((Timer::PerPath(*path_id, timer), time));
+374                }
+375            }
+376        }
+377
+378        values
+379    }
+380}
+381
+382#[cfg(test)]
+383mod tests {
+384    use std::time::Duration;
+385
+386    use crate::connection::qlog::QlogSink;
+387
+388    use super::*;
+389
+390    #[test]
+391    fn timer_table() {
+392        let mut timers = TimerTable::default();
+393        let sec = Duration::from_secs(1);
+394        let now = Instant::now() + Duration::from_secs(10);
+395        timers.set(
+396            Timer::Conn(ConnTimer::Idle),
+397            now - 3 * sec,
+398            QlogSink::default().with_time(now),
+399        );
+400        timers.set(
+401            Timer::Conn(ConnTimer::Close),
+402            now - 2 * sec,
+403            QlogSink::default().with_time(now),
+404        );
+405
+406        assert_eq!(timers.peek(), Some(now - 3 * sec));
+407        assert_eq!(
+408            timers.expire_before(now, &QlogSink::default()),
+409            Some((Timer::Conn(ConnTimer::Idle), now - 3 * sec))
+410        );
+411        assert_eq!(
+412            timers.expire_before(now, &QlogSink::default()),
+413            Some((Timer::Conn(ConnTimer::Close), now - 2 * sec))
+414        );
+415        assert_eq!(timers.expire_before(now, &QlogSink::default()), None);
+416    }
+417
+418    #[test]
+419    fn test_small_map() {
+420        let mut map = SmallMap::<usize, usize, 2>::default();
+421
+422        // inserts only on the stack
+423        assert_eq!(map.insert(1, 1), None);
+424        assert!(map.heap.is_none());
+425        assert_eq!(map.insert(2, 2), None);
+426        assert!(map.heap.is_none());
+427
+428        // replace on the stack
+429        assert_eq!(map.insert(1, 2), Some(1));
+430
+431        assert_eq!(map.remove(&1), Some(2));
+432        assert_eq!(map.insert(3, 3), None);
+433        assert!(map.heap.is_none());
+434
+435        // spill
+436        assert_eq!(map.insert(4, 4), None);
+437        assert!(map.heap.is_some());
+438
+439        assert_eq!(
+440            map.iter()
+441                .map(|(&a, &b)| (a, b))
+442                .collect::<Vec<(usize, usize)>>(),
+443            vec![(3, 3), (2, 2), (4, 4)]
+444        );
+445        assert_eq!(
+446            map.iter()
+447                .map(|(a, b)| (*a, *b))
+448                .collect::<Vec<(usize, usize)>>(),
+449            map.iter_mut()
+450                .map(|(a, b)| (*a, *b))
+451                .collect::<Vec<(usize, usize)>>(),
+452        );
+453
+454        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+455
+456        for i in 0..10 {
+457            map.insert(10 + i, 10 + i);
+458        }
+459        assert_eq!(map.heap.as_ref().unwrap().len(), 11);
+460        map.retain(|k, _v| *k < 10);
+461
+462        assert_eq!(map.heap.as_ref().unwrap().len(), 1);
+463
+464        assert_eq!(
+465            map.iter()
+466                .map(|(&a, &b)| (a, b))
+467                .collect::<Vec<(usize, usize)>>(),
+468            vec![(3, 3), (2, 2), (4, 4)]
+469        );
+470
+471        assert_eq!(
+472            map.iter()
+473                .map(|(a, b)| (*a, *b))
+474                .collect::<Vec<(usize, usize)>>(),
+475            map.iter_mut()
+476                .map(|(a, b)| (*a, *b))
+477                .collect::<Vec<(usize, usize)>>(),
+478        );
+479
+480        map.clear();
+481        assert_eq!(map.iter().collect::<Vec<_>>(), Vec::new());
+482        assert!(map.heap.is_none());
+483    }
+484}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html b/pr/234/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html new file mode 100644 index 000000000..f4d60d96f --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/connection/transmit_buf.rs.html @@ -0,0 +1,242 @@ +transmit_buf.rs - source

iroh_quinn_proto/connection/
transmit_buf.rs

1use bytes::BufMut;
+2use tracing::trace;
+3
+4use crate::packet::BufLen;
+5
+6/// The buffer in which to write datagrams for [`Connection::poll_transmit`]
+7///
+8/// The `poll_transmit` function writes zero or more datagrams to a buffer. Multiple
+9/// datagrams are possible in case GSO (Generic Segmentation Offload) is supported.
+10///
+11/// This buffer tracks datagrams being written to it. There is always a "current" datagram,
+12/// which is started by calling [`TransmitBuf::start_new_datagram`]. Writing to the buffer
+13/// is done through the [`BufMut`] interface.
+14///
+15/// Usually a datagram contains one QUIC packet, though QUIC-TRANSPORT 12.2 Coalescing
+16/// Packets allows for placing multiple packets into a single datagram provided all but the
+17/// last packet uses long headers. This is normally used during connection setup where often
+18/// the initial, handshake and sometimes even a 1-RTT packet can be coalesced into a single
+19/// datagram.
+20///
+21/// Inside a single packet multiple QUIC frames are written.
+22///
+23/// The buffer managed here is passed straight to the OS' `sendmsg` call (or variant) once
+24/// `poll_transmit` returns.  So needs to contain the datagrams as they are sent on the
+25/// wire.
+26///
+27/// [`Connection::poll_transmit`]: super::Connection::poll_transmit
+28#[derive(Debug)]
+29pub(super) struct TransmitBuf<'a> {
+30    /// The buffer itself, packets are written to this buffer
+31    buf: &'a mut Vec<u8>,
+32    /// Offset into the buffer at which the current datagram starts
+33    ///
+34    /// Note that when coalescing packets this might be before the start of the current
+35    /// packet.
+36    datagram_start: usize,
+37    /// The maximum offset allowed to be used for the current datagram in the buffer
+38    ///
+39    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+40    /// size. All datagrams in between need to be exactly this size.
+41    buf_capacity: usize,
+42    /// The maximum number of datagrams allowed to write into [`TransmitBuf::buf`]
+43    max_datagrams: usize,
+44    /// The number of datagrams already (partially) written into the buffer
+45    ///
+46    /// Incremented by a call to [`TransmitBuf::start_new_datagram`].
+47    pub(super) num_datagrams: usize,
+48    /// The segment size of this GSO batch
+49    ///
+50    /// The segment size is the size of each datagram in the GSO batch, only the last
+51    /// datagram in the batch may be smaller.
+52    ///
+53    /// For the first datagram this is set to the maximum size a datagram is allowed to be:
+54    /// the current path MTU. After the first datagram is finished this is reduced to the
+55    /// size of the first datagram and can no longer change.
+56    segment_size: usize,
+57}
+58
+59impl<'a> TransmitBuf<'a> {
+60    pub(super) fn new(buf: &'a mut Vec<u8>, max_datagrams: usize, mtu: usize) -> Self {
+61        Self {
+62            buf,
+63            datagram_start: 0,
+64            buf_capacity: 0,
+65            max_datagrams,
+66            num_datagrams: 0,
+67            segment_size: mtu,
+68        }
+69    }
+70
+71    pub(super) fn set_segment_size(&mut self, mtu: usize) {
+72        debug_assert!(
+73            self.datagram_start == 0 && self.buf_capacity == 0 && self.num_datagrams == 0,
+74            "can only change the segment size if nothing has been written yet"
+75        );
+76
+77        self.segment_size = mtu;
+78    }
+79
+80    /// Starts a datagram with a custom datagram size
+81    ///
+82    /// This is a specialized version of [`TransmitBuf::start_new_datagram`] which sets the
+83    /// datagram size. Useful for e.g. PATH_CHALLENGE, tail-loss probes or MTU probes.
+84    ///
+85    /// After the first datagram you can never increase the segment size. If you decrease
+86    /// the size of a datagram in a batch, it must be the last datagram of the batch.
+87    pub(super) fn start_new_datagram_with_size(&mut self, datagram_size: usize) {
+88        // Only reserve space for this datagram, usually it is the last one in the batch.
+89        let max_capacity_hint = datagram_size;
+90        self.new_datagram_inner(datagram_size, max_capacity_hint)
+91    }
+92
+93    /// Starts a new datagram in the transmit buffer
+94    ///
+95    /// If this starts the second datagram the segment size will be set to the size of the
+96    /// first datagram.
+97    ///
+98    /// If the underlying buffer does not have enough capacity yet this will allocate enough
+99    /// capacity for all the datagrams allowed in a single batch. Use
+100    /// [`TransmitBuf::start_new_datagram_with_size`] if you know you will need less.
+101    pub(super) fn start_new_datagram(&mut self) {
+102        // We reserve the maximum space for sending `max_datagrams` upfront to avoid any
+103        // reallocations if more datagrams have to be appended later on.  Benchmarks have
+104        // shown a 5-10% throughput improvement compared to continuously resizing the
+105        // datagram buffer. While this will lead to over-allocation for small transmits
+106        // (e.g. purely containing ACKs), modern memory allocators (e.g. mimalloc and
+107        // jemalloc) will pool certain allocation sizes and therefore this is still rather
+108        // efficient.
+109        let max_capacity_hint = self.max_datagrams * self.segment_size;
+110        self.new_datagram_inner(self.segment_size, max_capacity_hint)
+111    }
+112
+113    fn new_datagram_inner(&mut self, datagram_size: usize, max_capacity_hint: usize) {
+114        debug_assert!(self.num_datagrams < self.max_datagrams);
+115        if self.num_datagrams == 1 {
+116            // Set the segment size to the size of the first datagram.
+117            self.segment_size = self.buf.len();
+118        }
+119        if self.num_datagrams >= 1 {
+120            debug_assert!(datagram_size <= self.segment_size);
+121            if datagram_size < self.segment_size {
+122                // If this is a GSO batch and this datagram is smaller than the segment
+123                // size, this must be the last datagram in the batch.
+124                self.max_datagrams = self.num_datagrams + 1;
+125            }
+126        }
+127        self.datagram_start = self.buf.len();
+128        debug_assert_eq!(
+129            self.datagram_start % self.segment_size,
+130            0,
+131            "datagrams in a GSO batch must be aligned to the segment size"
+132        );
+133        self.buf_capacity = self.datagram_start + datagram_size;
+134        if self.buf_capacity > self.buf.capacity() {
+135            self.buf
+136                .reserve_exact(max_capacity_hint.saturating_sub(self.buf.capacity()));
+137        }
+138        self.num_datagrams += 1;
+139    }
+140
+141    /// Clips the datagram size to the current size
+142    ///
+143    /// Only valid for the first datagram, when the datagram might be smaller than the
+144    /// segment size. Needed before estimating the available space in the next datagram
+145    /// based on [`TransmitBuf::segment_size`].
+146    ///
+147    /// Use [`TransmitBuf::start_new_datagram_with_size`] if you need to reduce the size of
+148    /// the last datagram in a batch.
+149    pub(super) fn clip_datagram_size(&mut self) {
+150        debug_assert_eq!(self.num_datagrams, 1);
+151        if self.buf.len() < self.segment_size {
+152            trace!(
+153                segment_size = self.buf.len(),
+154                prev_segment_size = self.segment_size,
+155                "clipped datagram size"
+156            );
+157        }
+158        self.segment_size = self.buf.len();
+159        self.buf_capacity = self.buf.len();
+160    }
+161
+162    /// Returns the GSO segment size
+163    ///
+164    /// This is also the maximum size datagrams are allowed to be. The first and last
+165    /// datagram in a batch are allowed to be smaller however. After the first datagram the
+166    /// segment size is clipped to the size of the first datagram.
+167    ///
+168    /// If the last datagram was created using [`TransmitBuf::start_new_datagram_with_size`]
+169    /// the the segment size will be greater than the current datagram is allowed to be.
+170    /// Thus [`TransmitBuf::datagram_remaining_mut`] should be used if you need to know the
+171    /// amount of data that can be written into the datagram.
+172    pub(super) fn segment_size(&self) -> usize {
+173        self.segment_size
+174    }
+175
+176    /// Returns the number of datagrams written into the buffer
+177    ///
+178    /// The last datagram is not necessarily finished yet.
+179    pub(super) fn num_datagrams(&self) -> usize {
+180        self.num_datagrams
+181    }
+182
+183    /// Returns the maximum number of datagrams allowed to be written into the buffer
+184    pub(super) fn max_datagrams(&self) -> usize {
+185        self.max_datagrams
+186    }
+187
+188    /// Returns the start offset of the current datagram in the buffer
+189    ///
+190    /// In other words, this offset contains the first byte of the current datagram.
+191    pub(super) fn datagram_start_offset(&self) -> usize {
+192        self.datagram_start
+193    }
+194
+195    /// Returns the maximum offset in the buffer allowed for the current datagram
+196    ///
+197    /// The first and last datagram in a batch are allowed to be smaller then the maximum
+198    /// size. All datagrams in between need to be exactly this size.
+199    pub(super) fn datagram_max_offset(&self) -> usize {
+200        self.buf_capacity
+201    }
+202
+203    /// Returns the number of bytes that may still be written into this datagram
+204    pub(super) fn datagram_remaining_mut(&self) -> usize {
+205        self.buf_capacity.saturating_sub(self.buf.len())
+206    }
+207
+208    /// Returns `true` if the buffer did not have anything written into it
+209    pub(super) fn is_empty(&self) -> bool {
+210        self.len() == 0
+211    }
+212
+213    /// The number of bytes written into the buffer so far
+214    pub(super) fn len(&self) -> usize {
+215        self.buf.len()
+216    }
+217
+218    /// Returns the already written bytes in the buffer
+219    pub(super) fn as_mut_slice(&mut self) -> &mut [u8] {
+220        self.buf.as_mut_slice()
+221    }
+222}
+223
+224unsafe impl BufMut for TransmitBuf<'_> {
+225    fn remaining_mut(&self) -> usize {
+226        self.buf.remaining_mut()
+227    }
+228
+229    unsafe fn advance_mut(&mut self, cnt: usize) {
+230        unsafe { self.buf.advance_mut(cnt) };
+231    }
+232
+233    fn chunk_mut(&mut self) -> &mut bytes::buf::UninitSlice {
+234        self.buf.chunk_mut()
+235    }
+236}
+237
+238impl BufLen for TransmitBuf<'_> {
+239    fn len(&self) -> usize {
+240        self.len()
+241    }
+242}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/constant_time.rs.html b/pr/234/docs/src/iroh_quinn_proto/constant_time.rs.html new file mode 100644 index 000000000..1e5f93742 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/constant_time.rs.html @@ -0,0 +1,22 @@ +constant_time.rs - source

iroh_quinn_proto/
constant_time.rs

1// This function is non-inline to prevent the optimizer from looking inside it.
+2#[inline(never)]
+3fn constant_time_ne(a: &[u8], b: &[u8]) -> u8 {
+4    assert!(a.len() == b.len());
+5
+6    // These useless slices make the optimizer elide the bounds checks.
+7    // See the comment in clone_from_slice() added on Rust commit 6a7bc47.
+8    let len = a.len();
+9    let a = &a[..len];
+10    let b = &b[..len];
+11
+12    let mut tmp = 0;
+13    for i in 0..len {
+14        tmp |= a[i] ^ b[i];
+15    }
+16    tmp // The compare with 0 must happen outside this function.
+17}
+18
+19/// Compares byte strings in constant time.
+20pub(crate) fn eq(a: &[u8], b: &[u8]) -> bool {
+21    a.len() == b.len() && constant_time_ne(a, b) == 0
+22}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/crypto.rs.html b/pr/234/docs/src/iroh_quinn_proto/crypto.rs.html new file mode 100644 index 000000000..ab9b332e2 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/crypto.rs.html @@ -0,0 +1,221 @@ +crypto.rs - source

iroh_quinn_proto/
crypto.rs

1//! Traits and implementations for the QUIC cryptography protocol
+2//!
+3//! The protocol logic in Quinn is contained in types that abstract over the actual
+4//! cryptographic protocol used. This module contains the traits used for this
+5//! abstraction layer as well as a single implementation of these traits that uses
+6//! *ring* and rustls to implement the TLS protocol support.
+7//!
+8//! Note that usage of any protocol (version) other than TLS 1.3 does not conform to any
+9//! published versions of the specification, and will not be supported in QUIC v1.
+10
+11use std::{any::Any, str, sync::Arc};
+12
+13use bytes::BytesMut;
+14
+15use crate::{
+16    ConnectError, PathId, Side, TransportError, shared::ConnectionId,
+17    transport_parameters::TransportParameters,
+18};
+19
+20/// Cryptography interface based on *ring*
+21#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]
+22pub(crate) mod ring_like;
+23/// TLS interface based on rustls
+24#[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+25pub mod rustls;
+26
+27/// A cryptographic session (commonly TLS)
+28pub trait Session: Send + Sync + 'static {
+29    /// Create the initial set of keys given the client's initial destination ConnectionId
+30    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys;
+31
+32    /// Get data negotiated during the handshake, if available
+33    ///
+34    /// Returns `None` until the connection emits `HandshakeDataReady`.
+35    fn handshake_data(&self) -> Option<Box<dyn Any>>;
+36
+37    /// Get the peer's identity, if available
+38    fn peer_identity(&self) -> Option<Box<dyn Any>>;
+39
+40    /// Get the 0-RTT keys if available (clients only)
+41    ///
+42    /// On the client side, this method can be used to see if 0-RTT key material is available
+43    /// to start sending data before the protocol handshake has completed.
+44    ///
+45    /// Returns `None` if the key material is not available. This might happen if you have
+46    /// not connected to this server before.
+47    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn PacketKey>)>;
+48
+49    /// If the 0-RTT-encrypted data has been accepted by the peer
+50    fn early_data_accepted(&self) -> Option<bool>;
+51
+52    /// Returns `true` until the connection is fully established.
+53    fn is_handshaking(&self) -> bool;
+54
+55    /// Read bytes of handshake data
+56    ///
+57    /// This should be called with the contents of `CRYPTO` frames. If it returns `Ok`, the
+58    /// caller should call `write_handshake()` to check if the crypto protocol has anything
+59    /// to send to the peer. This method will only return `true` the first time that
+60    /// handshake data is available. Future calls will always return false.
+61    ///
+62    /// On success, returns `true` iff `self.handshake_data()` has been populated.
+63    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError>;
+64
+65    /// The peer's QUIC transport parameters
+66    ///
+67    /// These are only available after the first flight from the peer has been received.
+68    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError>;
+69
+70    /// Writes handshake bytes into the given buffer and optionally returns the negotiated keys
+71    ///
+72    /// When the handshake proceeds to the next phase, this method will return a new set of
+73    /// keys to encrypt data with.
+74    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys>;
+75
+76    /// Compute keys for the next key update
+77    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn PacketKey>>>;
+78
+79    /// Verify the integrity of a retry packet
+80    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool;
+81
+82    /// Fill `output` with `output.len()` bytes of keying material derived
+83    /// from the [Session]'s secrets, using `label` and `context` for domain
+84    /// separation.
+85    ///
+86    /// This function will fail, returning [ExportKeyingMaterialError],
+87    /// if the requested output length is too large.
+88    fn export_keying_material(
+89        &self,
+90        output: &mut [u8],
+91        label: &[u8],
+92        context: &[u8],
+93    ) -> Result<(), ExportKeyingMaterialError>;
+94}
+95
+96/// A pair of keys for bidirectional communication
+97pub struct KeyPair<T> {
+98    /// Key for encrypting data
+99    pub local: T,
+100    /// Key for decrypting data
+101    pub remote: T,
+102}
+103
+104/// A complete set of keys for a certain packet space
+105pub struct Keys {
+106    /// Header protection keys
+107    pub header: KeyPair<Box<dyn HeaderKey>>,
+108    /// Packet protection keys
+109    pub packet: KeyPair<Box<dyn PacketKey>>,
+110}
+111
+112/// Client-side configuration for the crypto protocol
+113pub trait ClientConfig: Send + Sync {
+114    /// Start a client session with this configuration
+115    fn start_session(
+116        self: Arc<Self>,
+117        version: u32,
+118        server_name: &str,
+119        params: &TransportParameters,
+120    ) -> Result<Box<dyn Session>, ConnectError>;
+121}
+122
+123/// Server-side configuration for the crypto protocol
+124pub trait ServerConfig: Send + Sync {
+125    /// Create the initial set of keys given the client's initial destination ConnectionId
+126    fn initial_keys(&self, version: u32, dst_cid: ConnectionId)
+127    -> Result<Keys, UnsupportedVersion>;
+128
+129    /// Generate the integrity tag for a retry packet
+130    ///
+131    /// Never called if `initial_keys` rejected `version`.
+132    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16];
+133
+134    /// Start a server session with this configuration
+135    ///
+136    /// Never called if `initial_keys` rejected `version`.
+137    fn start_session(
+138        self: Arc<Self>,
+139        version: u32,
+140        params: &TransportParameters,
+141    ) -> Box<dyn Session>;
+142}
+143
+144/// Keys used to protect packet payloads
+145pub trait PacketKey: Send + Sync {
+146    /// Encrypt the packet payload with the given packet number
+147    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize);
+148    /// Decrypt the packet payload with the given packet number
+149    fn decrypt(
+150        &self,
+151        path_id: PathId,
+152        packet: u64,
+153        header: &[u8],
+154        payload: &mut BytesMut,
+155    ) -> Result<(), CryptoError>;
+156    /// The length of the AEAD tag appended to packets on encryption
+157    fn tag_len(&self) -> usize;
+158    /// Maximum number of packets that may be sent using a single key
+159    fn confidentiality_limit(&self) -> u64;
+160    /// Maximum number of incoming packets that may fail decryption before the connection must be
+161    /// abandoned
+162    fn integrity_limit(&self) -> u64;
+163}
+164
+165/// Keys used to protect packet headers
+166pub trait HeaderKey: Send + Sync {
+167    /// Decrypt the given packet's header
+168    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]);
+169    /// Encrypt the given packet's header
+170    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]);
+171    /// The sample size used for this key's algorithm
+172    fn sample_size(&self) -> usize;
+173}
+174
+175/// A key for signing with HMAC-based algorithms
+176pub trait HmacKey: Send + Sync {
+177    /// Method for signing a message
+178    fn sign(&self, data: &[u8], signature_out: &mut [u8]);
+179    /// Length of `sign`'s output
+180    fn signature_len(&self) -> usize;
+181    /// Method for verifying a message
+182    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError>;
+183}
+184
+185/// Error returned by [Session::export_keying_material].
+186///
+187/// This error occurs if the requested output length is too large.
+188#[derive(Debug, PartialEq, Eq)]
+189pub struct ExportKeyingMaterialError;
+190
+191/// A pseudo random key for HKDF
+192pub trait HandshakeTokenKey: Send + Sync {
+193    /// Derive AEAD using hkdf
+194    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn AeadKey>;
+195}
+196
+197/// A key for sealing data with AEAD-based algorithms
+198pub trait AeadKey {
+199    /// Method for sealing message `data`
+200    fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError>;
+201    /// Method for opening a sealed message `data`
+202    fn open<'a>(
+203        &self,
+204        data: &'a mut [u8],
+205        additional_data: &[u8],
+206    ) -> Result<&'a mut [u8], CryptoError>;
+207}
+208
+209/// Generic crypto errors
+210#[derive(Debug)]
+211pub struct CryptoError;
+212
+213/// Error indicating that the specified QUIC version is not supported
+214#[derive(Debug)]
+215pub struct UnsupportedVersion;
+216
+217impl From<UnsupportedVersion> for ConnectError {
+218    fn from(_: UnsupportedVersion) -> Self {
+219        Self::UnsupportedVersion
+220    }
+221}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html b/pr/234/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html new file mode 100644 index 000000000..e4c1ba9cb --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/crypto/ring_like.rs.html @@ -0,0 +1,57 @@ +ring_like.rs - source

iroh_quinn_proto/crypto/
ring_like.rs

1#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+2use aws_lc_rs::{aead, error, hkdf, hmac};
+3#[cfg(feature = "ring")]
+4use ring::{aead, error, hkdf, hmac};
+5
+6use crate::crypto::{self, CryptoError};
+7
+8impl crypto::HmacKey for hmac::Key {
+9    fn sign(&self, data: &[u8], out: &mut [u8]) {
+10        out.copy_from_slice(hmac::sign(self, data).as_ref());
+11    }
+12
+13    fn signature_len(&self) -> usize {
+14        32
+15    }
+16
+17    fn verify(&self, data: &[u8], signature: &[u8]) -> Result<(), CryptoError> {
+18        Ok(hmac::verify(self, data, signature)?)
+19    }
+20}
+21
+22impl crypto::HandshakeTokenKey for hkdf::Prk {
+23    fn aead_from_hkdf(&self, random_bytes: &[u8]) -> Box<dyn crypto::AeadKey> {
+24        let mut key_buffer = [0u8; 32];
+25        let info = [random_bytes];
+26        let okm = self.expand(&info, hkdf::HKDF_SHA256).unwrap();
+27
+28        okm.fill(&mut key_buffer).unwrap();
+29
+30        let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_buffer).unwrap();
+31        Box::new(aead::LessSafeKey::new(key))
+32    }
+33}
+34
+35impl crypto::AeadKey for aead::LessSafeKey {
+36    fn seal(&self, data: &mut Vec<u8>, additional_data: &[u8]) -> Result<(), CryptoError> {
+37        let aad = aead::Aad::from(additional_data);
+38        let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
+39        Ok(self.seal_in_place_append_tag(zero_nonce, aad, data)?)
+40    }
+41
+42    fn open<'a>(
+43        &self,
+44        data: &'a mut [u8],
+45        additional_data: &[u8],
+46    ) -> Result<&'a mut [u8], CryptoError> {
+47        let aad = aead::Aad::from(additional_data);
+48        let zero_nonce = aead::Nonce::assume_unique_for_key([0u8; 12]);
+49        Ok(self.open_in_place(zero_nonce, aad, data)?)
+50    }
+51}
+52
+53impl From<error::Unspecified> for CryptoError {
+54    fn from(_: error::Unspecified) -> Self {
+55        Self
+56    }
+57}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/crypto/rustls.rs.html b/pr/234/docs/src/iroh_quinn_proto/crypto/rustls.rs.html new file mode 100644 index 000000000..dae403f06 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/crypto/rustls.rs.html @@ -0,0 +1,660 @@ +rustls.rs - source

iroh_quinn_proto/crypto/
rustls.rs

1use std::{any::Any, io, str, sync::Arc};
+2
+3#[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+4use aws_lc_rs::aead;
+5use bytes::BytesMut;
+6#[cfg(feature = "ring")]
+7use ring::aead;
+8pub use rustls::Error;
+9#[cfg(feature = "__rustls-post-quantum-test")]
+10use rustls::NamedGroup;
+11use rustls::{
+12    self, CipherSuite,
+13    client::danger::ServerCertVerifier,
+14    pki_types::{CertificateDer, PrivateKeyDer, ServerName},
+15    quic::{Connection, HeaderProtectionKey, KeyChange, PacketKey, Secrets, Suite, Version},
+16};
+17#[cfg(feature = "platform-verifier")]
+18use rustls_platform_verifier::BuilderVerifierExt;
+19
+20use crate::{
+21    ConnectError, ConnectionId, PathId, Side, TransportError, TransportErrorCode,
+22    crypto::{
+23        self, CryptoError, ExportKeyingMaterialError, HeaderKey, KeyPair, Keys, UnsupportedVersion,
+24    },
+25    transport_parameters::TransportParameters,
+26};
+27
+28impl From<Side> for rustls::Side {
+29    fn from(s: Side) -> Self {
+30        match s {
+31            Side::Client => Self::Client,
+32            Side::Server => Self::Server,
+33        }
+34    }
+35}
+36
+37/// A rustls TLS session
+38pub struct TlsSession {
+39    version: Version,
+40    got_handshake_data: bool,
+41    next_secrets: Option<Secrets>,
+42    inner: Connection,
+43    suite: Suite,
+44}
+45
+46impl TlsSession {
+47    fn side(&self) -> Side {
+48        match self.inner {
+49            Connection::Client(_) => Side::Client,
+50            Connection::Server(_) => Side::Server,
+51        }
+52    }
+53}
+54
+55impl crypto::Session for TlsSession {
+56    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> Keys {
+57        initial_keys(self.version, dst_cid, side, &self.suite)
+58    }
+59
+60    fn handshake_data(&self) -> Option<Box<dyn Any>> {
+61        if !self.got_handshake_data {
+62            return None;
+63        }
+64        Some(Box::new(HandshakeData {
+65            protocol: self.inner.alpn_protocol().map(|x| x.into()),
+66            server_name: match self.inner {
+67                Connection::Client(_) => None,
+68                Connection::Server(ref session) => session.server_name().map(|x| x.into()),
+69            },
+70            #[cfg(feature = "__rustls-post-quantum-test")]
+71            negotiated_key_exchange_group: self
+72                .inner
+73                .negotiated_key_exchange_group()
+74                .expect("key exchange group is negotiated")
+75                .name(),
+76        }))
+77    }
+78
+79    /// For the rustls `TlsSession`, the `Any` type is `Vec<rustls::pki_types::CertificateDer>`
+80    fn peer_identity(&self) -> Option<Box<dyn Any>> {
+81        self.inner.peer_certificates().map(|v| -> Box<dyn Any> {
+82            Box::new(
+83                v.iter()
+84                    .map(|v| v.clone().into_owned())
+85                    .collect::<Vec<CertificateDer<'static>>>(),
+86            )
+87        })
+88    }
+89
+90    fn early_crypto(&self) -> Option<(Box<dyn HeaderKey>, Box<dyn crypto::PacketKey>)> {
+91        let keys = self.inner.zero_rtt_keys()?;
+92        Some((Box::new(keys.header), Box::new(keys.packet)))
+93    }
+94
+95    fn early_data_accepted(&self) -> Option<bool> {
+96        match self.inner {
+97            Connection::Client(ref session) => Some(session.is_early_data_accepted()),
+98            _ => None,
+99        }
+100    }
+101
+102    fn is_handshaking(&self) -> bool {
+103        self.inner.is_handshaking()
+104    }
+105
+106    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+107        self.inner.read_hs(buf).map_err(|e| {
+108            if let Some(alert) = self.inner.alert() {
+109                TransportError {
+110                    code: TransportErrorCode::crypto(alert.into()),
+111                    frame: None,
+112                    reason: e.to_string(),
+113                    crypto: Some(Arc::new(e)),
+114                }
+115            } else {
+116                TransportError::PROTOCOL_VIOLATION(format!("TLS error: {e}"))
+117            }
+118        })?;
+119        if !self.got_handshake_data {
+120            // Hack around the lack of an explicit signal from rustls to reflect ClientHello being
+121            // ready on incoming connections, or ALPN negotiation completing on outgoing
+122            // connections.
+123            let have_server_name = match self.inner {
+124                Connection::Client(_) => false,
+125                Connection::Server(ref session) => session.server_name().is_some(),
+126            };
+127            if self.inner.alpn_protocol().is_some() || have_server_name || !self.is_handshaking() {
+128                self.got_handshake_data = true;
+129                return Ok(true);
+130            }
+131        }
+132        Ok(false)
+133    }
+134
+135    fn transport_parameters(&self) -> Result<Option<TransportParameters>, TransportError> {
+136        match self.inner.quic_transport_parameters() {
+137            None => Ok(None),
+138            Some(buf) => match TransportParameters::read(self.side(), &mut io::Cursor::new(buf)) {
+139                Ok(params) => Ok(Some(params)),
+140                Err(e) => Err(e.into()),
+141            },
+142        }
+143    }
+144
+145    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<Keys> {
+146        let keys = match self.inner.write_hs(buf)? {
+147            KeyChange::Handshake { keys } => keys,
+148            KeyChange::OneRtt { keys, next } => {
+149                self.next_secrets = Some(next);
+150                keys
+151            }
+152        };
+153
+154        Some(Keys {
+155            header: KeyPair {
+156                local: Box::new(keys.local.header),
+157                remote: Box::new(keys.remote.header),
+158            },
+159            packet: KeyPair {
+160                local: Box::new(keys.local.packet),
+161                remote: Box::new(keys.remote.packet),
+162            },
+163        })
+164    }
+165
+166    fn next_1rtt_keys(&mut self) -> Option<KeyPair<Box<dyn crypto::PacketKey>>> {
+167        let secrets = self.next_secrets.as_mut()?;
+168        let keys = secrets.next_packet_keys();
+169        Some(KeyPair {
+170            local: Box::new(keys.local),
+171            remote: Box::new(keys.remote),
+172        })
+173    }
+174
+175    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+176        let tag_start = match payload.len().checked_sub(16) {
+177            Some(x) => x,
+178            None => return false,
+179        };
+180
+181        let mut pseudo_packet =
+182            Vec::with_capacity(header.len() + payload.len() + orig_dst_cid.len() + 1);
+183        pseudo_packet.push(orig_dst_cid.len() as u8);
+184        pseudo_packet.extend_from_slice(&orig_dst_cid);
+185        pseudo_packet.extend_from_slice(header);
+186        let tag_start = tag_start + pseudo_packet.len();
+187        pseudo_packet.extend_from_slice(payload);
+188
+189        let (nonce, key) = match self.version {
+190            Version::V1 => (RETRY_INTEGRITY_NONCE_V1, RETRY_INTEGRITY_KEY_V1),
+191            Version::V1Draft => (RETRY_INTEGRITY_NONCE_DRAFT, RETRY_INTEGRITY_KEY_DRAFT),
+192            _ => unreachable!(),
+193        };
+194
+195        let nonce = aead::Nonce::assume_unique_for_key(nonce);
+196        let key = aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
+197
+198        let (aad, tag) = pseudo_packet.split_at_mut(tag_start);
+199        key.open_in_place(nonce, aead::Aad::from(aad), tag).is_ok()
+200    }
+201
+202    fn export_keying_material(
+203        &self,
+204        output: &mut [u8],
+205        label: &[u8],
+206        context: &[u8],
+207    ) -> Result<(), ExportKeyingMaterialError> {
+208        self.inner
+209            .export_keying_material(output, label, Some(context))
+210            .map_err(|_| ExportKeyingMaterialError)?;
+211        Ok(())
+212    }
+213}
+214
+215const RETRY_INTEGRITY_KEY_DRAFT: [u8; 16] = [
+216    0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1,
+217];
+218const RETRY_INTEGRITY_NONCE_DRAFT: [u8; 12] = [
+219    0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c,
+220];
+221
+222const RETRY_INTEGRITY_KEY_V1: [u8; 16] = [
+223    0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e,
+224];
+225const RETRY_INTEGRITY_NONCE_V1: [u8; 12] = [
+226    0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb,
+227];
+228
+229impl crypto::HeaderKey for Box<dyn HeaderProtectionKey> {
+230    fn decrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+231        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+232        let (first, rest) = header.split_at_mut(1);
+233        let pn_end = Ord::min(pn_offset + 3, rest.len());
+234        self.decrypt_in_place(
+235            &sample[..self.sample_size()],
+236            &mut first[0],
+237            &mut rest[pn_offset - 1..pn_end],
+238        )
+239        .unwrap();
+240    }
+241
+242    fn encrypt(&self, pn_offset: usize, packet: &mut [u8]) {
+243        let (header, sample) = packet.split_at_mut(pn_offset + 4);
+244        let (first, rest) = header.split_at_mut(1);
+245        let pn_end = Ord::min(pn_offset + 3, rest.len());
+246        self.encrypt_in_place(
+247            &sample[..self.sample_size()],
+248            &mut first[0],
+249            &mut rest[pn_offset - 1..pn_end],
+250        )
+251        .unwrap();
+252    }
+253
+254    fn sample_size(&self) -> usize {
+255        self.sample_len()
+256    }
+257}
+258
+259/// Authentication data for (rustls) TLS session
+260pub struct HandshakeData {
+261    /// The negotiated application protocol, if ALPN is in use
+262    ///
+263    /// Guaranteed to be set if a nonempty list of protocols was specified for this connection.
+264    pub protocol: Option<Vec<u8>>,
+265    /// The server name specified by the client, if any
+266    ///
+267    /// Always `None` for outgoing connections
+268    pub server_name: Option<String>,
+269    /// The key exchange group negotiated with the peer
+270    #[cfg(feature = "__rustls-post-quantum-test")]
+271    pub negotiated_key_exchange_group: NamedGroup,
+272}
+273
+274/// A QUIC-compatible TLS client configuration
+275///
+276/// Quinn implicitly constructs a `QuicClientConfig` with reasonable defaults within
+277/// [`ClientConfig::with_root_certificates()`][root_certs] and [`ClientConfig::with_platform_verifier()`][platform].
+278/// Alternatively, `QuicClientConfig`'s [`TryFrom`] implementation can be used to wrap around a
+279/// custom [`rustls::ClientConfig`], in which case care should be taken around certain points:
+280///
+281/// - If `enable_early_data` is not set to true, then sending 0-RTT data will not be possible on
+282///   outgoing connections.
+283/// - The [`rustls::ClientConfig`] must have TLS 1.3 support enabled for conversion to succeed.
+284///
+285/// The object in the `resumption` field of the inner [`rustls::ClientConfig`] determines whether
+286/// calling `into_0rtt` on outgoing connections returns `Ok` or `Err`. It typically allows
+287/// `into_0rtt` to proceed if it recognizes the server name, and defaults to an in-memory cache of
+288/// 256 server names.
+289///
+290/// [root_certs]: crate::config::ClientConfig::with_root_certificates()
+291/// [platform]: crate::config::ClientConfig::with_platform_verifier()
+292pub struct QuicClientConfig {
+293    pub(crate) inner: Arc<rustls::ClientConfig>,
+294    initial: Suite,
+295}
+296
+297impl QuicClientConfig {
+298    #[cfg(feature = "platform-verifier")]
+299    pub(crate) fn with_platform_verifier() -> Result<Self, Error> {
+300        // Keep in sync with `inner()` below
+301        let mut inner = rustls::ClientConfig::builder_with_provider(configured_provider())
+302            .with_protocol_versions(&[&rustls::version::TLS13])
+303            .unwrap() // The default providers support TLS 1.3
+304            .with_platform_verifier()?
+305            .with_no_client_auth();
+306
+307        inner.enable_early_data = true;
+308        Ok(Self {
+309            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+310            initial: initial_suite_from_provider(inner.crypto_provider())
+311                .expect("no initial cipher suite found"),
+312            inner: Arc::new(inner),
+313        })
+314    }
+315
+316    /// Initialize a sane QUIC-compatible TLS client configuration
+317    ///
+318    /// QUIC requires that TLS 1.3 be enabled. Advanced users can use any [`rustls::ClientConfig`] that
+319    /// satisfies this requirement.
+320    pub(crate) fn new(verifier: Arc<dyn ServerCertVerifier>) -> Self {
+321        let inner = Self::inner(verifier);
+322        Self {
+323            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+324            initial: initial_suite_from_provider(inner.crypto_provider())
+325                .expect("no initial cipher suite found"),
+326            inner: Arc::new(inner),
+327        }
+328    }
+329
+330    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+331    ///
+332    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+333    pub fn with_initial(
+334        inner: Arc<rustls::ClientConfig>,
+335        initial: Suite,
+336    ) -> Result<Self, NoInitialCipherSuite> {
+337        match initial.suite.common.suite {
+338            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+339            _ => Err(NoInitialCipherSuite { specific: true }),
+340        }
+341    }
+342
+343    pub(crate) fn inner(verifier: Arc<dyn ServerCertVerifier>) -> rustls::ClientConfig {
+344        // Keep in sync with `with_platform_verifier()` above
+345        let mut config = rustls::ClientConfig::builder_with_provider(configured_provider())
+346            .with_protocol_versions(&[&rustls::version::TLS13])
+347            .unwrap() // The default providers support TLS 1.3
+348            .dangerous()
+349            .with_custom_certificate_verifier(verifier)
+350            .with_no_client_auth();
+351
+352        config.enable_early_data = true;
+353        config
+354    }
+355}
+356
+357impl crypto::ClientConfig for QuicClientConfig {
+358    fn start_session(
+359        self: Arc<Self>,
+360        version: u32,
+361        server_name: &str,
+362        params: &TransportParameters,
+363    ) -> Result<Box<dyn crypto::Session>, ConnectError> {
+364        let version = interpret_version(version)?;
+365        Ok(Box::new(TlsSession {
+366            version,
+367            got_handshake_data: false,
+368            next_secrets: None,
+369            inner: rustls::quic::Connection::Client(
+370                rustls::quic::ClientConnection::new(
+371                    self.inner.clone(),
+372                    version,
+373                    ServerName::try_from(server_name)
+374                        .map_err(|_| ConnectError::InvalidServerName(server_name.into()))?
+375                        .to_owned(),
+376                    to_vec(params),
+377                )
+378                .unwrap(),
+379            ),
+380            suite: self.initial,
+381        }))
+382    }
+383}
+384
+385impl TryFrom<rustls::ClientConfig> for QuicClientConfig {
+386    type Error = NoInitialCipherSuite;
+387
+388    fn try_from(inner: rustls::ClientConfig) -> Result<Self, Self::Error> {
+389        Arc::new(inner).try_into()
+390    }
+391}
+392
+393impl TryFrom<Arc<rustls::ClientConfig>> for QuicClientConfig {
+394    type Error = NoInitialCipherSuite;
+395
+396    fn try_from(inner: Arc<rustls::ClientConfig>) -> Result<Self, Self::Error> {
+397        Ok(Self {
+398            initial: initial_suite_from_provider(inner.crypto_provider())
+399                .ok_or(NoInitialCipherSuite { specific: false })?,
+400            inner,
+401        })
+402    }
+403}
+404
+405/// The initial cipher suite (AES-128-GCM-SHA256) is not available
+406///
+407/// When the cipher suite is supplied `with_initial()`, it must be
+408/// [`CipherSuite::TLS13_AES_128_GCM_SHA256`]. When the cipher suite is derived from a config's
+409/// [`CryptoProvider`][provider], that provider must reference a cipher suite with the same ID.
+410///
+411/// [provider]: rustls::crypto::CryptoProvider
+412#[derive(Clone, Debug)]
+413pub struct NoInitialCipherSuite {
+414    /// Whether the initial cipher suite was supplied by the caller
+415    specific: bool,
+416}
+417
+418impl std::fmt::Display for NoInitialCipherSuite {
+419    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+420        f.write_str(match self.specific {
+421            true => "invalid cipher suite specified",
+422            false => "no initial cipher suite found",
+423        })
+424    }
+425}
+426
+427impl std::error::Error for NoInitialCipherSuite {}
+428
+429/// A QUIC-compatible TLS server configuration
+430///
+431/// Quinn implicitly constructs a `QuicServerConfig` with reasonable defaults within
+432/// [`ServerConfig::with_single_cert()`][single]. Alternatively, `QuicServerConfig`'s [`TryFrom`]
+433/// implementation or `with_initial` method can be used to wrap around a custom
+434/// [`rustls::ServerConfig`], in which case care should be taken around certain points:
+435///
+436/// - If `max_early_data_size` is not set to `u32::MAX`, the server will not be able to accept
+437///   incoming 0-RTT data. QUIC prohibits `max_early_data_size` values other than 0 or `u32::MAX`.
+438/// - The `rustls::ServerConfig` must have TLS 1.3 support enabled for conversion to succeed.
+439///
+440/// [single]: crate::config::ServerConfig::with_single_cert()
+441pub struct QuicServerConfig {
+442    inner: Arc<rustls::ServerConfig>,
+443    initial: Suite,
+444}
+445
+446impl QuicServerConfig {
+447    pub(crate) fn new(
+448        cert_chain: Vec<CertificateDer<'static>>,
+449        key: PrivateKeyDer<'static>,
+450    ) -> Result<Self, rustls::Error> {
+451        let inner = Self::inner(cert_chain, key)?;
+452        Ok(Self {
+453            // We're confident that the *ring* default provider contains TLS13_AES_128_GCM_SHA256
+454            initial: initial_suite_from_provider(inner.crypto_provider())
+455                .expect("no initial cipher suite found"),
+456            inner: Arc::new(inner),
+457        })
+458    }
+459
+460    /// Initialize a QUIC-compatible TLS client configuration with a separate initial cipher suite
+461    ///
+462    /// This is useful if you want to avoid the initial cipher suite for traffic encryption.
+463    pub fn with_initial(
+464        inner: Arc<rustls::ServerConfig>,
+465        initial: Suite,
+466    ) -> Result<Self, NoInitialCipherSuite> {
+467        match initial.suite.common.suite {
+468            CipherSuite::TLS13_AES_128_GCM_SHA256 => Ok(Self { inner, initial }),
+469            _ => Err(NoInitialCipherSuite { specific: true }),
+470        }
+471    }
+472
+473    /// Initialize a sane QUIC-compatible TLS server configuration
+474    ///
+475    /// QUIC requires that TLS 1.3 be enabled, and that the maximum early data size is either 0 or
+476    /// `u32::MAX`. Advanced users can use any [`rustls::ServerConfig`] that satisfies these
+477    /// requirements.
+478    pub(crate) fn inner(
+479        cert_chain: Vec<CertificateDer<'static>>,
+480        key: PrivateKeyDer<'static>,
+481    ) -> Result<rustls::ServerConfig, rustls::Error> {
+482        let mut inner = rustls::ServerConfig::builder_with_provider(configured_provider())
+483            .with_protocol_versions(&[&rustls::version::TLS13])
+484            .unwrap() // The *ring* default provider supports TLS 1.3
+485            .with_no_client_auth()
+486            .with_single_cert(cert_chain, key)?;
+487
+488        inner.max_early_data_size = u32::MAX;
+489        Ok(inner)
+490    }
+491}
+492
+493impl TryFrom<rustls::ServerConfig> for QuicServerConfig {
+494    type Error = NoInitialCipherSuite;
+495
+496    fn try_from(inner: rustls::ServerConfig) -> Result<Self, Self::Error> {
+497        Arc::new(inner).try_into()
+498    }
+499}
+500
+501impl TryFrom<Arc<rustls::ServerConfig>> for QuicServerConfig {
+502    type Error = NoInitialCipherSuite;
+503
+504    fn try_from(inner: Arc<rustls::ServerConfig>) -> Result<Self, Self::Error> {
+505        Ok(Self {
+506            initial: initial_suite_from_provider(inner.crypto_provider())
+507                .ok_or(NoInitialCipherSuite { specific: false })?,
+508            inner,
+509        })
+510    }
+511}
+512
+513impl crypto::ServerConfig for QuicServerConfig {
+514    fn start_session(
+515        self: Arc<Self>,
+516        version: u32,
+517        params: &TransportParameters,
+518    ) -> Box<dyn crypto::Session> {
+519        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+520        let version = interpret_version(version).unwrap();
+521        Box::new(TlsSession {
+522            version,
+523            got_handshake_data: false,
+524            next_secrets: None,
+525            inner: rustls::quic::Connection::Server(
+526                rustls::quic::ServerConnection::new(self.inner.clone(), version, to_vec(params))
+527                    .unwrap(),
+528            ),
+529            suite: self.initial,
+530        })
+531    }
+532
+533    fn initial_keys(
+534        &self,
+535        version: u32,
+536        dst_cid: ConnectionId,
+537    ) -> Result<Keys, UnsupportedVersion> {
+538        let version = interpret_version(version)?;
+539        Ok(initial_keys(version, dst_cid, Side::Server, &self.initial))
+540    }
+541
+542    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16] {
+543        // Safe: `start_session()` is never called if `initial_keys()` rejected `version`
+544        let version = interpret_version(version).unwrap();
+545        let (nonce, key) = match version {
+546            Version::V1 => (RETRY_INTEGRITY_NONCE_V1, RETRY_INTEGRITY_KEY_V1),
+547            Version::V1Draft => (RETRY_INTEGRITY_NONCE_DRAFT, RETRY_INTEGRITY_KEY_DRAFT),
+548            _ => unreachable!(),
+549        };
+550
+551        let mut pseudo_packet = Vec::with_capacity(packet.len() + orig_dst_cid.len() + 1);
+552        pseudo_packet.push(orig_dst_cid.len() as u8);
+553        pseudo_packet.extend_from_slice(&orig_dst_cid);
+554        pseudo_packet.extend_from_slice(packet);
+555
+556        let nonce = aead::Nonce::assume_unique_for_key(nonce);
+557        let key = aead::LessSafeKey::new(aead::UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
+558
+559        let tag = key
+560            .seal_in_place_separate_tag(nonce, aead::Aad::from(pseudo_packet), &mut [])
+561            .unwrap();
+562        let mut result = [0; 16];
+563        result.copy_from_slice(tag.as_ref());
+564        result
+565    }
+566}
+567
+568pub(crate) fn initial_suite_from_provider(
+569    provider: &Arc<rustls::crypto::CryptoProvider>,
+570) -> Option<Suite> {
+571    provider
+572        .cipher_suites
+573        .iter()
+574        .find_map(|cs| match (cs.suite(), cs.tls13()) {
+575            (rustls::CipherSuite::TLS13_AES_128_GCM_SHA256, Some(suite)) => {
+576                Some(suite.quic_suite())
+577            }
+578            _ => None,
+579        })
+580        .flatten()
+581}
+582
+583pub(crate) fn configured_provider() -> Arc<rustls::crypto::CryptoProvider> {
+584    #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))]
+585    let provider = rustls::crypto::aws_lc_rs::default_provider();
+586    #[cfg(feature = "rustls-ring")]
+587    let provider = rustls::crypto::ring::default_provider();
+588    Arc::new(provider)
+589}
+590
+591fn to_vec(params: &TransportParameters) -> Vec<u8> {
+592    let mut bytes = Vec::new();
+593    params.write(&mut bytes);
+594    bytes
+595}
+596
+597pub(crate) fn initial_keys(
+598    version: Version,
+599    dst_cid: ConnectionId,
+600    side: Side,
+601    suite: &Suite,
+602) -> Keys {
+603    let keys = suite.keys(&dst_cid, side.into(), version);
+604    Keys {
+605        header: KeyPair {
+606            local: Box::new(keys.local.header),
+607            remote: Box::new(keys.remote.header),
+608        },
+609        packet: KeyPair {
+610            local: Box::new(keys.local.packet),
+611            remote: Box::new(keys.remote.packet),
+612        },
+613    }
+614}
+615
+616impl crypto::PacketKey for Box<dyn PacketKey> {
+617    fn encrypt(&self, path_id: PathId, packet: u64, buf: &mut [u8], header_len: usize) {
+618        let (header, payload_tag) = buf.split_at_mut(header_len);
+619        let (payload, tag_storage) = payload_tag.split_at_mut(payload_tag.len() - self.tag_len());
+620        let tag = self
+621            .encrypt_in_place_for_path(path_id.as_u32(), packet, &*header, payload)
+622            .unwrap();
+623        tag_storage.copy_from_slice(tag.as_ref());
+624    }
+625
+626    fn decrypt(
+627        &self,
+628        path_id: PathId,
+629        packet: u64,
+630        header: &[u8],
+631        payload: &mut BytesMut,
+632    ) -> Result<(), CryptoError> {
+633        let plain = self
+634            .decrypt_in_place_for_path(path_id.as_u32(), packet, header, payload.as_mut())
+635            .map_err(|_| CryptoError)?;
+636        let plain_len = plain.len();
+637        payload.truncate(plain_len);
+638        Ok(())
+639    }
+640
+641    fn tag_len(&self) -> usize {
+642        (**self).tag_len()
+643    }
+644
+645    fn confidentiality_limit(&self) -> u64 {
+646        (**self).confidentiality_limit()
+647    }
+648
+649    fn integrity_limit(&self) -> u64 {
+650        (**self).integrity_limit()
+651    }
+652}
+653
+654fn interpret_version(version: u32) -> Result<Version, UnsupportedVersion> {
+655    match version {
+656        0xff00_001d..=0xff00_0020 => Ok(Version::V1Draft),
+657        0x0000_0001 | 0xff00_0021..=0xff00_0022 => Ok(Version::V1),
+658        _ => Err(UnsupportedVersion),
+659    }
+660}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/endpoint.rs.html b/pr/234/docs/src/iroh_quinn_proto/endpoint.rs.html new file mode 100644 index 000000000..0c15c9bf8 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/endpoint.rs.html @@ -0,0 +1,1399 @@ +endpoint.rs - source

iroh_quinn_proto/
endpoint.rs

1use std::{
+2    collections::{HashMap, hash_map},
+3    convert::TryFrom,
+4    fmt, mem,
+5    net::{IpAddr, SocketAddr},
+6    ops::{Index, IndexMut},
+7    sync::Arc,
+8};
+9
+10use bytes::{BufMut, Bytes, BytesMut};
+11use rand::{Rng, RngCore, SeedableRng, rngs::StdRng};
+12use rustc_hash::FxHashMap;
+13use slab::Slab;
+14use thiserror::Error;
+15use tracing::{debug, error, trace, warn};
+16
+17use crate::{
+18    Duration, INITIAL_MTU, Instant, MAX_CID_SIZE, MIN_INITIAL_SIZE, PathId, RESET_TOKEN_SIZE,
+19    ResetToken, Side, Transmit, TransportConfig, TransportError,
+20    cid_generator::ConnectionIdGenerator,
+21    coding::BufMutExt,
+22    config::{ClientConfig, EndpointConfig, ServerConfig},
+23    connection::{Connection, ConnectionError, SideArgs},
+24    crypto::{self, Keys, UnsupportedVersion},
+25    frame,
+26    packet::{
+27        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, PacketDecodeError,
+28        PacketNumber, PartialDecode, ProtectedInitialHeader,
+29    },
+30    shared::{
+31        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
+32        EndpointEvent, EndpointEventInner, IssuedCid,
+33    },
+34    token::{IncomingToken, InvalidRetryTokenError, Token, TokenPayload},
+35    transport_parameters::{PreferredAddress, TransportParameters},
+36};
+37
+38/// The main entry point to the library
+39///
+40/// This object performs no I/O whatsoever. Instead, it consumes incoming packets and
+41/// connection-generated events via `handle` and `handle_event`.
+42pub struct Endpoint {
+43    rng: StdRng,
+44    index: ConnectionIndex,
+45    connections: Slab<ConnectionMeta>,
+46    local_cid_generator: Box<dyn ConnectionIdGenerator>,
+47    config: Arc<EndpointConfig>,
+48    server_config: Option<Arc<ServerConfig>>,
+49    /// Whether the underlying UDP socket promises not to fragment packets
+50    allow_mtud: bool,
+51    /// Time at which a stateless reset was most recently sent
+52    last_stateless_reset: Option<Instant>,
+53    /// Buffered Initial and 0-RTT messages for pending incoming connections
+54    incoming_buffers: Slab<IncomingBuffer>,
+55    all_incoming_buffers_total_bytes: u64,
+56}
+57
+58impl Endpoint {
+59    /// Create a new endpoint
+60    ///
+61    /// `allow_mtud` enables path MTU detection when requested by `Connection` configuration for
+62    /// better performance. This requires that outgoing packets are never fragmented, which can be
+63    /// achieved via e.g. the `IPV6_DONTFRAG` socket option.
+64    ///
+65    /// If `rng_seed` is provided, it will be used to initialize the endpoint's rng (having priority
+66    /// over the rng seed configured in [`EndpointConfig`]). Note that the `rng_seed` parameter will
+67    /// be removed in a future release, so prefer setting it to `None` and configuring rng seeds
+68    /// using [`EndpointConfig::rng_seed`].
+69    pub fn new(
+70        config: Arc<EndpointConfig>,
+71        server_config: Option<Arc<ServerConfig>>,
+72        allow_mtud: bool,
+73        rng_seed: Option<[u8; 32]>,
+74    ) -> Self {
+75        let rng_seed = rng_seed.or(config.rng_seed);
+76        Self {
+77            rng: rng_seed.map_or(StdRng::from_os_rng(), StdRng::from_seed),
+78            index: ConnectionIndex::default(),
+79            connections: Slab::new(),
+80            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
+81            config,
+82            server_config,
+83            allow_mtud,
+84            last_stateless_reset: None,
+85            incoming_buffers: Slab::new(),
+86            all_incoming_buffers_total_bytes: 0,
+87        }
+88    }
+89
+90    /// Replace the server configuration, affecting new incoming connections only
+91    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
+92        self.server_config = server_config;
+93    }
+94
+95    /// Process `EndpointEvent`s emitted from related `Connection`s
+96    ///
+97    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
+98    pub fn handle_event(
+99        &mut self,
+100        ch: ConnectionHandle,
+101        event: EndpointEvent,
+102    ) -> Option<ConnectionEvent> {
+103        use EndpointEventInner::*;
+104        match event.0 {
+105            NeedIdentifiers(path_id, now, n) => {
+106                return Some(self.send_new_identifiers(path_id, now, ch, n));
+107            }
+108            ResetToken(path_id, remote, token) => {
+109                if let Some(old) = self.connections[ch]
+110                    .reset_token
+111                    .insert(path_id, (remote, token))
+112                {
+113                    self.index.connection_reset_tokens.remove(old.0, old.1);
+114                }
+115                if self.index.connection_reset_tokens.insert(remote, token, ch) {
+116                    warn!("duplicate reset token");
+117                }
+118            }
+119            RetireResetToken(path_id) => {
+120                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
+121                    self.index.connection_reset_tokens.remove(old.0, old.1);
+122                }
+123            }
+124            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
+125                if let Some(cid) = self.connections[ch]
+126                    .loc_cids
+127                    .get_mut(&path_id)
+128                    .and_then(|pcid| pcid.cids.remove(&seq))
+129                {
+130                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
+131                    self.index.retire(cid);
+132                    if allow_more_cids {
+133                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
+134                    }
+135                }
+136            }
+137            Drained => {
+138                if let Some(conn) = self.connections.try_remove(ch.0) {
+139                    self.index.remove(&conn);
+140                } else {
+141                    // This indicates a bug in downstream code, which could cause spurious
+142                    // connection loss instead of this error if the CID was (re)allocated prior to
+143                    // the illegal call.
+144                    error!(id = ch.0, "unknown connection drained");
+145                }
+146            }
+147        }
+148        None
+149    }
+150
+151    /// Process an incoming UDP datagram
+152    pub fn handle(
+153        &mut self,
+154        now: Instant,
+155        remote: SocketAddr,
+156        local_ip: Option<IpAddr>,
+157        ecn: Option<EcnCodepoint>,
+158        data: BytesMut,
+159        buf: &mut Vec<u8>,
+160    ) -> Option<DatagramEvent> {
+161        // Partially decode packet or short-circuit if unable
+162        let datagram_len = data.len();
+163        let mut event = match PartialDecode::new(
+164            data,
+165            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
+166            &self.config.supported_versions,
+167            self.config.grease_quic_bit,
+168        ) {
+169            Ok((first_decode, remaining)) => DatagramConnectionEvent {
+170                now,
+171                remote,
+172                path_id: PathId::ZERO, // Corrected later for existing paths
+173                ecn,
+174                first_decode,
+175                remaining,
+176            },
+177            Err(PacketDecodeError::UnsupportedVersion {
+178                src_cid,
+179                dst_cid,
+180                version,
+181            }) => {
+182                if self.server_config.is_none() {
+183                    debug!("dropping packet with unsupported version");
+184                    return None;
+185                }
+186                trace!("sending version negotiation");
+187                // Negotiate versions
+188                Header::VersionNegotiate {
+189                    random: self.rng.random::<u8>() | 0x40,
+190                    src_cid: dst_cid,
+191                    dst_cid: src_cid,
+192                }
+193                .encode(buf);
+194                // Grease with a reserved version
+195                buf.write::<u32>(match version {
+196                    0x0a1a_2a3a => 0x0a1a_2a4a,
+197                    _ => 0x0a1a_2a3a,
+198                });
+199                for &version in &self.config.supported_versions {
+200                    buf.write(version);
+201                }
+202                return Some(DatagramEvent::Response(Transmit {
+203                    destination: remote,
+204                    ecn: None,
+205                    size: buf.len(),
+206                    segment_size: None,
+207                    src_ip: local_ip,
+208                }));
+209            }
+210            Err(e) => {
+211                trace!("malformed header: {}", e);
+212                return None;
+213            }
+214        };
+215
+216        let addresses = FourTuple { remote, local_ip };
+217        let dst_cid = event.first_decode.dst_cid();
+218
+219        if let Some(route_to) = self.index.get(&addresses, &event.first_decode) {
+220            event.path_id = match route_to {
+221                RouteDatagramTo::Incoming(_) => PathId::ZERO,
+222                RouteDatagramTo::Connection(_, path_id) => path_id,
+223            };
+224            match route_to {
+225                RouteDatagramTo::Incoming(incoming_idx) => {
+226                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
+227                    let config = &self.server_config.as_ref().unwrap();
+228
+229                    if incoming_buffer
+230                        .total_bytes
+231                        .checked_add(datagram_len as u64)
+232                        .is_some_and(|n| n <= config.incoming_buffer_size)
+233                        && self
+234                            .all_incoming_buffers_total_bytes
+235                            .checked_add(datagram_len as u64)
+236                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
+237                    {
+238                        incoming_buffer.datagrams.push(event);
+239                        incoming_buffer.total_bytes += datagram_len as u64;
+240                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
+241                    }
+242
+243                    None
+244                }
+245                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
+246                    ch,
+247                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
+248                )),
+249            }
+250        } else if event.first_decode.initial_header().is_some() {
+251            // Potentially create a new connection
+252
+253            self.handle_first_packet(datagram_len, event, addresses, buf)
+254        } else if event.first_decode.has_long_header() {
+255            debug!(
+256                "ignoring non-initial packet for unknown connection {}",
+257                dst_cid
+258            );
+259            None
+260        } else if !event.first_decode.is_initial()
+261            && self.local_cid_generator.validate(dst_cid).is_err()
+262        {
+263            debug!("dropping packet with invalid CID");
+264            None
+265        } else if dst_cid.is_empty() {
+266            trace!("dropping unrecognized short packet without ID");
+267            None
+268        } else {
+269            // If we got this far, we're receiving a seemingly valid packet for an unknown
+270            // connection. Send a stateless reset if possible.
+271            self.stateless_reset(now, datagram_len, addresses, dst_cid, buf)
+272                .map(DatagramEvent::Response)
+273        }
+274    }
+275
+276    /// Builds a stateless reset packet to respond with
+277    fn stateless_reset(
+278        &mut self,
+279        now: Instant,
+280        inciting_dgram_len: usize,
+281        addresses: FourTuple,
+282        dst_cid: ConnectionId,
+283        buf: &mut Vec<u8>,
+284    ) -> Option<Transmit> {
+285        if self
+286            .last_stateless_reset
+287            .is_some_and(|last| last + self.config.min_reset_interval > now)
+288        {
+289            debug!("ignoring unexpected packet within minimum stateless reset interval");
+290            return None;
+291        }
+292
+293        /// Minimum amount of padding for the stateless reset to look like a short-header packet
+294        const MIN_PADDING_LEN: usize = 5;
+295
+296        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
+297        // smaller than the inciting packet.
+298        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
+299            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
+300            _ => {
+301                debug!(
+302                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
+303                    inciting_dgram_len
+304                );
+305                return None;
+306            }
+307        };
+308
+309        debug!(
+310            "sending stateless reset for {} to {}",
+311            dst_cid, addresses.remote
+312        );
+313        self.last_stateless_reset = Some(now);
+314        // Resets with at least this much padding can't possibly be distinguished from real packets
+315        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
+316        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
+317            max_padding_len
+318        } else {
+319            self.rng
+320                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
+321        };
+322        buf.reserve(padding_len + RESET_TOKEN_SIZE);
+323        buf.resize(padding_len, 0);
+324        self.rng.fill_bytes(&mut buf[0..padding_len]);
+325        buf[0] = 0b0100_0000 | (buf[0] >> 2);
+326        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
+327
+328        debug_assert!(buf.len() < inciting_dgram_len);
+329
+330        Some(Transmit {
+331            destination: addresses.remote,
+332            ecn: None,
+333            size: buf.len(),
+334            segment_size: None,
+335            src_ip: addresses.local_ip,
+336        })
+337    }
+338
+339    /// Initiate a connection
+340    pub fn connect(
+341        &mut self,
+342        now: Instant,
+343        config: ClientConfig,
+344        remote: SocketAddr,
+345        server_name: &str,
+346    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
+347        if self.cids_exhausted() {
+348            return Err(ConnectError::CidsExhausted);
+349        }
+350        if remote.port() == 0 || remote.ip().is_unspecified() {
+351            return Err(ConnectError::InvalidRemoteAddress(remote));
+352        }
+353        if !self.config.supported_versions.contains(&config.version) {
+354            return Err(ConnectError::UnsupportedVersion);
+355        }
+356
+357        let remote_id = (config.initial_dst_cid_provider)();
+358        trace!(initial_dcid = %remote_id);
+359
+360        let ch = ConnectionHandle(self.connections.vacant_key());
+361        let loc_cid = self.new_cid(ch, PathId::ZERO);
+362        let params = TransportParameters::new(
+363            &config.transport,
+364            &self.config,
+365            self.local_cid_generator.as_ref(),
+366            loc_cid,
+367            None,
+368            &mut self.rng,
+369        );
+370        let tls = config
+371            .crypto
+372            .start_session(config.version, server_name, &params)?;
+373
+374        let conn = self.add_connection(
+375            ch,
+376            config.version,
+377            remote_id,
+378            loc_cid,
+379            remote_id,
+380            FourTuple {
+381                remote,
+382                local_ip: None,
+383            },
+384            now,
+385            tls,
+386            config.transport,
+387            SideArgs::Client {
+388                token_store: config.token_store,
+389                server_name: server_name.into(),
+390            },
+391            &params,
+392        );
+393        Ok((ch, conn))
+394    }
+395
+396    /// Generates new CIDs and creates message to send to the connection state
+397    fn send_new_identifiers(
+398        &mut self,
+399        path_id: PathId,
+400        now: Instant,
+401        ch: ConnectionHandle,
+402        num: u64,
+403    ) -> ConnectionEvent {
+404        let mut ids = vec![];
+405        for _ in 0..num {
+406            let id = self.new_cid(ch, path_id);
+407            let cid_meta = self.connections[ch].loc_cids.entry(path_id).or_default();
+408            let sequence = cid_meta.issued;
+409            cid_meta.issued += 1;
+410            cid_meta.cids.insert(sequence, id);
+411            ids.push(IssuedCid {
+412                path_id,
+413                sequence,
+414                id,
+415                reset_token: ResetToken::new(&*self.config.reset_key, id),
+416            });
+417        }
+418        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
+419            ids,
+420            now,
+421            self.local_cid_generator.cid_len(),
+422            self.local_cid_generator.cid_lifetime(),
+423        ))
+424    }
+425
+426    /// Generate a connection ID for `ch`
+427    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
+428        loop {
+429            let cid = self.local_cid_generator.generate_cid();
+430            if cid.is_empty() {
+431                // Zero-length CID; nothing to track
+432                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
+433                return cid;
+434            }
+435            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
+436                e.insert((ch, path_id));
+437                break cid;
+438            }
+439        }
+440    }
+441
+442    fn handle_first_packet(
+443        &mut self,
+444        datagram_len: usize,
+445        event: DatagramConnectionEvent,
+446        addresses: FourTuple,
+447        buf: &mut Vec<u8>,
+448    ) -> Option<DatagramEvent> {
+449        let dst_cid = event.first_decode.dst_cid();
+450        let header = event.first_decode.initial_header().unwrap();
+451
+452        let Some(server_config) = &self.server_config else {
+453            debug!("packet for unrecognized connection {}", dst_cid);
+454            return self
+455                .stateless_reset(event.now, datagram_len, addresses, dst_cid, buf)
+456                .map(DatagramEvent::Response);
+457        };
+458
+459        if datagram_len < MIN_INITIAL_SIZE as usize {
+460            debug!("ignoring short initial for connection {}", dst_cid);
+461            return None;
+462        }
+463
+464        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
+465            Ok(keys) => keys,
+466            Err(UnsupportedVersion) => {
+467                // This probably indicates that the user set supported_versions incorrectly in
+468                // `EndpointConfig`.
+469                debug!(
+470                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
+471                    header.version
+472                );
+473                return None;
+474            }
+475        };
+476
+477        if let Err(reason) = self.early_validate_first_packet(header) {
+478            return Some(DatagramEvent::Response(self.initial_close(
+479                header.version,
+480                addresses,
+481                &crypto,
+482                header.src_cid,
+483                reason,
+484                buf,
+485            )));
+486        }
+487
+488        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
+489            Ok(packet) => packet,
+490            Err(e) => {
+491                trace!("unable to decode initial packet: {}", e);
+492                return None;
+493            }
+494        };
+495
+496        if !packet.reserved_bits_valid() {
+497            debug!("dropping connection attempt with invalid reserved bits");
+498            return None;
+499        }
+500
+501        let Header::Initial(header) = packet.header else {
+502            panic!("non-initial packet in handle_first_packet()");
+503        };
+504
+505        let server_config = self.server_config.as_ref().unwrap().clone();
+506
+507        let token = match IncomingToken::from_header(&header, &server_config, addresses.remote) {
+508            Ok(token) => token,
+509            Err(InvalidRetryTokenError) => {
+510                debug!("rejecting invalid retry token");
+511                return Some(DatagramEvent::Response(self.initial_close(
+512                    header.version,
+513                    addresses,
+514                    &crypto,
+515                    header.src_cid,
+516                    TransportError::INVALID_TOKEN(""),
+517                    buf,
+518                )));
+519            }
+520        };
+521
+522        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
+523        self.index
+524            .insert_initial_incoming(header.dst_cid, incoming_idx);
+525
+526        Some(DatagramEvent::NewConnection(Incoming {
+527            received_at: event.now,
+528            addresses,
+529            ecn: event.ecn,
+530            packet: InitialPacket {
+531                header,
+532                header_data: packet.header_data,
+533                payload: packet.payload,
+534            },
+535            rest: event.remaining,
+536            crypto,
+537            token,
+538            incoming_idx,
+539            improper_drop_warner: IncomingImproperDropWarner,
+540        }))
+541    }
+542
+543    /// Attempt to accept this incoming connection (an error may still occur)
+544    // box err to avoid clippy::result_large_err
+545    pub fn accept(
+546        &mut self,
+547        mut incoming: Incoming,
+548        now: Instant,
+549        buf: &mut Vec<u8>,
+550        server_config: Option<Arc<ServerConfig>>,
+551    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
+552        let remote_address_validated = incoming.remote_address_validated();
+553        incoming.improper_drop_warner.dismiss();
+554        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+555        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+556
+557        let packet_number = incoming.packet.header.number.expand(0);
+558        let InitialHeader {
+559            src_cid,
+560            dst_cid,
+561            version,
+562            ..
+563        } = incoming.packet.header;
+564        let server_config =
+565            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
+566
+567        if server_config
+568            .transport
+569            .max_idle_timeout
+570            .is_some_and(|timeout| {
+571                incoming.received_at + Duration::from_millis(timeout.into()) <= now
+572            })
+573        {
+574            debug!("abandoning accept of stale initial");
+575            self.index.remove_initial(dst_cid);
+576            return Err(Box::new(AcceptError {
+577                cause: ConnectionError::TimedOut,
+578                response: None,
+579            }));
+580        }
+581
+582        if self.cids_exhausted() {
+583            debug!("refusing connection");
+584            self.index.remove_initial(dst_cid);
+585            return Err(Box::new(AcceptError {
+586                cause: ConnectionError::CidsExhausted,
+587                response: Some(self.initial_close(
+588                    version,
+589                    incoming.addresses,
+590                    &incoming.crypto,
+591                    src_cid,
+592                    TransportError::CONNECTION_REFUSED(""),
+593                    buf,
+594                )),
+595            }));
+596        }
+597
+598        if incoming
+599            .crypto
+600            .packet
+601            .remote
+602            .decrypt(
+603                PathId::ZERO,
+604                packet_number,
+605                &incoming.packet.header_data,
+606                &mut incoming.packet.payload,
+607            )
+608            .is_err()
+609        {
+610            debug!(packet_number, "failed to authenticate initial packet");
+611            self.index.remove_initial(dst_cid);
+612            return Err(Box::new(AcceptError {
+613                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
+614                response: None,
+615            }));
+616        };
+617
+618        let ch = ConnectionHandle(self.connections.vacant_key());
+619        let loc_cid = self.new_cid(ch, PathId::ZERO);
+620        let mut params = TransportParameters::new(
+621            &server_config.transport,
+622            &self.config,
+623            self.local_cid_generator.as_ref(),
+624            loc_cid,
+625            Some(&server_config),
+626            &mut self.rng,
+627        );
+628        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, loc_cid));
+629        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
+630        params.retry_src_cid = incoming.token.retry_src_cid;
+631        let mut pref_addr_cid = None;
+632        if server_config.has_preferred_address() {
+633            let cid = self.new_cid(ch, PathId::ZERO);
+634            pref_addr_cid = Some(cid);
+635            params.preferred_address = Some(PreferredAddress {
+636                address_v4: server_config.preferred_address_v4,
+637                address_v6: server_config.preferred_address_v6,
+638                connection_id: cid,
+639                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
+640            });
+641        }
+642
+643        let tls = server_config.crypto.clone().start_session(version, &params);
+644        let transport_config = server_config.transport.clone();
+645        let mut conn = self.add_connection(
+646            ch,
+647            version,
+648            dst_cid,
+649            loc_cid,
+650            src_cid,
+651            incoming.addresses,
+652            incoming.received_at,
+653            tls,
+654            transport_config,
+655            SideArgs::Server {
+656                server_config,
+657                pref_addr_cid,
+658                path_validated: remote_address_validated,
+659            },
+660            &params,
+661        );
+662        self.index.insert_initial(dst_cid, ch);
+663
+664        match conn.handle_first_packet(
+665            incoming.received_at,
+666            incoming.addresses.remote,
+667            incoming.ecn,
+668            packet_number,
+669            incoming.packet,
+670            incoming.rest,
+671        ) {
+672            Ok(()) => {
+673                trace!(id = ch.0, icid = %dst_cid, "new connection");
+674
+675                for event in incoming_buffer.datagrams {
+676                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
+677                }
+678
+679                Ok((ch, conn))
+680            }
+681            Err(e) => {
+682                debug!("handshake failed: {}", e);
+683                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
+684                let response = match e {
+685                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
+686                        version,
+687                        incoming.addresses,
+688                        &incoming.crypto,
+689                        src_cid,
+690                        e.clone(),
+691                        buf,
+692                    )),
+693                    _ => None,
+694                };
+695                Err(Box::new(AcceptError { cause: e, response }))
+696            }
+697        }
+698    }
+699
+700    /// Check if we should refuse a connection attempt regardless of the packet's contents
+701    fn early_validate_first_packet(
+702        &mut self,
+703        header: &ProtectedInitialHeader,
+704    ) -> Result<(), TransportError> {
+705        let config = &self.server_config.as_ref().unwrap();
+706        if self.cids_exhausted() || self.incoming_buffers.len() >= config.max_incoming {
+707            return Err(TransportError::CONNECTION_REFUSED(""));
+708        }
+709
+710        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
+711        // bytes. If this is a Retry packet, then the length must instead match our usual CID
+712        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
+713        // also need to validate CID length for those after decoding the token.
+714        if header.dst_cid.len() < 8
+715            && (header.token_pos.is_empty()
+716                || header.dst_cid.len() != self.local_cid_generator.cid_len())
+717        {
+718            debug!(
+719                "rejecting connection due to invalid DCID length {}",
+720                header.dst_cid.len()
+721            );
+722            return Err(TransportError::PROTOCOL_VIOLATION(
+723                "invalid destination CID length",
+724            ));
+725        }
+726
+727        Ok(())
+728    }
+729
+730    /// Reject this incoming connection attempt
+731    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
+732        self.clean_up_incoming(&incoming);
+733        incoming.improper_drop_warner.dismiss();
+734
+735        self.initial_close(
+736            incoming.packet.header.version,
+737            incoming.addresses,
+738            &incoming.crypto,
+739            incoming.packet.header.src_cid,
+740            TransportError::CONNECTION_REFUSED(""),
+741            buf,
+742        )
+743    }
+744
+745    /// Respond with a retry packet, requiring the client to retry with address validation
+746    ///
+747    /// Errors if `incoming.may_retry()` is false.
+748    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
+749        if !incoming.may_retry() {
+750            return Err(RetryError(Box::new(incoming)));
+751        }
+752
+753        self.clean_up_incoming(&incoming);
+754        incoming.improper_drop_warner.dismiss();
+755
+756        let server_config = self.server_config.as_ref().unwrap();
+757
+758        // First Initial
+759        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
+760        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
+761        // with established connections. In the unlikely event that a collision occurs
+762        // between two connections in the initial phase, both will fail fast and may be
+763        // retried by the application layer.
+764        let loc_cid = self.local_cid_generator.generate_cid();
+765
+766        let payload = TokenPayload::Retry {
+767            address: incoming.addresses.remote,
+768            orig_dst_cid: incoming.packet.header.dst_cid,
+769            issued: server_config.time_source.now(),
+770        };
+771        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
+772
+773        let header = Header::Retry {
+774            src_cid: loc_cid,
+775            dst_cid: incoming.packet.header.src_cid,
+776            version: incoming.packet.header.version,
+777        };
+778
+779        let encode = header.encode(buf);
+780        buf.put_slice(&token);
+781        buf.extend_from_slice(&server_config.crypto.retry_tag(
+782            incoming.packet.header.version,
+783            incoming.packet.header.dst_cid,
+784            buf,
+785        ));
+786        encode.finish(buf, &*incoming.crypto.header.local, None);
+787
+788        Ok(Transmit {
+789            destination: incoming.addresses.remote,
+790            ecn: None,
+791            size: buf.len(),
+792            segment_size: None,
+793            src_ip: incoming.addresses.local_ip,
+794        })
+795    }
+796
+797    /// Ignore this incoming connection attempt, not sending any packet in response
+798    ///
+799    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
+800    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
+801    pub fn ignore(&mut self, incoming: Incoming) {
+802        self.clean_up_incoming(&incoming);
+803        incoming.improper_drop_warner.dismiss();
+804    }
+805
+806    /// Clean up endpoint data structures associated with an `Incoming`.
+807    fn clean_up_incoming(&mut self, incoming: &Incoming) {
+808        self.index.remove_initial(incoming.packet.header.dst_cid);
+809        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
+810        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
+811    }
+812
+813    fn add_connection(
+814        &mut self,
+815        ch: ConnectionHandle,
+816        version: u32,
+817        init_cid: ConnectionId,
+818        loc_cid: ConnectionId,
+819        rem_cid: ConnectionId,
+820        addresses: FourTuple,
+821        now: Instant,
+822        tls: Box<dyn crypto::Session>,
+823        transport_config: Arc<TransportConfig>,
+824        side_args: SideArgs,
+825        // Only used for qlog.
+826        params: &TransportParameters,
+827    ) -> Connection {
+828        let mut rng_seed = [0; 32];
+829        self.rng.fill_bytes(&mut rng_seed);
+830        let side = side_args.side();
+831        let pref_addr_cid = side_args.pref_addr_cid();
+832
+833        let qlog =
+834            transport_config.create_qlog_sink(side_args.side(), addresses.remote, init_cid, now);
+835
+836        qlog.emit_connection_started(
+837            now,
+838            loc_cid,
+839            rem_cid,
+840            addresses.remote,
+841            addresses.local_ip,
+842            params,
+843        );
+844
+845        let conn = Connection::new(
+846            self.config.clone(),
+847            transport_config,
+848            init_cid,
+849            loc_cid,
+850            rem_cid,
+851            addresses.remote,
+852            addresses.local_ip,
+853            tls,
+854            self.local_cid_generator.as_ref(),
+855            now,
+856            version,
+857            self.allow_mtud,
+858            rng_seed,
+859            side_args,
+860            qlog,
+861        );
+862
+863        let mut path_cids = PathLocalCids::default();
+864        path_cids.cids.insert(path_cids.issued, loc_cid);
+865        path_cids.issued += 1;
+866
+867        if let Some(cid) = pref_addr_cid {
+868            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
+869            path_cids.cids.insert(path_cids.issued, cid);
+870            path_cids.issued += 1;
+871        }
+872
+873        let id = self.connections.insert(ConnectionMeta {
+874            init_cid,
+875            loc_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
+876            addresses,
+877            side,
+878            reset_token: Default::default(),
+879        });
+880        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
+881
+882        self.index.insert_conn(addresses, loc_cid, ch, side);
+883
+884        conn
+885    }
+886
+887    fn initial_close(
+888        &mut self,
+889        version: u32,
+890        addresses: FourTuple,
+891        crypto: &Keys,
+892        remote_id: ConnectionId,
+893        reason: TransportError,
+894        buf: &mut Vec<u8>,
+895    ) -> Transmit {
+896        // We don't need to worry about CID collisions in initial closes because the peer
+897        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
+898        // unexpected response.
+899        let local_id = self.local_cid_generator.generate_cid();
+900        let number = PacketNumber::U8(0);
+901        let header = Header::Initial(InitialHeader {
+902            dst_cid: remote_id,
+903            src_cid: local_id,
+904            number,
+905            token: Bytes::new(),
+906            version,
+907        });
+908
+909        let partial_encode = header.encode(buf);
+910        let max_len =
+911            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
+912        frame::Close::from(reason).encode(buf, max_len);
+913        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
+914        partial_encode.finish(
+915            buf,
+916            &*crypto.header.local,
+917            Some((0, Default::default(), &*crypto.packet.local)),
+918        );
+919        Transmit {
+920            destination: addresses.remote,
+921            ecn: None,
+922            size: buf.len(),
+923            segment_size: None,
+924            src_ip: addresses.local_ip,
+925        }
+926    }
+927
+928    /// Access the configuration used by this endpoint
+929    pub fn config(&self) -> &EndpointConfig {
+930        &self.config
+931    }
+932
+933    /// Number of connections that are currently open
+934    pub fn open_connections(&self) -> usize {
+935        self.connections.len()
+936    }
+937
+938    /// Counter for the number of bytes currently used
+939    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
+940    pub fn incoming_buffer_bytes(&self) -> u64 {
+941        self.all_incoming_buffers_total_bytes
+942    }
+943
+944    #[cfg(test)]
+945    pub(crate) fn known_connections(&self) -> usize {
+946        let x = self.connections.len();
+947        debug_assert_eq!(x, self.index.connection_ids_initial.len());
+948        // Not all connections have known reset tokens
+949        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
+950        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
+951        debug_assert!(x >= self.index.incoming_connection_remotes.len());
+952        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
+953        x
+954    }
+955
+956    #[cfg(test)]
+957    pub(crate) fn known_cids(&self) -> usize {
+958        self.index.connection_ids.len()
+959    }
+960
+961    /// Whether we've used up 3/4 of the available CID space
+962    ///
+963    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
+964    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
+965    fn cids_exhausted(&self) -> bool {
+966        self.local_cid_generator.cid_len() <= 4
+967            && self.local_cid_generator.cid_len() != 0
+968            && (2usize.pow(self.local_cid_generator.cid_len() as u32 * 8)
+969                - self.index.connection_ids.len())
+970                < 2usize.pow(self.local_cid_generator.cid_len() as u32 * 8 - 2)
+971    }
+972}
+973
+974impl fmt::Debug for Endpoint {
+975    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+976        fmt.debug_struct("Endpoint")
+977            .field("rng", &self.rng)
+978            .field("index", &self.index)
+979            .field("connections", &self.connections)
+980            .field("config", &self.config)
+981            .field("server_config", &self.server_config)
+982            // incoming_buffers too large
+983            .field("incoming_buffers.len", &self.incoming_buffers.len())
+984            .field(
+985                "all_incoming_buffers_total_bytes",
+986                &self.all_incoming_buffers_total_bytes,
+987            )
+988            .finish()
+989    }
+990}
+991
+992/// Buffered Initial and 0-RTT messages for a pending incoming connection
+993#[derive(Default)]
+994struct IncomingBuffer {
+995    datagrams: Vec<DatagramConnectionEvent>,
+996    total_bytes: u64,
+997}
+998
+999/// Part of protocol state incoming datagrams can be routed to
+1000#[derive(Copy, Clone, Debug)]
+1001enum RouteDatagramTo {
+1002    Incoming(usize),
+1003    Connection(ConnectionHandle, PathId),
+1004}
+1005
+1006/// Maps packets to existing connections
+1007#[derive(Default, Debug)]
+1008struct ConnectionIndex {
+1009    /// Identifies connections based on the initial DCID the peer utilized
+1010    ///
+1011    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1012    ///
+1013    /// Used by the server, not the client.
+1014    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
+1015    /// Identifies connections based on locally created CIDs
+1016    ///
+1017    /// Uses a cheaper hash function since keys are locally created
+1018    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
+1019    /// Identifies incoming connections with zero-length CIDs
+1020    ///
+1021    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1022    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
+1023    /// Identifies outgoing connections with zero-length CIDs
+1024    ///
+1025    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
+1026    /// require a unique four-tuple, so at most one client connection with zero-length local CIDs
+1027    /// may be established per remote. We must omit the local address from the key because we don't
+1028    /// necessarily know what address we're sending from, and hence receiving at.
+1029    ///
+1030    /// Uses a standard `HashMap` to protect against hash collision attacks.
+1031    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
+1032    /// Reset tokens provided by the peer for the CID each connection is currently sending to
+1033    ///
+1034    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
+1035    /// recipient, if any.
+1036    connection_reset_tokens: ResetTokenTable,
+1037}
+1038
+1039impl ConnectionIndex {
+1040    /// Associate an incoming connection with its initial destination CID
+1041    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
+1042        if dst_cid.is_empty() {
+1043            return;
+1044        }
+1045        self.connection_ids_initial
+1046            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
+1047    }
+1048
+1049    /// Remove an association with an initial destination CID
+1050    fn remove_initial(&mut self, dst_cid: ConnectionId) {
+1051        if dst_cid.is_empty() {
+1052            return;
+1053        }
+1054        let removed = self.connection_ids_initial.remove(&dst_cid);
+1055        debug_assert!(removed.is_some());
+1056    }
+1057
+1058    /// Associate a connection with its initial destination CID
+1059    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
+1060        if dst_cid.is_empty() {
+1061            return;
+1062        }
+1063        self.connection_ids_initial.insert(
+1064            dst_cid,
+1065            RouteDatagramTo::Connection(connection, PathId::ZERO),
+1066        );
+1067    }
+1068
+1069    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
+1070    /// its current 4-tuple
+1071    fn insert_conn(
+1072        &mut self,
+1073        addresses: FourTuple,
+1074        dst_cid: ConnectionId,
+1075        connection: ConnectionHandle,
+1076        side: Side,
+1077    ) {
+1078        match dst_cid.len() {
+1079            0 => match side {
+1080                Side::Server => {
+1081                    self.incoming_connection_remotes
+1082                        .insert(addresses, connection);
+1083                }
+1084                Side::Client => {
+1085                    self.outgoing_connection_remotes
+1086                        .insert(addresses.remote, connection);
+1087                }
+1088            },
+1089            _ => {
+1090                self.connection_ids
+1091                    .insert(dst_cid, (connection, PathId::ZERO));
+1092            }
+1093        }
+1094    }
+1095
+1096    /// Discard a connection ID
+1097    fn retire(&mut self, dst_cid: ConnectionId) {
+1098        self.connection_ids.remove(&dst_cid);
+1099    }
+1100
+1101    /// Remove all references to a connection
+1102    fn remove(&mut self, conn: &ConnectionMeta) {
+1103        if conn.side.is_server() {
+1104            self.remove_initial(conn.init_cid);
+1105        }
+1106        for cid in conn.loc_cids.values().flat_map(|pcids| pcids.cids.values()) {
+1107            self.connection_ids.remove(cid);
+1108        }
+1109        self.incoming_connection_remotes.remove(&conn.addresses);
+1110        self.outgoing_connection_remotes
+1111            .remove(&conn.addresses.remote);
+1112        for (remote, token) in conn.reset_token.values() {
+1113            self.connection_reset_tokens.remove(*remote, *token);
+1114        }
+1115    }
+1116
+1117    /// Find the existing connection that `datagram` should be routed to, if any
+1118    fn get(&self, addresses: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
+1119        if !datagram.dst_cid().is_empty() {
+1120            if let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid()) {
+1121                return Some(RouteDatagramTo::Connection(ch, path_id));
+1122            }
+1123        }
+1124        if datagram.is_initial() || datagram.is_0rtt() {
+1125            if let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid()) {
+1126                return Some(ch);
+1127            }
+1128        }
+1129        if datagram.dst_cid().is_empty() {
+1130            if let Some(&ch) = self.incoming_connection_remotes.get(addresses) {
+1131                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
+1132                // length CIDs.  So this is always PathId::ZERO.
+1133                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1134            }
+1135            if let Some(&ch) = self.outgoing_connection_remotes.get(&addresses.remote) {
+1136                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
+1137                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
+1138            }
+1139        }
+1140        let data = datagram.data();
+1141        if data.len() < RESET_TOKEN_SIZE {
+1142            return None;
+1143        }
+1144        // For stateless resets the PathId is meaningless since it closes the entire
+1145        // connection regardless of path.  So use PathId::ZERO.
+1146        self.connection_reset_tokens
+1147            .get(addresses.remote, &data[data.len() - RESET_TOKEN_SIZE..])
+1148            .cloned()
+1149            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
+1150    }
+1151}
+1152
+1153#[derive(Debug)]
+1154pub(crate) struct ConnectionMeta {
+1155    init_cid: ConnectionId,
+1156    /// Locally issues CIDs for each path
+1157    loc_cids: FxHashMap<PathId, PathLocalCids>,
+1158    /// Remote/local addresses the connection began with
+1159    ///
+1160    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
+1161    /// bother keeping it up to date.
+1162    addresses: FourTuple,
+1163    side: Side,
+1164    /// Reset tokens provided by the peer for CIDs we're currently sending to
+1165    ///
+1166    /// Since each reset token is for a CID, it is also for a fixed remote address which is
+1167    /// also stored. This allows us to look up which reset tokens we might expect from a
+1168    /// given remote address, see [`ResetTokenTable`].
+1169    ///
+1170    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
+1171    /// us to retire the reset token when a path is abandoned.
+1172    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
+1173}
+1174
+1175/// Local connection IDs for a single path
+1176#[derive(Debug, Default)]
+1177struct PathLocalCids {
+1178    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
+1179    ///
+1180    /// Another way of saying this is that this is the next sequence number to be issued.
+1181    issued: u64,
+1182    /// Issues CIDs indexed by their sequence number.
+1183    cids: FxHashMap<u64, ConnectionId>,
+1184}
+1185
+1186/// Internal identifier for a `Connection` currently associated with an endpoint
+1187#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+1188pub struct ConnectionHandle(pub usize);
+1189
+1190impl From<ConnectionHandle> for usize {
+1191    fn from(x: ConnectionHandle) -> Self {
+1192        x.0
+1193    }
+1194}
+1195
+1196impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
+1197    type Output = ConnectionMeta;
+1198    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
+1199        &self[ch.0]
+1200    }
+1201}
+1202
+1203impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
+1204    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
+1205        &mut self[ch.0]
+1206    }
+1207}
+1208
+1209/// Event resulting from processing a single datagram
+1210pub enum DatagramEvent {
+1211    /// The datagram is redirected to its `Connection`
+1212    ConnectionEvent(ConnectionHandle, ConnectionEvent),
+1213    /// The datagram may result in starting a new `Connection`
+1214    NewConnection(Incoming),
+1215    /// Response generated directly by the endpoint
+1216    Response(Transmit),
+1217}
+1218
+1219/// An incoming connection for which the server has not yet begun its part of the handshake.
+1220pub struct Incoming {
+1221    received_at: Instant,
+1222    addresses: FourTuple,
+1223    ecn: Option<EcnCodepoint>,
+1224    packet: InitialPacket,
+1225    rest: Option<BytesMut>,
+1226    crypto: Keys,
+1227    token: IncomingToken,
+1228    incoming_idx: usize,
+1229    improper_drop_warner: IncomingImproperDropWarner,
+1230}
+1231
+1232impl Incoming {
+1233    /// The local IP address which was used when the peer established the connection
+1234    ///
+1235    /// This has the same behavior as [`Connection::local_ip`].
+1236    pub fn local_ip(&self) -> Option<IpAddr> {
+1237        self.addresses.local_ip
+1238    }
+1239
+1240    /// The peer's UDP address
+1241    pub fn remote_address(&self) -> SocketAddr {
+1242        self.addresses.remote
+1243    }
+1244
+1245    /// Whether the socket address that is initiating this connection has been validated
+1246    ///
+1247    /// This means that the sender of the initial packet has proved that they can receive traffic
+1248    /// sent to `self.remote_address()`.
+1249    ///
+1250    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1251    /// The inverse is not guaranteed.
+1252    pub fn remote_address_validated(&self) -> bool {
+1253        self.token.validated
+1254    }
+1255
+1256    /// Whether it is legal to respond with a retry packet
+1257    ///
+1258    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
+1259    /// The inverse is not guaranteed.
+1260    pub fn may_retry(&self) -> bool {
+1261        self.token.retry_src_cid.is_none()
+1262    }
+1263
+1264    /// The original destination connection ID sent by the client
+1265    pub fn orig_dst_cid(&self) -> ConnectionId {
+1266        self.token.orig_dst_cid
+1267    }
+1268}
+1269
+1270impl fmt::Debug for Incoming {
+1271    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+1272        f.debug_struct("Incoming")
+1273            .field("addresses", &self.addresses)
+1274            .field("ecn", &self.ecn)
+1275            // packet doesn't implement debug
+1276            // rest is too big and not meaningful enough
+1277            .field("token", &self.token)
+1278            .field("incoming_idx", &self.incoming_idx)
+1279            // improper drop warner contains no information
+1280            .finish_non_exhaustive()
+1281    }
+1282}
+1283
+1284struct IncomingImproperDropWarner;
+1285
+1286impl IncomingImproperDropWarner {
+1287    fn dismiss(self) {
+1288        mem::forget(self);
+1289    }
+1290}
+1291
+1292impl Drop for IncomingImproperDropWarner {
+1293    fn drop(&mut self) {
+1294        warn!(
+1295            "quinn_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
+1296               (may cause memory leak and eventual inability to accept new connections)"
+1297        );
+1298    }
+1299}
+1300
+1301/// Errors in the parameters being used to create a new connection
+1302///
+1303/// These arise before any I/O has been performed.
+1304#[derive(Debug, Error, Clone, PartialEq, Eq)]
+1305pub enum ConnectError {
+1306    /// The endpoint can no longer create new connections
+1307    ///
+1308    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
+1309    #[error("endpoint stopping")]
+1310    EndpointStopping,
+1311    /// The connection could not be created because not enough of the CID space is available
+1312    ///
+1313    /// Try using longer connection IDs
+1314    #[error("CIDs exhausted")]
+1315    CidsExhausted,
+1316    /// The given server name was malformed
+1317    #[error("invalid server name: {0}")]
+1318    InvalidServerName(String),
+1319    /// The remote [`SocketAddr`] supplied was malformed
+1320    ///
+1321    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
+1322    #[error("invalid remote address: {0}")]
+1323    InvalidRemoteAddress(SocketAddr),
+1324    /// No default client configuration was set up
+1325    ///
+1326    /// Use `Endpoint::connect_with` to specify a client configuration.
+1327    #[error("no default client config")]
+1328    NoDefaultClientConfig,
+1329    /// The local endpoint does not support the QUIC version specified in the client configuration
+1330    #[error("unsupported QUIC version")]
+1331    UnsupportedVersion,
+1332}
+1333
+1334/// Error type for attempting to accept an [`Incoming`]
+1335#[derive(Debug)]
+1336pub struct AcceptError {
+1337    /// Underlying error describing reason for failure
+1338    pub cause: ConnectionError,
+1339    /// Optional response to transmit back
+1340    pub response: Option<Transmit>,
+1341}
+1342
+1343/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
+1344#[derive(Debug, Error)]
+1345#[error("retry() with validated Incoming")]
+1346pub struct RetryError(Box<Incoming>);
+1347
+1348impl RetryError {
+1349    /// Get the [`Incoming`]
+1350    pub fn into_incoming(self) -> Incoming {
+1351        *self.0
+1352    }
+1353}
+1354
+1355/// Reset Tokens which are associated with peer socket addresses
+1356///
+1357/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
+1358/// peer generated and might be usable for hash collision attacks.
+1359#[derive(Default, Debug)]
+1360struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
+1361
+1362impl ResetTokenTable {
+1363    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
+1364        self.0
+1365            .entry(remote)
+1366            .or_default()
+1367            .insert(token, ch)
+1368            .is_some()
+1369    }
+1370
+1371    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
+1372        use std::collections::hash_map::Entry;
+1373        match self.0.entry(remote) {
+1374            Entry::Vacant(_) => {}
+1375            Entry::Occupied(mut e) => {
+1376                e.get_mut().remove(&token);
+1377                if e.get().is_empty() {
+1378                    e.remove_entry();
+1379                }
+1380            }
+1381        }
+1382    }
+1383
+1384    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
+1385        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
+1386        self.0.get(&remote)?.get(&token)
+1387    }
+1388}
+1389
+1390/// Identifies a connection by the combination of remote and local addresses
+1391///
+1392/// Including the local ensures good behavior when the host has multiple IP addresses on the same
+1393/// subnet and zero-length connection IDs are in use.
+1394#[derive(Hash, Eq, PartialEq, Debug, Copy, Clone)]
+1395struct FourTuple {
+1396    remote: SocketAddr,
+1397    // A single socket can only listen on a single port, so no need to store it explicitly
+1398    local_ip: Option<IpAddr>,
+1399}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/frame.rs.html b/pr/234/docs/src/iroh_quinn_proto/frame.rs.html new file mode 100644 index 000000000..db3d55ceb --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/frame.rs.html @@ -0,0 +1,2077 @@ +frame.rs - source

iroh_quinn_proto/
frame.rs

1use std::{
+2    fmt::{self, Write},
+3    mem,
+4    net::{IpAddr, SocketAddr},
+5    ops::{Range, RangeInclusive},
+6};
+7
+8use bytes::{Buf, BufMut, Bytes};
+9use tinyvec::TinyVec;
+10
+11use crate::{
+12    Dir, MAX_CID_SIZE, RESET_TOKEN_SIZE, ResetToken, StreamId, TransportError, TransportErrorCode,
+13    VarInt,
+14    coding::{self, BufExt, BufMutExt, UnexpectedEnd},
+15    connection::PathId,
+16    range_set::ArrayRangeSet,
+17    shared::{ConnectionId, EcnCodepoint},
+18};
+19
+20#[cfg(feature = "arbitrary")]
+21use arbitrary::Arbitrary;
+22
+23/// A QUIC frame type
+24#[derive(Copy, Clone, Eq, PartialEq)]
+25pub struct FrameType(u64);
+26
+27impl FrameType {
+28    fn stream(self) -> Option<StreamInfo> {
+29        if STREAM_TYS.contains(&self.0) {
+30            Some(StreamInfo(self.0 as u8))
+31        } else {
+32            None
+33        }
+34    }
+35    fn datagram(self) -> Option<DatagramInfo> {
+36        if DATAGRAM_TYS.contains(&self.0) {
+37            Some(DatagramInfo(self.0 as u8))
+38        } else {
+39            None
+40        }
+41    }
+42}
+43
+44impl coding::Codec for FrameType {
+45    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+46        Ok(Self(buf.get_var()?))
+47    }
+48    fn encode<B: BufMut>(&self, buf: &mut B) {
+49        buf.write_var(self.0);
+50    }
+51}
+52
+53pub(crate) trait FrameStruct {
+54    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+55    const SIZE_BOUND: usize;
+56}
+57
+58macro_rules! frame_types {
+59    {$($name:ident = $val:expr,)*} => {
+60        impl FrameType {
+61            $(pub(crate) const $name: FrameType = FrameType($val);)*
+62        }
+63
+64        impl fmt::Debug for FrameType {
+65            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+66                match self.0 {
+67                    $($val => f.write_str(stringify!($name)),)*
+68                    _ => write!(f, "Type({:02x})", self.0)
+69                }
+70            }
+71        }
+72
+73        impl fmt::Display for FrameType {
+74            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+75                match self.0 {
+76                    $($val => f.write_str(stringify!($name)),)*
+77                    x if STREAM_TYS.contains(&x) => f.write_str("STREAM"),
+78                    x if DATAGRAM_TYS.contains(&x) => f.write_str("DATAGRAM"),
+79                    _ => write!(f, "<unknown {:02x}>", self.0),
+80                }
+81            }
+82        }
+83    }
+84}
+85
+86#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+87struct StreamInfo(u8);
+88
+89impl StreamInfo {
+90    fn fin(self) -> bool {
+91        self.0 & 0x01 != 0
+92    }
+93    fn len(self) -> bool {
+94        self.0 & 0x02 != 0
+95    }
+96    fn off(self) -> bool {
+97        self.0 & 0x04 != 0
+98    }
+99}
+100
+101#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+102struct DatagramInfo(u8);
+103
+104impl DatagramInfo {
+105    fn len(self) -> bool {
+106        self.0 & 0x01 != 0
+107    }
+108}
+109
+110frame_types! {
+111    PADDING = 0x00,
+112    PING = 0x01,
+113    ACK = 0x02,
+114    ACK_ECN = 0x03,
+115    RESET_STREAM = 0x04,
+116    STOP_SENDING = 0x05,
+117    CRYPTO = 0x06,
+118    NEW_TOKEN = 0x07,
+119    // STREAM
+120    MAX_DATA = 0x10,
+121    MAX_STREAM_DATA = 0x11,
+122    MAX_STREAMS_BIDI = 0x12,
+123    MAX_STREAMS_UNI = 0x13,
+124    DATA_BLOCKED = 0x14,
+125    STREAM_DATA_BLOCKED = 0x15,
+126    STREAMS_BLOCKED_BIDI = 0x16,
+127    STREAMS_BLOCKED_UNI = 0x17,
+128    NEW_CONNECTION_ID = 0x18,
+129    RETIRE_CONNECTION_ID = 0x19,
+130    PATH_CHALLENGE = 0x1a,
+131    PATH_RESPONSE = 0x1b,
+132    CONNECTION_CLOSE = 0x1c,
+133    APPLICATION_CLOSE = 0x1d,
+134    HANDSHAKE_DONE = 0x1e,
+135    // ACK Frequency
+136    ACK_FREQUENCY = 0xaf,
+137    IMMEDIATE_ACK = 0x1f,
+138    // DATAGRAM
+139    // ADDRESS DISCOVERY REPORT
+140    OBSERVED_IPV4_ADDR = 0x9f81a6,
+141    OBSERVED_IPV6_ADDR = 0x9f81a7,
+142    // Multipath
+143    PATH_ACK = 0x15228c00,
+144    PATH_ACK_ECN = 0x15228c01,
+145    PATH_ABANDON = 0x15228c05,
+146    PATH_STATUS_BACKUP = 0x15228c07,
+147    PATH_STATUS_AVAILABLE = 0x15228c08,
+148    PATH_NEW_CONNECTION_ID = 0x15228c09,
+149    PATH_RETIRE_CONNECTION_ID = 0x15228c0a,
+150    MAX_PATH_ID = 0x15228c0c,
+151    PATHS_BLOCKED = 0x15228c0d,
+152    PATH_CIDS_BLOCKED = 0x15228c0e,
+153    // IROH'S NAT TRAVERSAL
+154    ADD_IPV4_ADDRESS = 0x3d7f90,
+155    ADD_IPV6_ADDRESS = 0x3d7f91,
+156    REACH_OUT_AT_IPV4 = 0x3d7f92,
+157    REACH_OUT_AT_IPV6 = 0x3d7f93,
+158    REMOVE_ADDRESS = 0x3d7f94,
+159}
+160
+161const STREAM_TYS: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+162const DATAGRAM_TYS: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+163
+164#[derive(Debug)]
+165pub(crate) enum Frame {
+166    Padding,
+167    Ping,
+168    Ack(Ack),
+169    PathAck(PathAck),
+170    ResetStream(ResetStream),
+171    StopSending(StopSending),
+172    Crypto(Crypto),
+173    NewToken(NewToken),
+174    Stream(Stream),
+175    MaxData(VarInt),
+176    MaxStreamData { id: StreamId, offset: u64 },
+177    MaxStreams { dir: Dir, count: u64 },
+178    DataBlocked { offset: u64 },
+179    StreamDataBlocked { id: StreamId, offset: u64 },
+180    StreamsBlocked { dir: Dir, limit: u64 },
+181    NewConnectionId(NewConnectionId),
+182    RetireConnectionId(RetireConnectionId),
+183    PathChallenge(PathChallenge),
+184    PathResponse(PathResponse),
+185    Close(Close),
+186    Datagram(Datagram),
+187    AckFrequency(AckFrequency),
+188    ImmediateAck,
+189    HandshakeDone,
+190    ObservedAddr(ObservedAddr),
+191    PathAbandon(PathAbandon),
+192    PathStatusAvailable(PathStatusAvailable),
+193    PathStatusBackup(PathStatusBackup),
+194    MaxPathId(MaxPathId),
+195    PathsBlocked(PathsBlocked),
+196    PathCidsBlocked(PathCidsBlocked),
+197    AddAddress(AddAddress),
+198    ReachOut(ReachOut),
+199    RemoveAddress(RemoveAddress),
+200}
+201
+202impl Frame {
+203    pub(crate) fn ty(&self) -> FrameType {
+204        use Frame::*;
+205        match *self {
+206            Padding => FrameType::PADDING,
+207            ResetStream(_) => FrameType::RESET_STREAM,
+208            Close(self::Close::Connection(_)) => FrameType::CONNECTION_CLOSE,
+209            Close(self::Close::Application(_)) => FrameType::APPLICATION_CLOSE,
+210            MaxData(_) => FrameType::MAX_DATA,
+211            MaxStreamData { .. } => FrameType::MAX_STREAM_DATA,
+212            MaxStreams { dir: Dir::Bi, .. } => FrameType::MAX_STREAMS_BIDI,
+213            MaxStreams { dir: Dir::Uni, .. } => FrameType::MAX_STREAMS_UNI,
+214            Ping => FrameType::PING,
+215            DataBlocked { .. } => FrameType::DATA_BLOCKED,
+216            StreamDataBlocked { .. } => FrameType::STREAM_DATA_BLOCKED,
+217            StreamsBlocked { dir: Dir::Bi, .. } => FrameType::STREAMS_BLOCKED_BIDI,
+218            StreamsBlocked { dir: Dir::Uni, .. } => FrameType::STREAMS_BLOCKED_UNI,
+219            StopSending { .. } => FrameType::STOP_SENDING,
+220            RetireConnectionId { .. } => FrameType::RETIRE_CONNECTION_ID,
+221            Ack(_) => FrameType::ACK,
+222            PathAck(_) => FrameType::PATH_ACK,
+223            Stream(ref x) => {
+224                let mut ty = *STREAM_TYS.start();
+225                if x.fin {
+226                    ty |= 0x01;
+227                }
+228                if x.offset != 0 {
+229                    ty |= 0x04;
+230                }
+231                FrameType(ty)
+232            }
+233            PathChallenge(_) => FrameType::PATH_CHALLENGE,
+234            PathResponse(_) => FrameType::PATH_RESPONSE,
+235            NewConnectionId(cid) => cid.get_type(),
+236            Crypto(_) => FrameType::CRYPTO,
+237            NewToken(_) => FrameType::NEW_TOKEN,
+238            Datagram(_) => FrameType(*DATAGRAM_TYS.start()),
+239            AckFrequency(_) => FrameType::ACK_FREQUENCY,
+240            ImmediateAck => FrameType::IMMEDIATE_ACK,
+241            HandshakeDone => FrameType::HANDSHAKE_DONE,
+242            ObservedAddr(ref observed) => observed.get_type(),
+243            PathAbandon(_) => FrameType::PATH_ABANDON,
+244            PathStatusAvailable(_) => FrameType::PATH_STATUS_AVAILABLE,
+245            PathStatusBackup(_) => FrameType::PATH_STATUS_BACKUP,
+246            MaxPathId(_) => FrameType::MAX_PATH_ID,
+247            PathsBlocked(_) => FrameType::PATHS_BLOCKED,
+248            PathCidsBlocked(_) => FrameType::PATH_CIDS_BLOCKED,
+249            AddAddress(ref frame) => frame.get_type(),
+250            ReachOut(ref frame) => frame.get_type(),
+251            RemoveAddress(_) => self::RemoveAddress::TYPE,
+252        }
+253    }
+254
+255    pub(crate) fn is_ack_eliciting(&self) -> bool {
+256        !matches!(
+257            *self,
+258            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+259        )
+260    }
+261
+262    /// Returns `true` if this frame MUST be sent in 1-RTT space
+263    pub(crate) fn is_1rtt(&self) -> bool {
+264        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+265        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+266        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+267        // > connection with an error of type PROTOCOL_VIOLATION.
+268
+269        self.is_multipath_frame() || self.is_qad_frame()
+270    }
+271
+272    fn is_qad_frame(&self) -> bool {
+273        matches!(*self, Self::ObservedAddr(_))
+274    }
+275
+276    fn is_multipath_frame(&self) -> bool {
+277        matches!(
+278            *self,
+279            Self::PathAck(_)
+280                | Self::PathAbandon(_)
+281                | Self::PathStatusBackup(_)
+282                | Self::PathStatusAvailable(_)
+283                | Self::MaxPathId(_)
+284                | Self::PathsBlocked(_)
+285                | Self::PathCidsBlocked(_)
+286                | Self::NewConnectionId(NewConnectionId {
+287                    path_id: Some(_),
+288                    ..
+289                })
+290                | Self::RetireConnectionId(RetireConnectionId {
+291                    path_id: Some(_),
+292                    ..
+293                })
+294        )
+295    }
+296}
+297
+298#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+299pub(crate) struct PathChallenge(#[display("PATH_CHALLENGE({:08x})")] pub(crate) u64);
+300
+301impl PathChallenge {
+302    pub(crate) const SIZE_BOUND: usize = 9;
+303}
+304impl coding::Codec for PathChallenge {
+305    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+306    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+307        Ok(Self(buf.get()?))
+308    }
+309
+310    /// Encode [`Self`] into the given buffer
+311    fn encode<B: BufMut>(&self, buf: &mut B) {
+312        buf.write(FrameType::PATH_CHALLENGE);
+313        buf.write(self.0);
+314    }
+315}
+316
+317#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+318pub(crate) struct PathResponse(#[display("PATH_RESPONSE({:08x})")] pub(crate) u64);
+319
+320impl PathResponse {
+321    pub(crate) const SIZE_BOUND: usize = 9;
+322}
+323
+324impl coding::Codec for PathResponse {
+325    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+326    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+327        Ok(Self(buf.get()?))
+328    }
+329
+330    /// Encode [`Self`] into the given buffer
+331    fn encode<B: BufMut>(&self, buf: &mut B) {
+332        buf.write(FrameType::PATH_RESPONSE);
+333        buf.write(self.0);
+334    }
+335}
+336
+337#[derive(Debug, PartialEq, Eq)]
+338pub(crate) struct RetireConnectionId {
+339    pub(crate) path_id: Option<PathId>,
+340    pub(crate) sequence: u64,
+341}
+342
+343impl RetireConnectionId {
+344    /// Maximum size of this frame when the frame type is [`FrameType::RETIRE_CONNECTION_ID`]
+345    pub(crate) const SIZE_BOUND: usize = {
+346        let type_len = VarInt(FrameType::RETIRE_CONNECTION_ID.0).size();
+347        let seq_max_len = 8usize;
+348        type_len + seq_max_len
+349    };
+350
+351    /// Maximum size of this frame when the frame type is [`FrameType::PATH_RETIRE_CONNECTION_ID`]
+352    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+353        let type_len = VarInt(FrameType::PATH_RETIRE_CONNECTION_ID.0).size();
+354        let path_id_len = VarInt::from_u32(u32::MAX).size();
+355        let seq_max_len = 8usize;
+356        type_len + path_id_len + seq_max_len
+357    };
+358
+359    /// Encode [`Self`] into the given buffer
+360    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+361        buf.write(self.get_type());
+362        if let Some(id) = self.path_id {
+363            buf.write(id);
+364        }
+365        buf.write_var(self.sequence);
+366    }
+367
+368    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+369    /// [`FrameType::PATH_RETIRE_CONNECTION_ID`], or [`FrameType::RETIRE_CONNECTION_ID`])
+370    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+371        Ok(Self {
+372            path_id: if read_path { Some(bytes.get()?) } else { None },
+373            sequence: bytes.get_var()?,
+374        })
+375    }
+376
+377    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+378    pub(crate) fn get_type(&self) -> FrameType {
+379        if self.path_id.is_some() {
+380            FrameType::PATH_RETIRE_CONNECTION_ID
+381        } else {
+382            FrameType::RETIRE_CONNECTION_ID
+383        }
+384    }
+385
+386    /// Returns the maximum encoded size on the wire
+387    ///
+388    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+389    /// estimate, does not squeeze every last byte out.
+390    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+391        match path_retire_cid {
+392            true => Self::SIZE_BOUND_MULTIPATH,
+393            false => Self::SIZE_BOUND,
+394        }
+395    }
+396}
+397
+398#[derive(Clone, Debug)]
+399pub enum Close {
+400    Connection(ConnectionClose),
+401    Application(ApplicationClose),
+402}
+403
+404impl Close {
+405    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+406        match *self {
+407            Self::Connection(ref x) => x.encode(out, max_len),
+408            Self::Application(ref x) => x.encode(out, max_len),
+409        }
+410    }
+411
+412    pub(crate) fn is_transport_layer(&self) -> bool {
+413        matches!(*self, Self::Connection(_))
+414    }
+415}
+416
+417impl From<TransportError> for Close {
+418    fn from(x: TransportError) -> Self {
+419        Self::Connection(x.into())
+420    }
+421}
+422impl From<ConnectionClose> for Close {
+423    fn from(x: ConnectionClose) -> Self {
+424        Self::Connection(x)
+425    }
+426}
+427impl From<ApplicationClose> for Close {
+428    fn from(x: ApplicationClose) -> Self {
+429        Self::Application(x)
+430    }
+431}
+432
+433/// Reason given by the transport for closing the connection
+434#[derive(Debug, Clone, PartialEq, Eq)]
+435pub struct ConnectionClose {
+436    /// Class of error as encoded in the specification
+437    pub error_code: TransportErrorCode,
+438    /// Type of frame that caused the close
+439    pub frame_type: Option<FrameType>,
+440    /// Human-readable reason for the close
+441    pub reason: Bytes,
+442}
+443
+444impl fmt::Display for ConnectionClose {
+445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+446        self.error_code.fmt(f)?;
+447        if !self.reason.as_ref().is_empty() {
+448            f.write_str(": ")?;
+449            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+450        }
+451        Ok(())
+452    }
+453}
+454
+455impl From<TransportError> for ConnectionClose {
+456    fn from(x: TransportError) -> Self {
+457        Self {
+458            error_code: x.code,
+459            frame_type: x.frame,
+460            reason: x.reason.into(),
+461        }
+462    }
+463}
+464
+465impl FrameStruct for ConnectionClose {
+466    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+467}
+468
+469impl ConnectionClose {
+470    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+471        out.write(FrameType::CONNECTION_CLOSE); // 1 byte
+472        out.write(self.error_code); // <= 8 bytes
+473        let ty = self.frame_type.map_or(0, |x| x.0);
+474        out.write_var(ty); // <= 8 bytes
+475        let max_len = max_len
+476            - 3
+477            - VarInt::from_u64(ty).unwrap().size()
+478            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+479        let actual_len = self.reason.len().min(max_len);
+480        out.write_var(actual_len as u64); // <= 8 bytes
+481        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+482    }
+483}
+484
+485/// Reason given by an application for closing the connection
+486#[derive(Debug, Clone, PartialEq, Eq)]
+487pub struct ApplicationClose {
+488    /// Application-specific reason code
+489    pub error_code: VarInt,
+490    /// Human-readable reason for the close
+491    pub reason: Bytes,
+492}
+493
+494impl fmt::Display for ApplicationClose {
+495    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+496        if !self.reason.as_ref().is_empty() {
+497            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+498            f.write_str(" (code ")?;
+499            self.error_code.fmt(f)?;
+500            f.write_str(")")?;
+501        } else {
+502            self.error_code.fmt(f)?;
+503        }
+504        Ok(())
+505    }
+506}
+507
+508impl FrameStruct for ApplicationClose {
+509    const SIZE_BOUND: usize = 1 + 8 + 8;
+510}
+511
+512impl ApplicationClose {
+513    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+514        out.write(FrameType::APPLICATION_CLOSE); // 1 byte
+515        out.write(self.error_code); // <= 8 bytes
+516        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+517        let actual_len = self.reason.len().min(max_len);
+518        out.write_var(actual_len as u64); // <= 8 bytes
+519        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+520    }
+521}
+522
+523#[derive(Clone, Eq, PartialEq)]
+524pub struct PathAck {
+525    pub path_id: PathId,
+526    pub largest: u64,
+527    pub delay: u64,
+528    pub additional: Bytes,
+529    pub ecn: Option<EcnCounts>,
+530}
+531
+532impl fmt::Debug for PathAck {
+533    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+534        let mut ranges = "[".to_string();
+535        let mut first = true;
+536        for range in self.into_iter() {
+537            if !first {
+538                ranges.push(',');
+539            }
+540            write!(ranges, "{range:?}")?;
+541            first = false;
+542        }
+543        ranges.push(']');
+544
+545        f.debug_struct("PathAck")
+546            .field("path_id", &self.path_id)
+547            .field("largest", &self.largest)
+548            .field("delay", &self.delay)
+549            .field("ecn", &self.ecn)
+550            .field("ranges", &ranges)
+551            .finish()
+552    }
+553}
+554
+555impl<'a> IntoIterator for &'a PathAck {
+556    type Item = RangeInclusive<u64>;
+557    type IntoIter = AckIter<'a>;
+558
+559    fn into_iter(self) -> AckIter<'a> {
+560        AckIter::new(self.largest, &self.additional[..])
+561    }
+562}
+563
+564impl PathAck {
+565    /// Encode [`Self`] into the given buffer
+566    ///
+567    /// The [`FrameType`] will be either [`FrameType::PATH_ACK_ECN`] or [`FrameType::PATH_ACK`]
+568    /// depending on whether [`EcnCounts`] are provided.
+569    ///
+570    /// PANICS: if `ranges` is empty.
+571    pub fn encode<W: BufMut>(
+572        path_id: PathId,
+573        delay: u64,
+574        ranges: &ArrayRangeSet,
+575        ecn: Option<&EcnCounts>,
+576        buf: &mut W,
+577    ) {
+578        let mut rest = ranges.iter().rev();
+579        let first = rest
+580            .next()
+581            .expect("Caller has verified ranges is non empty");
+582        let largest = first.end - 1;
+583        let first_size = first.end - first.start;
+584        let kind = match ecn.is_some() {
+585            true => FrameType::PATH_ACK_ECN,
+586            false => FrameType::PATH_ACK,
+587        };
+588        buf.write(kind);
+589        buf.write(path_id);
+590        buf.write_var(largest);
+591        buf.write_var(delay);
+592        buf.write_var(ranges.len() as u64 - 1);
+593        buf.write_var(first_size - 1);
+594        let mut prev = first.start;
+595        for block in rest {
+596            let size = block.end - block.start;
+597            buf.write_var(prev - block.end - 1);
+598            buf.write_var(size - 1);
+599            prev = block.start;
+600        }
+601        if let Some(x) = ecn {
+602            x.encode(buf)
+603        }
+604    }
+605
+606    pub fn into_ack(self) -> (Ack, PathId) {
+607        let ack = Ack {
+608            largest: self.largest,
+609            delay: self.delay,
+610            additional: self.additional,
+611            ecn: self.ecn,
+612        };
+613
+614        (ack, self.path_id)
+615    }
+616}
+617
+618#[derive(Clone, Eq, PartialEq)]
+619pub struct Ack {
+620    pub largest: u64,
+621    pub delay: u64,
+622    pub additional: Bytes,
+623    pub ecn: Option<EcnCounts>,
+624}
+625
+626impl fmt::Debug for Ack {
+627    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+628        let mut ranges = "[".to_string();
+629        let mut first = true;
+630        for range in self.iter() {
+631            if !first {
+632                ranges.push(',');
+633            }
+634            write!(ranges, "{range:?}").unwrap();
+635            first = false;
+636        }
+637        ranges.push(']');
+638
+639        f.debug_struct("Ack")
+640            .field("largest", &self.largest)
+641            .field("delay", &self.delay)
+642            .field("ecn", &self.ecn)
+643            .field("ranges", &ranges)
+644            .finish()
+645    }
+646}
+647
+648impl<'a> IntoIterator for &'a Ack {
+649    type Item = RangeInclusive<u64>;
+650    type IntoIter = AckIter<'a>;
+651
+652    fn into_iter(self) -> AckIter<'a> {
+653        AckIter::new(self.largest, &self.additional[..])
+654    }
+655}
+656
+657impl Ack {
+658    pub fn encode<W: BufMut>(
+659        delay: u64,
+660        ranges: &ArrayRangeSet,
+661        ecn: Option<&EcnCounts>,
+662        buf: &mut W,
+663    ) {
+664        let mut rest = ranges.iter().rev();
+665        let first = rest.next().unwrap();
+666        let largest = first.end - 1;
+667        let first_size = first.end - first.start;
+668        let kind = match ecn.is_some() {
+669            true => FrameType::ACK_ECN,
+670            false => FrameType::ACK,
+671        };
+672        buf.write(kind);
+673        buf.write_var(largest);
+674        buf.write_var(delay);
+675        buf.write_var(ranges.len() as u64 - 1);
+676        buf.write_var(first_size - 1);
+677        let mut prev = first.start;
+678        for block in rest {
+679            let size = block.end - block.start;
+680            buf.write_var(prev - block.end - 1);
+681            buf.write_var(size - 1);
+682            prev = block.start;
+683        }
+684        if let Some(x) = ecn {
+685            x.encode(buf)
+686        }
+687    }
+688
+689    pub fn iter(&self) -> AckIter<'_> {
+690        self.into_iter()
+691    }
+692}
+693
+694#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+695pub struct EcnCounts {
+696    pub ect0: u64,
+697    pub ect1: u64,
+698    pub ce: u64,
+699}
+700
+701impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+702    fn add_assign(&mut self, rhs: EcnCodepoint) {
+703        match rhs {
+704            EcnCodepoint::Ect0 => {
+705                self.ect0 += 1;
+706            }
+707            EcnCodepoint::Ect1 => {
+708                self.ect1 += 1;
+709            }
+710            EcnCodepoint::Ce => {
+711                self.ce += 1;
+712            }
+713        }
+714    }
+715}
+716
+717impl EcnCounts {
+718    pub const ZERO: Self = Self {
+719        ect0: 0,
+720        ect1: 0,
+721        ce: 0,
+722    };
+723
+724    pub fn encode<W: BufMut>(&self, out: &mut W) {
+725        out.write_var(self.ect0);
+726        out.write_var(self.ect1);
+727        out.write_var(self.ce);
+728    }
+729}
+730
+731#[derive(Debug, Clone)]
+732pub(crate) struct Stream {
+733    pub(crate) id: StreamId,
+734    pub(crate) offset: u64,
+735    pub(crate) fin: bool,
+736    pub(crate) data: Bytes,
+737}
+738
+739impl FrameStruct for Stream {
+740    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+741}
+742
+743/// Metadata from a stream frame
+744#[derive(Debug, Clone)]
+745pub(crate) struct StreamMeta {
+746    pub(crate) id: StreamId,
+747    pub(crate) offsets: Range<u64>,
+748    pub(crate) fin: bool,
+749}
+750
+751// This manual implementation exists because `Default` is not implemented for `StreamId`
+752impl Default for StreamMeta {
+753    fn default() -> Self {
+754        Self {
+755            id: StreamId(0),
+756            offsets: 0..0,
+757            fin: false,
+758        }
+759    }
+760}
+761
+762impl StreamMeta {
+763    pub(crate) fn encode<W: BufMut>(&self, length: bool, out: &mut W) {
+764        let mut ty = *STREAM_TYS.start();
+765        if self.offsets.start != 0 {
+766            ty |= 0x04;
+767        }
+768        if length {
+769            ty |= 0x02;
+770        }
+771        if self.fin {
+772            ty |= 0x01;
+773        }
+774        out.write_var(ty); // 1 byte
+775        out.write(self.id); // <=8 bytes
+776        if self.offsets.start != 0 {
+777            out.write_var(self.offsets.start); // <=8 bytes
+778        }
+779        if length {
+780            out.write_var(self.offsets.end - self.offsets.start); // <=8 bytes
+781        }
+782    }
+783}
+784
+785/// A vector of [`StreamMeta`] with optimization for the single element case
+786pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+787
+788#[derive(Debug, Clone)]
+789pub(crate) struct Crypto {
+790    pub(crate) offset: u64,
+791    pub(crate) data: Bytes,
+792}
+793
+794impl Crypto {
+795    pub(crate) const SIZE_BOUND: usize = 17;
+796
+797    pub(crate) fn encode<W: BufMut>(&self, out: &mut W) {
+798        out.write(FrameType::CRYPTO);
+799        out.write_var(self.offset);
+800        out.write_var(self.data.len() as u64);
+801        out.put_slice(&self.data);
+802    }
+803}
+804
+805#[derive(Debug, Clone)]
+806pub(crate) struct NewToken {
+807    pub(crate) token: Bytes,
+808}
+809
+810impl NewToken {
+811    pub(crate) fn encode<W: BufMut>(&self, out: &mut W) {
+812        out.write(FrameType::NEW_TOKEN);
+813        out.write_var(self.token.len() as u64);
+814        out.put_slice(&self.token);
+815    }
+816
+817    pub(crate) fn size(&self) -> usize {
+818        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+819    }
+820}
+821
+822#[derive(Debug, Clone)]
+823pub(crate) struct MaxPathId(pub(crate) PathId);
+824
+825impl MaxPathId {
+826    pub(crate) const SIZE_BOUND: usize =
+827        VarInt(FrameType::MAX_PATH_ID.0).size() + VarInt(u32::MAX as u64).size();
+828
+829    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+830    pub(crate) fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+831        Ok(Self(buf.get()?))
+832    }
+833
+834    /// Encode [`Self`] into the given buffer
+835    pub(crate) fn encode<B: BufMut>(&self, buf: &mut B) {
+836        buf.write(FrameType::MAX_PATH_ID);
+837        buf.write(self.0);
+838    }
+839}
+840
+841#[derive(Debug, Clone, PartialEq, Eq)]
+842pub(crate) struct PathsBlocked(pub(crate) PathId);
+843
+844impl PathsBlocked {
+845    pub(crate) const SIZE_BOUND: usize =
+846        VarInt(FrameType::PATHS_BLOCKED.0).size() + VarInt(u32::MAX as u64).size();
+847
+848    /// Encode [`Self`] into the given buffer
+849    pub(crate) fn encode<B: BufMut>(&self, buf: &mut B) {
+850        buf.write(FrameType::PATHS_BLOCKED);
+851        buf.write(self.0);
+852    }
+853
+854    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+855    pub(crate) fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+856        Ok(Self(buf.get()?))
+857    }
+858}
+859
+860#[derive(Debug, Clone, PartialEq, Eq)]
+861pub(crate) struct PathCidsBlocked {
+862    pub(crate) path_id: PathId,
+863    pub(crate) next_seq: VarInt,
+864}
+865
+866impl PathCidsBlocked {
+867    pub(crate) const SIZE_BOUND: usize = VarInt(FrameType::PATH_CIDS_BLOCKED.0).size()
+868        + VarInt(u32::MAX as u64).size()
+869        + VarInt::MAX.size();
+870
+871    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+872    pub(crate) fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+873        Ok(Self {
+874            path_id: buf.get()?,
+875            next_seq: buf.get()?,
+876        })
+877    }
+878
+879    // Encode [`Self`] into the given buffer
+880    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+881        buf.write(FrameType::PATH_CIDS_BLOCKED);
+882        buf.write(self.path_id);
+883        buf.write(self.next_seq);
+884    }
+885}
+886
+887pub(crate) struct Iter {
+888    bytes: Bytes,
+889    last_ty: Option<FrameType>,
+890}
+891
+892impl Iter {
+893    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+894        if payload.is_empty() {
+895            // "An endpoint MUST treat receipt of a packet containing no frames as a
+896            // connection error of type PROTOCOL_VIOLATION."
+897            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+898            return Err(TransportError::PROTOCOL_VIOLATION(
+899                "packet payload is empty",
+900            ));
+901        }
+902
+903        Ok(Self {
+904            bytes: payload,
+905            last_ty: None,
+906        })
+907    }
+908
+909    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+910        let len = self.bytes.get_var()?;
+911        if len > self.bytes.remaining() as u64 {
+912            return Err(UnexpectedEnd);
+913        }
+914        Ok(self.bytes.split_to(len as usize))
+915    }
+916
+917    #[track_caller]
+918    fn try_next(&mut self) -> Result<Frame, IterErr> {
+919        let ty = self.bytes.get::<FrameType>()?;
+920        self.last_ty = Some(ty);
+921        Ok(match ty {
+922            FrameType::PADDING => Frame::Padding,
+923            FrameType::RESET_STREAM => Frame::ResetStream(ResetStream {
+924                id: self.bytes.get()?,
+925                error_code: self.bytes.get()?,
+926                final_offset: self.bytes.get()?,
+927            }),
+928            FrameType::CONNECTION_CLOSE => Frame::Close(Close::Connection(ConnectionClose {
+929                error_code: self.bytes.get()?,
+930                frame_type: {
+931                    let x = self.bytes.get_var()?;
+932                    if x == 0 { None } else { Some(FrameType(x)) }
+933                },
+934                reason: self.take_len()?,
+935            })),
+936            FrameType::APPLICATION_CLOSE => Frame::Close(Close::Application(ApplicationClose {
+937                error_code: self.bytes.get()?,
+938                reason: self.take_len()?,
+939            })),
+940            FrameType::MAX_DATA => Frame::MaxData(self.bytes.get()?),
+941            FrameType::MAX_STREAM_DATA => Frame::MaxStreamData {
+942                id: self.bytes.get()?,
+943                offset: self.bytes.get_var()?,
+944            },
+945            FrameType::MAX_STREAMS_BIDI => Frame::MaxStreams {
+946                dir: Dir::Bi,
+947                count: self.bytes.get_var()?,
+948            },
+949            FrameType::MAX_STREAMS_UNI => Frame::MaxStreams {
+950                dir: Dir::Uni,
+951                count: self.bytes.get_var()?,
+952            },
+953            FrameType::PING => Frame::Ping,
+954            FrameType::DATA_BLOCKED => Frame::DataBlocked {
+955                offset: self.bytes.get_var()?,
+956            },
+957            FrameType::STREAM_DATA_BLOCKED => Frame::StreamDataBlocked {
+958                id: self.bytes.get()?,
+959                offset: self.bytes.get_var()?,
+960            },
+961            FrameType::STREAMS_BLOCKED_BIDI => Frame::StreamsBlocked {
+962                dir: Dir::Bi,
+963                limit: self.bytes.get_var()?,
+964            },
+965            FrameType::STREAMS_BLOCKED_UNI => Frame::StreamsBlocked {
+966                dir: Dir::Uni,
+967                limit: self.bytes.get_var()?,
+968            },
+969            FrameType::STOP_SENDING => Frame::StopSending(StopSending {
+970                id: self.bytes.get()?,
+971                error_code: self.bytes.get()?,
+972            }),
+973            FrameType::RETIRE_CONNECTION_ID | FrameType::PATH_RETIRE_CONNECTION_ID => {
+974                Frame::RetireConnectionId(RetireConnectionId::decode(
+975                    &mut self.bytes,
+976                    ty == FrameType::PATH_RETIRE_CONNECTION_ID,
+977                )?)
+978            }
+979            FrameType::ACK | FrameType::ACK_ECN => {
+980                let largest = self.bytes.get_var()?;
+981                let delay = self.bytes.get_var()?;
+982                let extra_blocks = self.bytes.get_var()? as usize;
+983                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+984                Frame::Ack(Ack {
+985                    delay,
+986                    largest,
+987                    additional: self.bytes.split_to(n),
+988                    ecn: if ty != FrameType::ACK_ECN && ty != FrameType::PATH_ACK_ECN {
+989                        None
+990                    } else {
+991                        Some(EcnCounts {
+992                            ect0: self.bytes.get_var()?,
+993                            ect1: self.bytes.get_var()?,
+994                            ce: self.bytes.get_var()?,
+995                        })
+996                    },
+997                })
+998            }
+999            FrameType::PATH_ACK | FrameType::PATH_ACK_ECN => {
+1000                let path_id = self.bytes.get()?;
+1001                let largest = self.bytes.get_var()?;
+1002                let delay = self.bytes.get_var()?;
+1003                let extra_blocks = self.bytes.get_var()? as usize;
+1004                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+1005                Frame::PathAck(PathAck {
+1006                    path_id,
+1007                    delay,
+1008                    largest,
+1009                    additional: self.bytes.split_to(n),
+1010                    ecn: if ty != FrameType::ACK_ECN && ty != FrameType::PATH_ACK_ECN {
+1011                        None
+1012                    } else {
+1013                        Some(EcnCounts {
+1014                            ect0: self.bytes.get_var()?,
+1015                            ect1: self.bytes.get_var()?,
+1016                            ce: self.bytes.get_var()?,
+1017                        })
+1018                    },
+1019                })
+1020            }
+1021            FrameType::PATH_CHALLENGE => Frame::PathChallenge(self.bytes.get()?),
+1022            FrameType::PATH_RESPONSE => Frame::PathResponse(self.bytes.get()?),
+1023            FrameType::NEW_CONNECTION_ID | FrameType::PATH_NEW_CONNECTION_ID => {
+1024                let read_path = ty == FrameType::PATH_NEW_CONNECTION_ID;
+1025                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1026            }
+1027            FrameType::CRYPTO => Frame::Crypto(Crypto {
+1028                offset: self.bytes.get_var()?,
+1029                data: self.take_len()?,
+1030            }),
+1031            FrameType::NEW_TOKEN => Frame::NewToken(NewToken {
+1032                token: self.take_len()?,
+1033            }),
+1034            FrameType::HANDSHAKE_DONE => Frame::HandshakeDone,
+1035            FrameType::ACK_FREQUENCY => Frame::AckFrequency(AckFrequency {
+1036                sequence: self.bytes.get()?,
+1037                ack_eliciting_threshold: self.bytes.get()?,
+1038                request_max_ack_delay: self.bytes.get()?,
+1039                reordering_threshold: self.bytes.get()?,
+1040            }),
+1041            FrameType::IMMEDIATE_ACK => Frame::ImmediateAck,
+1042            FrameType::OBSERVED_IPV4_ADDR | FrameType::OBSERVED_IPV6_ADDR => {
+1043                let is_ipv6 = ty == FrameType::OBSERVED_IPV6_ADDR;
+1044                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1045                Frame::ObservedAddr(observed)
+1046            }
+1047            FrameType::PATH_ABANDON => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1048            FrameType::PATH_STATUS_AVAILABLE => {
+1049                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1050            }
+1051            FrameType::PATH_STATUS_BACKUP => {
+1052                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1053            }
+1054            FrameType::MAX_PATH_ID => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1055            FrameType::PATHS_BLOCKED => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1056            FrameType::PATH_CIDS_BLOCKED => {
+1057                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1058            }
+1059            FrameType::ADD_IPV4_ADDRESS | FrameType::ADD_IPV6_ADDRESS => {
+1060                let is_ipv6 = ty == FrameType::ADD_IPV6_ADDRESS;
+1061                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1062                Frame::AddAddress(add_address)
+1063            }
+1064            FrameType::REACH_OUT_AT_IPV4 | FrameType::REACH_OUT_AT_IPV6 => {
+1065                let is_ipv6 = ty == FrameType::REACH_OUT_AT_IPV6;
+1066                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1067                Frame::ReachOut(reach_out)
+1068            }
+1069            FrameType::REMOVE_ADDRESS => {
+1070                Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?)
+1071            }
+1072            _ => {
+1073                if let Some(s) = ty.stream() {
+1074                    Frame::Stream(Stream {
+1075                        id: self.bytes.get()?,
+1076                        offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1077                        fin: s.fin(),
+1078                        data: if s.len() {
+1079                            self.take_len()?
+1080                        } else {
+1081                            self.take_remaining()
+1082                        },
+1083                    })
+1084                } else if let Some(d) = ty.datagram() {
+1085                    Frame::Datagram(Datagram {
+1086                        data: if d.len() {
+1087                            self.take_len()?
+1088                        } else {
+1089                            self.take_remaining()
+1090                        },
+1091                    })
+1092                } else {
+1093                    return Err(IterErr::InvalidFrameId);
+1094                }
+1095            }
+1096        })
+1097    }
+1098
+1099    fn take_remaining(&mut self) -> Bytes {
+1100        mem::take(&mut self.bytes)
+1101    }
+1102}
+1103
+1104impl Iterator for Iter {
+1105    type Item = Result<Frame, InvalidFrame>;
+1106    fn next(&mut self) -> Option<Self::Item> {
+1107        if !self.bytes.has_remaining() {
+1108            return None;
+1109        }
+1110        match self.try_next() {
+1111            Ok(x) => Some(Ok(x)),
+1112            Err(e) => {
+1113                // Corrupt frame, skip it and everything that follows
+1114                self.bytes.clear();
+1115                Some(Err(InvalidFrame {
+1116                    ty: self.last_ty,
+1117                    reason: e.reason(),
+1118                }))
+1119            }
+1120        }
+1121    }
+1122}
+1123
+1124#[derive(Debug)]
+1125pub(crate) struct InvalidFrame {
+1126    pub(crate) ty: Option<FrameType>,
+1127    pub(crate) reason: &'static str,
+1128}
+1129
+1130impl From<InvalidFrame> for TransportError {
+1131    fn from(err: InvalidFrame) -> Self {
+1132        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1133        te.frame = err.ty;
+1134        te
+1135    }
+1136}
+1137
+1138/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover
+1139fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> {
+1140    let total_len = buf.remaining();
+1141    let first_block = buf.get_var()?;
+1142    let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?;
+1143    for _ in 0..n {
+1144        let gap = buf.get_var()?;
+1145        smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?;
+1146        let block = buf.get_var()?;
+1147        smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?;
+1148    }
+1149    Ok(total_len - buf.remaining())
+1150}
+1151
+1152#[derive(Debug)]
+1153enum IterErr {
+1154    UnexpectedEnd,
+1155    InvalidFrameId,
+1156    Malformed,
+1157}
+1158
+1159impl IterErr {
+1160    fn reason(&self) -> &'static str {
+1161        use IterErr::*;
+1162        match *self {
+1163            UnexpectedEnd => "unexpected end",
+1164            InvalidFrameId => "invalid frame ID",
+1165            Malformed => "malformed",
+1166        }
+1167    }
+1168}
+1169
+1170impl From<UnexpectedEnd> for IterErr {
+1171    fn from(_: UnexpectedEnd) -> Self {
+1172        Self::UnexpectedEnd
+1173    }
+1174}
+1175
+1176#[derive(Debug, Clone)]
+1177pub struct AckIter<'a> {
+1178    largest: u64,
+1179    data: &'a [u8],
+1180}
+1181
+1182impl<'a> AckIter<'a> {
+1183    fn new(largest: u64, data: &'a [u8]) -> Self {
+1184        Self { largest, data }
+1185    }
+1186}
+1187
+1188impl Iterator for AckIter<'_> {
+1189    type Item = RangeInclusive<u64>;
+1190    fn next(&mut self) -> Option<RangeInclusive<u64>> {
+1191        if !self.data.has_remaining() {
+1192            return None;
+1193        }
+1194        let block = self.data.get_var().unwrap();
+1195        let largest = self.largest;
+1196        if let Ok(gap) = self.data.get_var() {
+1197            self.largest -= block + gap + 2;
+1198        }
+1199        Some(largest - block..=largest)
+1200    }
+1201}
+1202
+1203#[allow(unreachable_pub)] // fuzzing only
+1204#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+1205#[derive(Debug, Copy, Clone)]
+1206pub struct ResetStream {
+1207    pub(crate) id: StreamId,
+1208    pub(crate) error_code: VarInt,
+1209    pub(crate) final_offset: VarInt,
+1210}
+1211
+1212impl FrameStruct for ResetStream {
+1213    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1214}
+1215
+1216impl ResetStream {
+1217    pub(crate) fn encode<W: BufMut>(&self, out: &mut W) {
+1218        out.write(FrameType::RESET_STREAM); // 1 byte
+1219        out.write(self.id); // <= 8 bytes
+1220        out.write(self.error_code); // <= 8 bytes
+1221        out.write(self.final_offset); // <= 8 bytes
+1222    }
+1223}
+1224
+1225#[derive(Debug, Copy, Clone)]
+1226pub(crate) struct StopSending {
+1227    pub(crate) id: StreamId,
+1228    pub(crate) error_code: VarInt,
+1229}
+1230
+1231impl FrameStruct for StopSending {
+1232    const SIZE_BOUND: usize = 1 + 8 + 8;
+1233}
+1234
+1235impl StopSending {
+1236    pub(crate) fn encode<W: BufMut>(&self, out: &mut W) {
+1237        out.write(FrameType::STOP_SENDING); // 1 byte
+1238        out.write(self.id); // <= 8 bytes
+1239        out.write(self.error_code) // <= 8 bytes
+1240    }
+1241}
+1242
+1243#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+1244pub(crate) struct NewConnectionId {
+1245    pub(crate) path_id: Option<PathId>,
+1246    pub(crate) sequence: u64,
+1247    pub(crate) retire_prior_to: u64,
+1248    pub(crate) id: ConnectionId,
+1249    pub(crate) reset_token: ResetToken,
+1250}
+1251
+1252impl NewConnectionId {
+1253    /// Maximum size of this frame when the frame type is [`FrameType::NEW_CONNECTION_ID`],
+1254    pub(crate) const SIZE_BOUND: usize = {
+1255        let type_len = VarInt(FrameType::NEW_CONNECTION_ID.0).size();
+1256        let seq_max_len = 8usize;
+1257        let retire_prior_to_max_len = 8usize;
+1258        let cid_len_len = 1;
+1259        let cid_len = 160;
+1260        let reset_token_len = 16;
+1261        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1262    };
+1263
+1264    /// Maximum size of this frame when the frame type is [`FrameType::PATH_NEW_CONNECTION_ID`],
+1265    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1266        let type_len = VarInt(FrameType::PATH_NEW_CONNECTION_ID.0).size();
+1267        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1268        let seq_max_len = 8usize;
+1269        let retire_prior_to_max_len = 8usize;
+1270        let cid_len_len = 1;
+1271        let cid_len = 160;
+1272        let reset_token_len = 16;
+1273        type_len
+1274            + path_id_len
+1275            + seq_max_len
+1276            + retire_prior_to_max_len
+1277            + cid_len_len
+1278            + cid_len
+1279            + reset_token_len
+1280    };
+1281
+1282    pub(crate) fn encode<W: BufMut>(&self, out: &mut W) {
+1283        out.write(self.get_type());
+1284        if let Some(id) = self.path_id {
+1285            out.write(id);
+1286        }
+1287        out.write_var(self.sequence);
+1288        out.write_var(self.retire_prior_to);
+1289        out.write(self.id.len() as u8);
+1290        out.put_slice(&self.id);
+1291        out.put_slice(&self.reset_token);
+1292    }
+1293
+1294    pub(crate) fn get_type(&self) -> FrameType {
+1295        if self.path_id.is_some() {
+1296            FrameType::PATH_NEW_CONNECTION_ID
+1297        } else {
+1298            FrameType::NEW_CONNECTION_ID
+1299        }
+1300    }
+1301
+1302    /// Returns the maximum encoded size on the wire.
+1303    ///
+1304    /// This is a rough upper estimate, does not squeeze every last byte out.
+1305    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1306        let upper_bound = match path_new_cid {
+1307            true => Self::SIZE_BOUND_MULTIPATH,
+1308            false => Self::SIZE_BOUND,
+1309        };
+1310        // instead of using the maximum cid len, use the provided one
+1311        upper_bound - 160 + cid_len
+1312    }
+1313
+1314    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1315        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1316        let sequence = bytes.get_var()?;
+1317        let retire_prior_to = bytes.get_var()?;
+1318        if retire_prior_to > sequence {
+1319            return Err(IterErr::Malformed);
+1320        }
+1321        let length = bytes.get::<u8>()? as usize;
+1322        if length > MAX_CID_SIZE || length == 0 {
+1323            return Err(IterErr::Malformed);
+1324        }
+1325        if length > bytes.remaining() {
+1326            return Err(IterErr::UnexpectedEnd);
+1327        }
+1328        let mut stage = [0; MAX_CID_SIZE];
+1329        bytes.copy_to_slice(&mut stage[0..length]);
+1330        let id = ConnectionId::new(&stage[..length]);
+1331        if bytes.remaining() < 16 {
+1332            return Err(IterErr::UnexpectedEnd);
+1333        }
+1334        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1335        bytes.copy_to_slice(&mut reset_token);
+1336        Ok(Self {
+1337            path_id,
+1338            sequence,
+1339            retire_prior_to,
+1340            id,
+1341            reset_token: reset_token.into(),
+1342        })
+1343    }
+1344}
+1345
+1346impl FrameStruct for NewConnectionId {
+1347    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+1348}
+1349
+1350/// An unreliable datagram
+1351#[derive(Debug, Clone)]
+1352pub struct Datagram {
+1353    /// Payload
+1354    pub data: Bytes,
+1355}
+1356
+1357impl FrameStruct for Datagram {
+1358    const SIZE_BOUND: usize = 1 + 8;
+1359}
+1360
+1361impl Datagram {
+1362    pub(crate) fn encode(&self, length: bool, out: &mut impl BufMut) {
+1363        out.write(FrameType(*DATAGRAM_TYS.start() | u64::from(length))); // 1 byte
+1364        if length {
+1365            // Safe to unwrap because we check length sanity before queueing datagrams
+1366            out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+1367        }
+1368        out.put_slice(&self.data);
+1369    }
+1370
+1371    pub(crate) fn size(&self, length: bool) -> usize {
+1372        1 + if length {
+1373            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+1374        } else {
+1375            0
+1376        } + self.data.len()
+1377    }
+1378}
+1379
+1380#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+1381pub(crate) struct AckFrequency {
+1382    pub(crate) sequence: VarInt,
+1383    pub(crate) ack_eliciting_threshold: VarInt,
+1384    pub(crate) request_max_ack_delay: VarInt,
+1385    pub(crate) reordering_threshold: VarInt,
+1386}
+1387
+1388impl AckFrequency {
+1389    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+1390        buf.write(FrameType::ACK_FREQUENCY);
+1391        buf.write(self.sequence);
+1392        buf.write(self.ack_eliciting_threshold);
+1393        buf.write(self.request_max_ack_delay);
+1394        buf.write(self.reordering_threshold);
+1395    }
+1396}
+1397
+1398/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+1399
+1400/// Conjunction of the information contained in the address discovery frames
+1401/// ([`FrameType::OBSERVED_IPV4_ADDR`], [`FrameType::OBSERVED_IPV6_ADDR`]).
+1402#[derive(Debug, PartialEq, Eq, Clone)]
+1403pub(crate) struct ObservedAddr {
+1404    /// Monotonically increasing integer within the same connection.
+1405    pub(crate) seq_no: VarInt,
+1406    /// Reported observed address.
+1407    pub(crate) ip: IpAddr,
+1408    /// Reported observed port.
+1409    pub(crate) port: u16,
+1410}
+1411
+1412impl ObservedAddr {
+1413    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+1414        Self {
+1415            ip: remote.ip(),
+1416            port: remote.port(),
+1417            seq_no: seq_no.into(),
+1418        }
+1419    }
+1420
+1421    /// Get the [`FrameType`] for this frame.
+1422    pub(crate) fn get_type(&self) -> FrameType {
+1423        if self.ip.is_ipv6() {
+1424            FrameType::OBSERVED_IPV6_ADDR
+1425        } else {
+1426            FrameType::OBSERVED_IPV4_ADDR
+1427        }
+1428    }
+1429
+1430    /// Compute the number of bytes needed to encode the frame.
+1431    pub(crate) fn size(&self) -> usize {
+1432        let type_size = VarInt(self.get_type().0).size();
+1433        let req_id_bytes = self.seq_no.size();
+1434        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1435        let port_bytes = 2;
+1436        type_size + req_id_bytes + ip_bytes + port_bytes
+1437    }
+1438
+1439    /// Unconditionally write this frame to `buf`.
+1440    pub(crate) fn write<W: BufMut>(&self, buf: &mut W) {
+1441        buf.write(self.get_type());
+1442        buf.write(self.seq_no);
+1443        match self.ip {
+1444            IpAddr::V4(ipv4_addr) => {
+1445                buf.write(ipv4_addr);
+1446            }
+1447            IpAddr::V6(ipv6_addr) => {
+1448                buf.write(ipv6_addr);
+1449            }
+1450        }
+1451        buf.write::<u16>(self.port);
+1452    }
+1453
+1454    /// Reads the frame contents from the buffer.
+1455    ///
+1456    /// Should only be called when the frame type has been identified as
+1457    /// [`FrameType::OBSERVED_IPV4_ADDR`] or [`FrameType::OBSERVED_IPV6_ADDR`].
+1458    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1459        let seq_no = bytes.get()?;
+1460        let ip = if is_ipv6 {
+1461            IpAddr::V6(bytes.get()?)
+1462        } else {
+1463            IpAddr::V4(bytes.get()?)
+1464        };
+1465        let port = bytes.get()?;
+1466        Ok(Self { seq_no, ip, port })
+1467    }
+1468
+1469    /// Gives the [`SocketAddr`] reported in the frame.
+1470    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1471        (self.ip, self.port).into()
+1472    }
+1473}
+1474
+1475/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
+1476
+1477#[derive(Debug, PartialEq, Eq)]
+1478pub(crate) struct PathAbandon {
+1479    pub(crate) path_id: PathId,
+1480    pub(crate) error_code: TransportErrorCode,
+1481}
+1482
+1483impl PathAbandon {
+1484    pub(crate) const SIZE_BOUND: usize = VarInt(FrameType::PATH_ABANDON.0).size() + 8 + 8;
+1485
+1486    /// Encode [`Self`] into the given buffer
+1487    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+1488        buf.write(FrameType::PATH_ABANDON);
+1489        buf.write(self.path_id);
+1490        buf.write(self.error_code);
+1491    }
+1492
+1493    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1494    pub(crate) fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1495        Ok(Self {
+1496            path_id: bytes.get()?,
+1497            error_code: bytes.get()?,
+1498        })
+1499    }
+1500}
+1501
+1502#[derive(Debug, PartialEq, Eq)]
+1503pub(crate) struct PathStatusAvailable {
+1504    pub(crate) path_id: PathId,
+1505    pub(crate) status_seq_no: VarInt,
+1506}
+1507
+1508impl PathStatusAvailable {
+1509    const TYPE: FrameType = FrameType::PATH_STATUS_AVAILABLE;
+1510    pub(crate) const SIZE_BOUND: usize = VarInt(FrameType::PATH_STATUS_AVAILABLE.0).size() + 8 + 8;
+1511
+1512    /// Encode [`Self`] into the given buffer
+1513    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+1514        buf.write(Self::TYPE);
+1515        buf.write(self.path_id);
+1516        buf.write(self.status_seq_no);
+1517    }
+1518
+1519    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1520    pub(crate) fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1521        Ok(Self {
+1522            path_id: bytes.get()?,
+1523            status_seq_no: bytes.get()?,
+1524        })
+1525    }
+1526}
+1527
+1528#[derive(Debug, PartialEq, Eq)]
+1529pub(crate) struct PathStatusBackup {
+1530    pub(crate) path_id: PathId,
+1531    pub(crate) status_seq_no: VarInt,
+1532}
+1533
+1534impl PathStatusBackup {
+1535    const TYPE: FrameType = FrameType::PATH_STATUS_BACKUP;
+1536
+1537    /// Encode [`Self`] into the given buffer
+1538    pub(crate) fn encode<W: BufMut>(&self, buf: &mut W) {
+1539        buf.write(Self::TYPE);
+1540        buf.write(self.path_id);
+1541        buf.write(self.status_seq_no);
+1542    }
+1543
+1544    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1545    pub(crate) fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1546        Ok(Self {
+1547            path_id: bytes.get()?,
+1548            status_seq_no: bytes.get()?,
+1549        })
+1550    }
+1551}
+1552
+1553/* Nat traversal frames */
+1554
+1555/// Conjunction of the information contained in the add address frames
+1556/// ([`FrameType::ADD_IPV4_ADDRESS`], [`FrameType::ADD_IPV6_ADDRESS`]).
+1557#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
+1558// TODO(@divma): remove
+1559#[allow(dead_code)]
+1560pub(crate) struct AddAddress {
+1561    /// Monotonically increasing integer within the same connection
+1562    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+1563    pub(crate) seq_no: VarInt,
+1564    /// Address to include in the known set
+1565    pub(crate) ip: IpAddr,
+1566    /// Port to use with this address
+1567    pub(crate) port: u16,
+1568}
+1569
+1570// TODO(@divma): remove
+1571#[allow(dead_code)]
+1572impl AddAddress {
+1573    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+1574    pub(crate) const SIZE_BOUND: usize = Self {
+1575        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+1576        port: u16::MAX,
+1577        seq_no: VarInt::MAX,
+1578    }
+1579    .size();
+1580
+1581    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+1582        Self { ip, port, seq_no }
+1583    }
+1584
+1585    /// Get the [`FrameType`] for this frame.
+1586    pub(crate) const fn get_type(&self) -> FrameType {
+1587        if self.ip.is_ipv6() {
+1588            FrameType::ADD_IPV6_ADDRESS
+1589        } else {
+1590            FrameType::ADD_IPV4_ADDRESS
+1591        }
+1592    }
+1593
+1594    /// Compute the number of bytes needed to encode the frame
+1595    pub(crate) const fn size(&self) -> usize {
+1596        let type_size = VarInt(self.get_type().0).size();
+1597        let seq_no_bytes = self.seq_no.size();
+1598        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1599        let port_bytes = 2;
+1600        type_size + seq_no_bytes + ip_bytes + port_bytes
+1601    }
+1602
+1603    /// Unconditionally write this frame to `buf`
+1604    pub(crate) fn write<W: BufMut>(&self, buf: &mut W) {
+1605        buf.write(self.get_type());
+1606        buf.write(self.seq_no);
+1607        match self.ip {
+1608            IpAddr::V4(ipv4_addr) => {
+1609                buf.write(ipv4_addr);
+1610            }
+1611            IpAddr::V6(ipv6_addr) => {
+1612                buf.write(ipv6_addr);
+1613            }
+1614        }
+1615        buf.write::<u16>(self.port);
+1616    }
+1617
+1618    /// Read the frame contents from the buffer
+1619    ///
+1620    /// Should only be called when the frame type has been identified as
+1621    /// [`FrameType::ADD_IPV4_ADDRESS`] or [`FrameType::ADD_IPV6_ADDRESS`].
+1622    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1623        let seq_no = bytes.get()?;
+1624        let ip = if is_ipv6 {
+1625            IpAddr::V6(bytes.get()?)
+1626        } else {
+1627            IpAddr::V4(bytes.get()?)
+1628        };
+1629        let port = bytes.get()?;
+1630        Ok(Self { seq_no, ip, port })
+1631    }
+1632
+1633    /// Give the [`SocketAddr`] encoded in the frame
+1634    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1635        self.ip_port().into()
+1636    }
+1637
+1638    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+1639        (self.ip, self.port)
+1640    }
+1641}
+1642
+1643/// Conjunction of the information contained in the reach out frames
+1644/// ([`FrameType::REACH_OUT_AT_IPV4`], [`FrameType::REACH_OUT_AT_IPV6`])
+1645#[derive(Debug, PartialEq, Eq, Clone)]
+1646// TODO(@divma): remove
+1647#[allow(dead_code)]
+1648pub(crate) struct ReachOut {
+1649    /// The sequence number of the NAT Traversal attempts
+1650    pub(crate) round: VarInt,
+1651    /// Address to use
+1652    pub(crate) ip: IpAddr,
+1653    /// Port to use with this address
+1654    pub(crate) port: u16,
+1655}
+1656
+1657// TODO(@divma): remove
+1658#[allow(dead_code)]
+1659impl ReachOut {
+1660    /// Smallest number of bytes this type of frame is guaranteed to fit within
+1661    pub(crate) const SIZE_BOUND: usize = Self {
+1662        round: VarInt::MAX,
+1663        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+1664        port: u16::MAX,
+1665    }
+1666    .size();
+1667
+1668    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
+1669        Self { round, ip, port }
+1670    }
+1671
+1672    /// Get the [`FrameType`] for this frame
+1673    pub(crate) const fn get_type(&self) -> FrameType {
+1674        if self.ip.is_ipv6() {
+1675            FrameType::REACH_OUT_AT_IPV6
+1676        } else {
+1677            FrameType::REACH_OUT_AT_IPV4
+1678        }
+1679    }
+1680
+1681    /// Compute the number of bytes needed to encode the frame
+1682    pub(crate) const fn size(&self) -> usize {
+1683        let type_size = VarInt(self.get_type().0).size();
+1684        let round_bytes = self.round.size();
+1685        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1686        let port_bytes = 2;
+1687        type_size + round_bytes + ip_bytes + port_bytes
+1688    }
+1689
+1690    /// Unconditionally write this frame to `buf`
+1691    pub(crate) fn write<W: BufMut>(&self, buf: &mut W) {
+1692        buf.write(self.get_type());
+1693        buf.write(self.round);
+1694        match self.ip {
+1695            IpAddr::V4(ipv4_addr) => {
+1696                buf.write(ipv4_addr);
+1697            }
+1698            IpAddr::V6(ipv6_addr) => {
+1699                buf.write(ipv6_addr);
+1700            }
+1701        }
+1702        buf.write::<u16>(self.port);
+1703    }
+1704
+1705    /// Read the frame contents from the buffer
+1706    ///
+1707    /// Should only be called when the frame type has been identified as
+1708    /// [`FrameType::REACH_OUT_AT_IPV4`] or [`FrameType::REACH_OUT_AT_IPV6`].
+1709    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1710        let round = bytes.get()?;
+1711        let ip = if is_ipv6 {
+1712            IpAddr::V6(bytes.get()?)
+1713        } else {
+1714            IpAddr::V4(bytes.get()?)
+1715        };
+1716        let port = bytes.get()?;
+1717        Ok(Self { round, ip, port })
+1718    }
+1719
+1720    /// Give the [`SocketAddr`] encoded in the frame
+1721    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1722        (self.ip, self.port).into()
+1723    }
+1724}
+1725
+1726/// Frame signaling an address is no longer being advertised
+1727#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
+1728// TODO(@divma): remove
+1729#[allow(dead_code)]
+1730pub(crate) struct RemoveAddress {
+1731    /// The sequence number of the address advertisement to be removed
+1732    pub(crate) seq_no: VarInt,
+1733}
+1734
+1735// TODO(@divma): remove
+1736#[allow(dead_code)]
+1737impl RemoveAddress {
+1738    /// [`FrameType`] of this frame
+1739    pub(crate) const TYPE: FrameType = FrameType::REMOVE_ADDRESS;
+1740
+1741    /// Smallest number of bytes this type of frame is guaranteed to fit within
+1742    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
+1743
+1744    pub(crate) const fn new(seq_no: VarInt) -> Self {
+1745        Self { seq_no }
+1746    }
+1747
+1748    /// Compute the number of bytes needed to encode the frame
+1749    pub(crate) const fn size(&self) -> usize {
+1750        let type_size = VarInt(Self::TYPE.0).size();
+1751        let seq_no_bytes = self.seq_no.size();
+1752        type_size + seq_no_bytes
+1753    }
+1754
+1755    /// Unconditionally write this frame to `buf`
+1756    pub(crate) fn write<W: BufMut>(&self, buf: &mut W) {
+1757        buf.write(Self::TYPE);
+1758        buf.write(self.seq_no);
+1759    }
+1760
+1761    /// Read the frame contents from the buffer
+1762    ///
+1763    /// Should only be called when the frame type has been identified as
+1764    /// [`FrameType::REMOVE_ADDRESS`].
+1765    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1766        Ok(Self {
+1767            seq_no: bytes.get()?,
+1768        })
+1769    }
+1770}
+1771
+1772#[cfg(test)]
+1773mod test {
+1774    use super::*;
+1775    use crate::coding::Codec;
+1776    use assert_matches::assert_matches;
+1777
+1778    #[track_caller]
+1779    fn frames(buf: Vec<u8>) -> Vec<Frame> {
+1780        Iter::new(Bytes::from(buf))
+1781            .unwrap()
+1782            .collect::<Result<Vec<_>, _>>()
+1783            .unwrap()
+1784    }
+1785
+1786    #[test]
+1787    fn ack_coding() {
+1788        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+1789        let mut ranges = ArrayRangeSet::new();
+1790        for &packet in PACKETS {
+1791            ranges.insert(packet..packet + 1);
+1792        }
+1793        let mut buf = Vec::new();
+1794        const ECN: EcnCounts = EcnCounts {
+1795            ect0: 42,
+1796            ect1: 24,
+1797            ce: 12,
+1798        };
+1799        Ack::encode(42, &ranges, Some(&ECN), &mut buf);
+1800        let frames = frames(buf);
+1801        assert_eq!(frames.len(), 1);
+1802        match frames[0] {
+1803            Frame::Ack(ref ack) => {
+1804                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
+1805                packets.sort_unstable();
+1806                assert_eq!(&packets[..], PACKETS);
+1807                assert_eq!(ack.ecn, Some(ECN));
+1808            }
+1809            ref x => panic!("incorrect frame {x:?}"),
+1810        }
+1811    }
+1812
+1813    #[test]
+1814    #[allow(clippy::range_plus_one)]
+1815    fn path_ack_coding() {
+1816        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
+1817        let mut ranges = ArrayRangeSet::new();
+1818        for &packet in PACKETS {
+1819            ranges.insert(packet..packet + 1);
+1820        }
+1821        let mut buf = Vec::new();
+1822        const ECN: EcnCounts = EcnCounts {
+1823            ect0: 42,
+1824            ect1: 24,
+1825            ce: 12,
+1826        };
+1827        const PATH_ID: PathId = PathId::MAX;
+1828        PathAck::encode(PATH_ID, 42, &ranges, Some(&ECN), &mut buf);
+1829        let frames = frames(buf);
+1830        assert_eq!(frames.len(), 1);
+1831        match frames[0] {
+1832            Frame::PathAck(ref ack) => {
+1833                assert_eq!(ack.path_id, PATH_ID);
+1834                let mut packets = ack.into_iter().flatten().collect::<Vec<_>>();
+1835                packets.sort_unstable();
+1836                assert_eq!(&packets[..], PACKETS);
+1837                assert_eq!(ack.ecn, Some(ECN));
+1838            }
+1839            ref x => panic!("incorrect frame {x:?}"),
+1840        }
+1841    }
+1842
+1843    #[test]
+1844    fn ack_frequency_coding() {
+1845        let mut buf = Vec::new();
+1846        let original = AckFrequency {
+1847            sequence: VarInt(42),
+1848            ack_eliciting_threshold: VarInt(20),
+1849            request_max_ack_delay: VarInt(50_000),
+1850            reordering_threshold: VarInt(1),
+1851        };
+1852        original.encode(&mut buf);
+1853        let frames = frames(buf);
+1854        assert_eq!(frames.len(), 1);
+1855        match &frames[0] {
+1856            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
+1857            x => panic!("incorrect frame {x:?}"),
+1858        }
+1859    }
+1860
+1861    #[test]
+1862    fn immediate_ack_coding() {
+1863        let mut buf = Vec::new();
+1864        FrameType::IMMEDIATE_ACK.encode(&mut buf);
+1865        let frames = frames(buf);
+1866        assert_eq!(frames.len(), 1);
+1867        assert_matches!(&frames[0], Frame::ImmediateAck);
+1868    }
+1869
+1870    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
+1871    #[test]
+1872    fn test_observed_addr_roundrip() {
+1873        let observed_addr = ObservedAddr {
+1874            seq_no: VarInt(42),
+1875            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+1876            port: 4242,
+1877        };
+1878        let mut buf = Vec::with_capacity(observed_addr.size());
+1879        observed_addr.write(&mut buf);
+1880
+1881        assert_eq!(
+1882            observed_addr.size(),
+1883            buf.len(),
+1884            "expected written bytes and actual size differ"
+1885        );
+1886
+1887        let mut decoded = frames(buf);
+1888        assert_eq!(decoded.len(), 1);
+1889        match decoded.pop().expect("non empty") {
+1890            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
+1891            x => panic!("incorrect frame {x:?}"),
+1892        }
+1893    }
+1894
+1895    #[test]
+1896    fn test_path_abandon_roundtrip() {
+1897        let abandon = PathAbandon {
+1898            path_id: PathId(42),
+1899            error_code: TransportErrorCode::NO_ERROR,
+1900        };
+1901        let mut buf = Vec::new();
+1902        abandon.encode(&mut buf);
+1903
+1904        let mut decoded = frames(buf);
+1905        assert_eq!(decoded.len(), 1);
+1906        match decoded.pop().expect("non empty") {
+1907            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
+1908            x => panic!("incorrect frame {x:?}"),
+1909        }
+1910    }
+1911
+1912    #[test]
+1913    fn test_path_status_available_roundtrip() {
+1914        let path_status_available = PathStatusAvailable {
+1915            path_id: PathId(42),
+1916            status_seq_no: VarInt(73),
+1917        };
+1918        let mut buf = Vec::new();
+1919        path_status_available.encode(&mut buf);
+1920
+1921        let mut decoded = frames(buf);
+1922        assert_eq!(decoded.len(), 1);
+1923        match decoded.pop().expect("non empty") {
+1924            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
+1925            x => panic!("incorrect frame {x:?}"),
+1926        }
+1927    }
+1928
+1929    #[test]
+1930    fn test_path_status_backup_roundtrip() {
+1931        let path_status_backup = PathStatusBackup {
+1932            path_id: PathId(42),
+1933            status_seq_no: VarInt(73),
+1934        };
+1935        let mut buf = Vec::new();
+1936        path_status_backup.encode(&mut buf);
+1937
+1938        let mut decoded = frames(buf);
+1939        assert_eq!(decoded.len(), 1);
+1940        match decoded.pop().expect("non empty") {
+1941            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
+1942            x => panic!("incorrect frame {x:?}"),
+1943        }
+1944    }
+1945
+1946    #[test]
+1947    fn test_path_new_connection_id_roundtrip() {
+1948        let cid = NewConnectionId {
+1949            path_id: Some(PathId(22)),
+1950            sequence: 31,
+1951            retire_prior_to: 13,
+1952            id: ConnectionId::new(&[0xAB; 8]),
+1953            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
+1954        };
+1955        let mut buf = Vec::new();
+1956        cid.encode(&mut buf);
+1957
+1958        let mut decoded = frames(buf);
+1959        assert_eq!(decoded.len(), 1);
+1960        match decoded.pop().expect("non empty") {
+1961            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
+1962            x => panic!("incorrect frame {x:?}"),
+1963        }
+1964    }
+1965
+1966    #[test]
+1967    fn test_path_retire_connection_id_roundtrip() {
+1968        let retire_cid = RetireConnectionId {
+1969            path_id: Some(PathId(22)),
+1970            sequence: 31,
+1971        };
+1972        let mut buf = Vec::new();
+1973        retire_cid.encode(&mut buf);
+1974
+1975        let mut decoded = frames(buf);
+1976        assert_eq!(decoded.len(), 1);
+1977        match decoded.pop().expect("non empty") {
+1978            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
+1979            x => panic!("incorrect frame {x:?}"),
+1980        }
+1981    }
+1982
+1983    #[test]
+1984    fn test_paths_blocked_path_cids_blocked_roundtrip() {
+1985        let mut buf = Vec::new();
+1986
+1987        let frame0 = PathsBlocked(PathId(22));
+1988        frame0.encode(&mut buf);
+1989        let frame1 = PathCidsBlocked {
+1990            path_id: PathId(23),
+1991            next_seq: VarInt(32),
+1992        };
+1993        frame1.encode(&mut buf);
+1994
+1995        let mut decoded = frames(buf);
+1996        assert_eq!(decoded.len(), 2);
+1997        match decoded.pop().expect("non empty") {
+1998            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
+1999            x => panic!("incorrect frame {x:?}"),
+2000        }
+2001        match decoded.pop().expect("non empty") {
+2002            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
+2003            x => panic!("incorrect frame {x:?}"),
+2004        }
+2005    }
+2006
+2007    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2008    #[test]
+2009    fn test_add_address_roundrip() {
+2010        let add_address = AddAddress {
+2011            seq_no: VarInt(42),
+2012            ip: std::net::Ipv4Addr::LOCALHOST.into(),
+2013            port: 4242,
+2014        };
+2015        let mut buf = Vec::with_capacity(add_address.size());
+2016        add_address.write(&mut buf);
+2017
+2018        assert_eq!(
+2019            add_address.size(),
+2020            buf.len(),
+2021            "expected written bytes and actual size differ"
+2022        );
+2023
+2024        let mut decoded = frames(buf);
+2025        assert_eq!(decoded.len(), 1);
+2026        match decoded.pop().expect("non empty") {
+2027            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
+2028            x => panic!("incorrect frame {x:?}"),
+2029        }
+2030    }
+2031
+2032    /// Test that encoding and decoding [`AddAddress`] produces the same result
+2033    #[test]
+2034    fn test_reach_out_roundrip() {
+2035        let reach_out = ReachOut {
+2036            round: VarInt(42),
+2037            ip: std::net::Ipv6Addr::LOCALHOST.into(),
+2038            port: 4242,
+2039        };
+2040        let mut buf = Vec::with_capacity(reach_out.size());
+2041        reach_out.write(&mut buf);
+2042
+2043        assert_eq!(
+2044            reach_out.size(),
+2045            buf.len(),
+2046            "expected written bytes and actual size differ"
+2047        );
+2048
+2049        let mut decoded = frames(buf);
+2050        assert_eq!(decoded.len(), 1);
+2051        match decoded.pop().expect("non empty") {
+2052            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
+2053            x => panic!("incorrect frame {x:?}"),
+2054        }
+2055    }
+2056
+2057    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
+2058    #[test]
+2059    fn test_remove_address_roundrip() {
+2060        let remove_addr = RemoveAddress::new(VarInt(10));
+2061        let mut buf = Vec::with_capacity(remove_addr.size());
+2062        remove_addr.write(&mut buf);
+2063
+2064        assert_eq!(
+2065            remove_addr.size(),
+2066            buf.len(),
+2067            "expected written bytes and actual size differ"
+2068        );
+2069
+2070        let mut decoded = frames(buf);
+2071        assert_eq!(decoded.len(), 1);
+2072        match decoded.pop().expect("non empty") {
+2073            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
+2074            x => panic!("incorrect frame {x:?}"),
+2075        }
+2076    }
+2077}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/iroh_hp.rs.html b/pr/234/docs/src/iroh_quinn_proto/iroh_hp.rs.html new file mode 100644 index 000000000..927aebfdf --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/iroh_hp.rs.html @@ -0,0 +1,419 @@ +iroh_hp.rs - source

iroh_quinn_proto/
iroh_hp.rs

1//! iroh NAT Traversal
+2
+3use std::{
+4    collections::hash_map::Entry,
+5    net::{IpAddr, SocketAddr},
+6};
+7
+8use identity_hash::IntMap;
+9use rustc_hash::{FxHashMap, FxHashSet};
+10use tracing::trace;
+11
+12use crate::{
+13    PathId, Side, VarInt,
+14    frame::{AddAddress, ReachOut, RemoveAddress},
+15};
+16
+17type IpPort = (IpAddr, u16);
+18
+19/// Errors that the nat traversal state might encounter.
+20#[derive(Debug, thiserror::Error)]
+21pub enum Error {
+22    /// An endpoint (local or remote) tried to add too many addresses to their advertised set
+23    #[error("Tried to add too many addresses to their advertised set")]
+24    TooManyAddresses,
+25    /// The operation is not allowed for this endpoint's connection side
+26    #[error("Not allowed for this endpoint's connection side")]
+27    WrongConnectionSide,
+28    /// The extension was not negotiated
+29    #[error("Iroh's nat traversal was not negotiated")]
+30    ExtensionNotNegotiated,
+31    /// Not enough addresses to complete the operation
+32    #[error("Not enough addresses")]
+33    NotEnoughAddresses,
+34    /// Nat traversal attempt failed due to a multipath error
+35    #[error("Failed to establish paths {0}")]
+36    Multipath(super::PathError),
+37}
+38
+39pub(crate) struct NatTraversalRound {
+40    /// Sequence number to use for the new reach out frames
+41    pub(crate) new_round: VarInt,
+42    /// Addresses to use to send reach out frames
+43    pub(crate) reach_out_at: Vec<(IpAddr, u16)>,
+44    /// Remotes to probe by attempting to open new paths
+45    pub(crate) addresses_to_probe: Vec<(IpAddr, u16)>,
+46    /// [`PathId`]s of the cancelled round
+47    pub(crate) prev_round_path_ids: Vec<PathId>,
+48}
+49
+50/// Event emitted when the client receives ADD_ADDRESS or REMOVE_ADDRESS frames.
+51#[derive(Debug, Clone)]
+52pub enum Event {
+53    /// An ADD_ADDRESS frame was received.
+54    AddressAdded(SocketAddr),
+55    /// A REMOVE_ADDRESS frame was received.
+56    AddressRemoved(SocketAddr),
+57}
+58
+59/// State kept for Iroh's nat traversal
+60#[derive(Debug, Default)]
+61pub(crate) enum State {
+62    #[default]
+63    NotNegotiated,
+64    ClientSide(ClientState),
+65    ServerSide(ServerState),
+66}
+67
+68#[derive(Debug)]
+69pub(crate) struct ClientState {
+70    /// Max number of remote addresses we allow
+71    ///
+72    /// This is set by the local endpoint.
+73    max_remote_addresses: usize,
+74    /// Max number of local addresses allowed
+75    ///
+76    /// This is set by the remote endpoint.
+77    max_local_addresses: usize,
+78    /// Candidate addresses the remote server reports as potentially reachable, to use for nat
+79    /// traversal attempts.
+80    remote_addresses: FxHashMap<VarInt, (IpAddr, u16)>,
+81    /// Candidate addresses the local client reports as potentially reachable, to use for nat
+82    /// traversal attempts. Always canonical.
+83    local_addresses: FxHashSet<(IpAddr, u16)>,
+84    /// Current nat holepunching round.
+85    round: VarInt,
+86    /// [`PathId`]s used to probe remotes assigned to this round.
+87    round_path_ids: Vec<PathId>,
+88}
+89
+90impl ClientState {
+91    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+92        Self {
+93            max_remote_addresses,
+94            max_local_addresses,
+95            remote_addresses: Default::default(),
+96            local_addresses: Default::default(),
+97            round: Default::default(),
+98            round_path_ids: Default::default(),
+99        }
+100    }
+101
+102    fn add_local_address(&mut self, address: IpPort) -> Result<(), Error> {
+103        if self.local_addresses.len() < self.max_local_addresses {
+104            self.local_addresses.insert(address);
+105            Ok(())
+106        } else if self.local_addresses.contains(&address) {
+107            // at capacity, but the address is known, no issues here
+108            Ok(())
+109        } else {
+110            // at capacity and the address is new
+111            Err(Error::TooManyAddresses)
+112        }
+113    }
+114
+115    fn remove_local_address(&mut self, address: &IpPort) {
+116        self.local_addresses.remove(address);
+117    }
+118
+119    /// Initiates a new nat traversal round.
+120    ///
+121    /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT`
+122    /// frames, and initiating probing of the known remote addresses. When a new round is
+123    /// initiated, the previous one is cancelled, and paths that have not been opened should be
+124    /// closed.
+125    pub(crate) fn initiate_nat_traversal_round(&mut self) -> Result<NatTraversalRound, Error> {
+126        if self.local_addresses.is_empty() {
+127            return Err(Error::NotEnoughAddresses);
+128        }
+129
+130        let prev_round_path_ids = std::mem::take(&mut self.round_path_ids);
+131        self.round = self.round.saturating_add(1u8);
+132
+133        Ok(NatTraversalRound {
+134            new_round: self.round,
+135            reach_out_at: self.local_addresses.iter().copied().collect(),
+136            addresses_to_probe: self.remote_addresses.values().copied().collect(),
+137            prev_round_path_ids,
+138        })
+139    }
+140
+141    /// Add a [`PathId`] as part of the current attempts to create paths based on the server's
+142    /// advertised addresses.
+143    pub(crate) fn set_round_path_ids(&mut self, path_ids: Vec<PathId>) {
+144        self.round_path_ids = path_ids;
+145    }
+146
+147    /// Adds an address to the remote set
+148    ///
+149    /// On success returns the address if it was new to the set. It will error when the set has no
+150    /// capacity for the address.
+151    pub(crate) fn add_remote_address(
+152        &mut self,
+153        add_addr: AddAddress,
+154    ) -> Result<Option<SocketAddr>, Error> {
+155        let AddAddress { seq_no, ip, port } = add_addr;
+156        let address = (ip, port);
+157        let allow_new = self.remote_addresses.len() < self.max_remote_addresses;
+158        match self.remote_addresses.entry(seq_no) {
+159            Entry::Occupied(mut occupied_entry) => {
+160                let old_value = occupied_entry.insert(address);
+161                // The value might be different. This should not happen, but we assume that the new
+162                // address is more recent than the previous, and thus worth updating
+163                Ok((address != old_value).then_some(address.into()))
+164            }
+165            Entry::Vacant(vacant_entry) if allow_new => {
+166                vacant_entry.insert(address);
+167                Ok(Some(address.into()))
+168            }
+169            _ => Err(Error::TooManyAddresses),
+170        }
+171    }
+172
+173    /// Removes an address from the remote set
+174    ///
+175    /// Returns whether the address was present.
+176    pub(crate) fn remove_remote_address(
+177        &mut self,
+178        remove_addr: RemoveAddress,
+179    ) -> Option<SocketAddr> {
+180        self.remote_addresses
+181            .remove(&remove_addr.seq_no)
+182            .map(Into::into)
+183    }
+184
+185    /// Checks that a received remote address is valid
+186    ///
+187    /// An address is valid as long as it does not change the value of a known address id.
+188    pub(crate) fn check_remote_address(&self, add_addr: &AddAddress) -> bool {
+189        let existing = self.remote_addresses.get(&add_addr.seq_no);
+190        existing.is_none() || existing == Some(&add_addr.ip_port())
+191    }
+192
+193    pub(crate) fn get_remote_nat_traversal_addresses(&self) -> Vec<SocketAddr> {
+194        self.remote_addresses
+195            .values()
+196            .copied()
+197            .map(Into::into)
+198            .collect()
+199    }
+200}
+201
+202#[derive(Debug)]
+203pub(crate) struct ServerState {
+204    /// Max number of remote addresses we allow.
+205    ///
+206    /// This is set by the local endpoint.
+207    max_remote_addresses: usize,
+208    /// Max number of local addresses allowed.
+209    ///
+210    /// This is set by the remote endpoint.
+211    max_local_addresses: usize,
+212    /// Candidate addresses the server reports as potentially reachable, to use for nat
+213    /// traversal attempts.
+214    local_addresses: FxHashMap<IpPort, VarInt>,
+215    /// The next id to use for local addresses sent to the client.
+216    next_local_addr_id: VarInt,
+217    /// Current nat holepunching round
+218    ///
+219    /// Servers keep track of the client's most recent round and cancel probing related to previous
+220    /// rounds.
+221    round: VarInt,
+222    /// Addresses to which PATH_CHALLENGES need to be sent.
+223    pending_probes: FxHashSet<IpPort>,
+224    /// Sent PATH_CHALLENGES for this round.
+225    ///
+226    /// This is used to validate the remotes assigned to each token.
+227    active_probes: IntMap<u64, IpPort>,
+228}
+229
+230impl ServerState {
+231    fn new(max_remote_addresses: usize, max_local_addresses: usize) -> Self {
+232        Self {
+233            max_remote_addresses,
+234            max_local_addresses,
+235            local_addresses: Default::default(),
+236            next_local_addr_id: Default::default(),
+237            round: Default::default(),
+238            pending_probes: Default::default(),
+239            active_probes: Default::default(),
+240        }
+241    }
+242
+243    fn add_local_address(&mut self, address: IpPort) -> Result<Option<AddAddress>, Error> {
+244        let allow_new = self.local_addresses.len() < self.max_local_addresses;
+245        match self.local_addresses.entry(address) {
+246            Entry::Occupied(_) => Ok(None),
+247            Entry::Vacant(vacant_entry) if allow_new => {
+248                let id = self.next_local_addr_id;
+249                self.next_local_addr_id = self.next_local_addr_id.saturating_add(1u8);
+250                vacant_entry.insert(id);
+251                Ok(Some(AddAddress::new(address, id)))
+252            }
+253            _ => Err(Error::TooManyAddresses),
+254        }
+255    }
+256
+257    fn remove_local_address(&mut self, address: &IpPort) -> Option<RemoveAddress> {
+258        self.local_addresses.remove(address).map(RemoveAddress::new)
+259    }
+260
+261    /// Handles a received [`ReachOut`].
+262    ///
+263    /// It returns the token that should be sent in response to this frame as a challenge, and
+264    /// whether this starts a new nat traversal round.
+265    ///
+266    /// If this frame was ignored, it returns `None`.
+267    pub(crate) fn handle_reach_out(&mut self, reach_out: ReachOut) -> Result<(), Error> {
+268        let ReachOut { round, ip, port } = reach_out;
+269
+270        if round < self.round {
+271            trace!(current_round=%self.round, "ignoring REACH_OUT for previous round");
+272            return Ok(());
+273        }
+274
+275        if round > self.round {
+276            self.pending_probes.clear();
+277            // TODO(@divma): This log is here because I'm not sure if dropping the challenges
+278            // without further interaction with the connection is going to cause issues.
+279            for (token, remote) in self.active_probes.drain() {
+280                let remote: SocketAddr = remote.into();
+281                trace!(token=format!("{:08x}", token), %remote, "dropping nat traversal challenge pending response");
+282            }
+283        } else if self.pending_probes.len() >= self.max_remote_addresses {
+284            return Err(Error::TooManyAddresses);
+285        }
+286        self.pending_probes.insert((ip, port));
+287        Ok(())
+288    }
+289
+290    pub(crate) fn next_probe(&mut self) -> Option<ServerProbing<'_>> {
+291        self.pending_probes
+292            .iter()
+293            .next()
+294            .copied()
+295            .map(|remote| ServerProbing {
+296                remote,
+297                pending_probes: &mut self.pending_probes,
+298                active_probes: &mut self.active_probes,
+299            })
+300    }
+301}
+302
+303pub(crate) struct ServerProbing<'a> {
+304    remote: IpPort,
+305    pending_probes: &'a mut FxHashSet<IpPort>,
+306    active_probes: &'a mut IntMap<u64, IpPort>,
+307}
+308
+309impl<'a> ServerProbing<'a> {
+310    pub(crate) fn finish(self, token: u64) {
+311        self.pending_probes.remove(&self.remote);
+312        self.active_probes.insert(token, self.remote);
+313    }
+314
+315    pub(crate) fn remote(&self) -> SocketAddr {
+316        self.remote.into()
+317    }
+318}
+319
+320impl State {
+321    pub(crate) fn new(max_remote_addresses: u8, max_local_addresses: u8, side: Side) -> Self {
+322        match side {
+323            Side::Client => Self::ClientSide(ClientState::new(
+324                max_remote_addresses.into(),
+325                max_local_addresses.into(),
+326            )),
+327            Side::Server => Self::ServerSide(ServerState::new(
+328                max_remote_addresses.into(),
+329                max_local_addresses.into(),
+330            )),
+331        }
+332    }
+333
+334    pub(crate) fn client_side(&self) -> Result<&ClientState, Error> {
+335        match self {
+336            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+337            Self::ClientSide(client_side) => Ok(client_side),
+338            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+339        }
+340    }
+341
+342    pub(crate) fn client_side_mut(&mut self) -> Result<&mut ClientState, Error> {
+343        match self {
+344            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+345            Self::ClientSide(client_side) => Ok(client_side),
+346            Self::ServerSide(_) => Err(Error::WrongConnectionSide),
+347        }
+348    }
+349
+350    pub(crate) fn server_side_mut(&mut self) -> Result<&mut ServerState, Error> {
+351        match self {
+352            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+353            Self::ClientSide(_) => Err(Error::WrongConnectionSide),
+354            Self::ServerSide(server_side) => Ok(server_side),
+355        }
+356    }
+357
+358    /// Adds a local address to use for nat traversal.
+359    ///
+360    /// When this endpoint is the server within the connection, these addresses will be sent to the
+361    /// client in add address frames. For clients, these addresses will be sent in reach out frames
+362    /// when nat traversal attempts are initiated.
+363    ///
+364    /// If a frame should be sent, it is returned.
+365    pub(crate) fn add_local_address(
+366        &mut self,
+367        address: SocketAddr,
+368    ) -> Result<Option<AddAddress>, Error> {
+369        match self {
+370            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+371            Self::ClientSide(client_state) => {
+372                client_state.add_local_address((address.ip(), address.port()))?;
+373                Ok(None)
+374            }
+375            Self::ServerSide(server_state) => {
+376                server_state.add_local_address((address.ip(), address.port()))
+377            }
+378        }
+379    }
+380
+381    /// Removes a local address from the advertised set for nat traversal.
+382    ///
+383    /// When this endpoint is the server, removed addresses must be reported with remove address
+384    /// frames. Clients will simply stop reporting these addresses in reach out frames.
+385    ///
+386    /// If a frame should be sent, it is returned.
+387    pub(crate) fn remove_local_address(
+388        &mut self,
+389        address: SocketAddr,
+390    ) -> Result<Option<RemoveAddress>, Error> {
+391        let address = &(address.ip(), address.port());
+392        match self {
+393            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+394            Self::ClientSide(client_state) => {
+395                client_state.remove_local_address(address);
+396                Ok(None)
+397            }
+398            Self::ServerSide(server_state) => Ok(server_state.remove_local_address(address)),
+399        }
+400    }
+401
+402    pub(crate) fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, Error> {
+403        match self {
+404            Self::NotNegotiated => Err(Error::ExtensionNotNegotiated),
+405            Self::ClientSide(client_state) => Ok(client_state
+406                .local_addresses
+407                .iter()
+408                .copied()
+409                .map(Into::into)
+410                .collect()),
+411            Self::ServerSide(server_state) => Ok(server_state
+412                .local_addresses
+413                .keys()
+414                .copied()
+415                .map(Into::into)
+416                .collect()),
+417        }
+418    }
+419}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/lib.rs.html b/pr/234/docs/src/iroh_quinn_proto/lib.rs.html new file mode 100644 index 000000000..22666495b --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/lib.rs.html @@ -0,0 +1,340 @@ +lib.rs - source

iroh_quinn_proto/
lib.rs

1//! Low-level protocol logic for the QUIC protoocol
+2//!
+3//! quinn-proto contains a fully deterministic implementation of QUIC protocol logic. It contains
+4//! no networking code and does not get any relevant timestamps from the operating system. Most
+5//! users may want to use the futures-based quinn API instead.
+6//!
+7//! The quinn-proto API might be of interest if you want to use it from a C or C++ project
+8//! through C bindings or if you want to use a different event loop than the one tokio provides.
+9//!
+10//! The most important types are `Endpoint`, which conceptually represents the protocol state for
+11//! a single socket and mostly manages configuration and dispatches incoming datagrams to the
+12//! related `Connection`. `Connection` types contain the bulk of the protocol logic related to
+13//! managing a single connection and all the related state (such as streams).
+14
+15#![cfg_attr(not(fuzzing), warn(missing_docs))]
+16#![cfg_attr(test, allow(dead_code))]
+17// Fixes welcome:
+18#![allow(clippy::too_many_arguments)]
+19#![warn(unreachable_pub)]
+20#![warn(clippy::use_self)]
+21
+22use std::{
+23    fmt,
+24    net::{IpAddr, SocketAddr},
+25    ops,
+26};
+27
+28mod cid_queue;
+29pub mod coding;
+30mod constant_time;
+31mod range_set;
+32#[cfg(all(test, any(feature = "rustls-aws-lc-rs", feature = "rustls-ring")))]
+33mod tests;
+34pub mod transport_parameters;
+35mod varint;
+36
+37pub use varint::{VarInt, VarIntBoundsExceeded};
+38
+39#[cfg(feature = "bloom")]
+40mod bloom_token_log;
+41#[cfg(feature = "bloom")]
+42pub use bloom_token_log::BloomTokenLog;
+43
+44mod connection;
+45pub use crate::connection::{
+46    Chunk, Chunks, ClosePathError, ClosedPath, ClosedStream, Connection, ConnectionError,
+47    ConnectionStats, Datagrams, Event, FinishError, FrameStats, PathError, PathEvent, PathId,
+48    PathStats, PathStatus, ReadError, ReadableError, RecvStream, RttEstimator, SendDatagramError,
+49    SendStream, SetPathStatusError, ShouldTransmit, StreamEvent, Streams, UdpStats, WriteError,
+50    Written,
+51};
+52
+53#[cfg(feature = "rustls")]
+54pub use rustls;
+55
+56mod config;
+57#[cfg(doc)]
+58pub use config::DEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED;
+59pub use config::{
+60    AckFrequencyConfig, ClientConfig, ConfigError, EndpointConfig, IdleTimeout, MtuDiscoveryConfig,
+61    ServerConfig, StdSystemTime, TimeSource, TransportConfig, ValidationTokenConfig,
+62};
+63#[cfg(feature = "qlog")]
+64pub use config::{QlogConfig, QlogFactory, QlogFileFactory};
+65
+66pub mod crypto;
+67
+68mod frame;
+69use crate::frame::Frame;
+70pub use crate::frame::{ApplicationClose, ConnectionClose, Datagram, FrameType};
+71
+72mod endpoint;
+73pub use crate::endpoint::{
+74    AcceptError, ConnectError, ConnectionHandle, DatagramEvent, Endpoint, Incoming, RetryError,
+75};
+76
+77mod packet;
+78pub use packet::{
+79    ConnectionIdParser, FixedLengthConnectionIdParser, LongType, PacketDecodeError, PartialDecode,
+80    ProtectedHeader, ProtectedInitialHeader,
+81};
+82
+83mod shared;
+84pub use crate::shared::{ConnectionEvent, ConnectionId, EcnCodepoint, EndpointEvent};
+85
+86mod transport_error;
+87pub use crate::transport_error::{Code as TransportErrorCode, Error as TransportError};
+88
+89pub mod congestion;
+90
+91mod cid_generator;
+92pub use crate::cid_generator::{
+93    ConnectionIdGenerator, HashedConnectionIdGenerator, InvalidCid, RandomConnectionIdGenerator,
+94};
+95
+96mod token;
+97use token::ResetToken;
+98pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore};
+99
+100mod address_discovery;
+101
+102mod token_memory_cache;
+103pub use token_memory_cache::TokenMemoryCache;
+104
+105pub mod iroh_hp;
+106
+107#[cfg(feature = "arbitrary")]
+108use arbitrary::Arbitrary;
+109
+110// Deal with time
+111#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
+112pub(crate) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+113#[cfg(all(target_family = "wasm", target_os = "unknown"))]
+114pub(crate) use web_time::{Duration, Instant, SystemTime, UNIX_EPOCH};
+115
+116#[cfg(fuzzing)]
+117pub mod fuzzing {
+118    pub use crate::connection::{Retransmits, State as ConnectionState, StreamsState};
+119    pub use crate::frame::ResetStream;
+120    pub use crate::packet::PartialDecode;
+121    pub use crate::transport_parameters::TransportParameters;
+122    pub use bytes::{BufMut, BytesMut};
+123
+124    #[cfg(feature = "arbitrary")]
+125    use arbitrary::{Arbitrary, Result, Unstructured};
+126
+127    #[cfg(feature = "arbitrary")]
+128    impl<'arbitrary> Arbitrary<'arbitrary> for TransportParameters {
+129        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+130            Ok(Self {
+131                initial_max_streams_bidi: u.arbitrary()?,
+132                initial_max_streams_uni: u.arbitrary()?,
+133                ack_delay_exponent: u.arbitrary()?,
+134                max_udp_payload_size: u.arbitrary()?,
+135                ..Self::default()
+136            })
+137        }
+138    }
+139
+140    #[derive(Debug)]
+141    pub struct PacketParams {
+142        pub local_cid_len: usize,
+143        pub buf: BytesMut,
+144        pub grease_quic_bit: bool,
+145    }
+146
+147    #[cfg(feature = "arbitrary")]
+148    impl<'arbitrary> Arbitrary<'arbitrary> for PacketParams {
+149        fn arbitrary(u: &mut Unstructured<'arbitrary>) -> Result<Self> {
+150            let local_cid_len: usize = u.int_in_range(0..=crate::MAX_CID_SIZE)?;
+151            let bytes: Vec<u8> = Vec::arbitrary(u)?;
+152            let mut buf = BytesMut::new();
+153            buf.put_slice(&bytes[..]);
+154            Ok(Self {
+155                local_cid_len,
+156                buf,
+157                grease_quic_bit: bool::arbitrary(u)?,
+158            })
+159        }
+160    }
+161}
+162
+163/// The QUIC protocol version implemented.
+164pub const DEFAULT_SUPPORTED_VERSIONS: &[u32] = &[
+165    0x00000001,
+166    0xff00_001d,
+167    0xff00_001e,
+168    0xff00_001f,
+169    0xff00_0020,
+170    0xff00_0021,
+171    0xff00_0022,
+172];
+173
+174/// Whether an endpoint was the initiator of a connection
+175#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+176#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+177pub enum Side {
+178    /// The initiator of a connection
+179    Client = 0,
+180    /// The acceptor of a connection
+181    Server = 1,
+182}
+183
+184impl Side {
+185    #[inline]
+186    /// Shorthand for `self == Side::Client`
+187    pub fn is_client(self) -> bool {
+188        self == Self::Client
+189    }
+190
+191    #[inline]
+192    /// Shorthand for `self == Side::Server`
+193    pub fn is_server(self) -> bool {
+194        self == Self::Server
+195    }
+196}
+197
+198impl ops::Not for Side {
+199    type Output = Self;
+200    fn not(self) -> Self {
+201        match self {
+202            Self::Client => Self::Server,
+203            Self::Server => Self::Client,
+204        }
+205    }
+206}
+207
+208/// Whether a stream communicates data in both directions or only from the initiator
+209#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+210#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+211pub enum Dir {
+212    /// Data flows in both directions
+213    Bi = 0,
+214    /// Data flows only from the stream's initiator
+215    Uni = 1,
+216}
+217
+218impl Dir {
+219    fn iter() -> impl Iterator<Item = Self> {
+220        [Self::Bi, Self::Uni].iter().cloned()
+221    }
+222}
+223
+224impl fmt::Display for Dir {
+225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+226        use Dir::*;
+227        f.pad(match *self {
+228            Bi => "bidirectional",
+229            Uni => "unidirectional",
+230        })
+231    }
+232}
+233
+234/// Identifier for a stream within a particular connection
+235#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+236#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+237pub struct StreamId(u64);
+238
+239impl fmt::Display for StreamId {
+240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+241        let initiator = match self.initiator() {
+242            Side::Client => "client",
+243            Side::Server => "server",
+244        };
+245        let dir = match self.dir() {
+246            Dir::Uni => "uni",
+247            Dir::Bi => "bi",
+248        };
+249        write!(
+250            f,
+251            "{} {}directional stream {}",
+252            initiator,
+253            dir,
+254            self.index()
+255        )
+256    }
+257}
+258
+259impl StreamId {
+260    /// Create a new StreamId
+261    pub fn new(initiator: Side, dir: Dir, index: u64) -> Self {
+262        Self((index << 2) | ((dir as u64) << 1) | initiator as u64)
+263    }
+264    /// Which side of a connection initiated the stream
+265    pub fn initiator(self) -> Side {
+266        if self.0 & 0x1 == 0 {
+267            Side::Client
+268        } else {
+269            Side::Server
+270        }
+271    }
+272    /// Which directions data flows in
+273    pub fn dir(self) -> Dir {
+274        if self.0 & 0x2 == 0 { Dir::Bi } else { Dir::Uni }
+275    }
+276    /// Distinguishes streams of the same initiator and directionality
+277    pub fn index(self) -> u64 {
+278        self.0 >> 2
+279    }
+280}
+281
+282impl From<StreamId> for VarInt {
+283    fn from(x: StreamId) -> Self {
+284        unsafe { Self::from_u64_unchecked(x.0) }
+285    }
+286}
+287
+288impl From<VarInt> for StreamId {
+289    fn from(v: VarInt) -> Self {
+290        Self(v.0)
+291    }
+292}
+293
+294impl From<StreamId> for u64 {
+295    fn from(x: StreamId) -> Self {
+296        x.0
+297    }
+298}
+299
+300impl coding::Codec for StreamId {
+301    fn decode<B: bytes::Buf>(buf: &mut B) -> coding::Result<Self> {
+302        VarInt::decode(buf).map(|x| Self(x.into_inner()))
+303    }
+304    fn encode<B: bytes::BufMut>(&self, buf: &mut B) {
+305        VarInt::from_u64(self.0).unwrap().encode(buf);
+306    }
+307}
+308
+309/// An outgoing packet
+310#[derive(Debug)]
+311#[must_use]
+312pub struct Transmit {
+313    /// The socket this datagram should be sent to
+314    pub destination: SocketAddr,
+315    /// Explicit congestion notification bits to set on the packet
+316    pub ecn: Option<EcnCodepoint>,
+317    /// Amount of data written to the caller-supplied buffer
+318    pub size: usize,
+319    /// The segment size if this transmission contains multiple datagrams.
+320    /// This is `None` if the transmit only contains a single datagram
+321    pub segment_size: Option<usize>,
+322    /// Optional source IP address for the datagram
+323    pub src_ip: Option<IpAddr>,
+324}
+325
+326//
+327// Useful internal constants
+328//
+329
+330/// The maximum number of CIDs we bother to issue per path
+331const LOC_CID_COUNT: u64 = 12;
+332const RESET_TOKEN_SIZE: usize = 16;
+333const MAX_CID_SIZE: usize = 20;
+334const MIN_INITIAL_SIZE: u16 = 1200;
+335/// <https://www.rfc-editor.org/rfc/rfc9000.html#name-datagram-size>
+336const INITIAL_MTU: u16 = 1200;
+337const MAX_UDP_PAYLOAD: u16 = 65527;
+338const TIMER_GRANULARITY: Duration = Duration::from_millis(1);
+339/// Maximum number of streams that can be uniquely identified by a stream ID
+340const MAX_STREAM_COUNT: u64 = 1 << 60;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/packet.rs.html b/pr/234/docs/src/iroh_quinn_proto/packet.rs.html new file mode 100644 index 000000000..297af0dfa --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/packet.rs.html @@ -0,0 +1,1053 @@ +packet.rs - source

iroh_quinn_proto/
packet.rs

1use std::{cmp::Ordering, io, ops::Range, str};
+2
+3use bytes::{Buf, BufMut, Bytes, BytesMut};
+4use thiserror::Error;
+5
+6use crate::{
+7    ConnectionId, PathId,
+8    coding::{self, BufExt, BufMutExt},
+9    crypto,
+10};
+11
+12/// Decodes a QUIC packet's invariant header
+13///
+14/// Due to packet number encryption, it is impossible to fully decode a header
+15/// (which includes a variable-length packet number) without crypto context.
+16/// The crypto context (represented by the `Crypto` type in Quinn) is usually
+17/// part of the `Connection`, or can be derived from the destination CID for
+18/// Initial packets.
+19///
+20/// To cope with this, we decode the invariant header (which should be stable
+21/// across QUIC versions), which gives us the destination CID and allows us
+22/// to inspect the version and packet type (which depends on the version).
+23/// This information allows us to fully decode and decrypt the packet.
+24#[cfg_attr(test, derive(Clone))]
+25#[derive(Debug)]
+26pub struct PartialDecode {
+27    plain_header: ProtectedHeader,
+28    buf: io::Cursor<BytesMut>,
+29}
+30
+31#[allow(clippy::len_without_is_empty)]
+32impl PartialDecode {
+33    /// Begin decoding a QUIC packet from `bytes`, returning any trailing data not part of that packet
+34    pub fn new(
+35        bytes: BytesMut,
+36        cid_parser: &(impl ConnectionIdParser + ?Sized),
+37        supported_versions: &[u32],
+38        grease_quic_bit: bool,
+39    ) -> Result<(Self, Option<BytesMut>), PacketDecodeError> {
+40        let mut buf = io::Cursor::new(bytes);
+41        let plain_header =
+42            ProtectedHeader::decode(&mut buf, cid_parser, supported_versions, grease_quic_bit)?;
+43        let dgram_len = buf.get_ref().len();
+44        let packet_len = plain_header
+45            .payload_len()
+46            .map(|len| (buf.position() + len) as usize)
+47            .unwrap_or(dgram_len);
+48        match dgram_len.cmp(&packet_len) {
+49            Ordering::Equal => Ok((Self { plain_header, buf }, None)),
+50            Ordering::Less => Err(PacketDecodeError::InvalidHeader(
+51                "packet too short to contain payload length",
+52            )),
+53            Ordering::Greater => {
+54                let rest = Some(buf.get_mut().split_off(packet_len));
+55                Ok((Self { plain_header, buf }, rest))
+56            }
+57        }
+58    }
+59
+60    /// The underlying partially-decoded packet data
+61    pub(crate) fn data(&self) -> &[u8] {
+62        self.buf.get_ref()
+63    }
+64
+65    pub(crate) fn initial_header(&self) -> Option<&ProtectedInitialHeader> {
+66        self.plain_header.as_initial()
+67    }
+68
+69    pub(crate) fn has_long_header(&self) -> bool {
+70        !matches!(self.plain_header, ProtectedHeader::Short { .. })
+71    }
+72
+73    pub(crate) fn is_initial(&self) -> bool {
+74        self.space() == Some(SpaceId::Initial)
+75    }
+76
+77    pub(crate) fn space(&self) -> Option<SpaceId> {
+78        use ProtectedHeader::*;
+79        match self.plain_header {
+80            Initial { .. } => Some(SpaceId::Initial),
+81            Long {
+82                ty: LongType::Handshake,
+83                ..
+84            } => Some(SpaceId::Handshake),
+85            Long {
+86                ty: LongType::ZeroRtt,
+87                ..
+88            } => Some(SpaceId::Data),
+89            Short { .. } => Some(SpaceId::Data),
+90            _ => None,
+91        }
+92    }
+93
+94    pub(crate) fn is_0rtt(&self) -> bool {
+95        match self.plain_header {
+96            ProtectedHeader::Long { ty, .. } => ty == LongType::ZeroRtt,
+97            _ => false,
+98        }
+99    }
+100
+101    /// The destination connection ID of the packet
+102    pub fn dst_cid(&self) -> ConnectionId {
+103        self.plain_header.dst_cid()
+104    }
+105
+106    /// Length of QUIC packet being decoded
+107    #[allow(unreachable_pub)] // fuzzing only
+108    pub fn len(&self) -> usize {
+109        self.buf.get_ref().len()
+110    }
+111
+112    pub(crate) fn finish(
+113        self,
+114        header_crypto: Option<&dyn crypto::HeaderKey>,
+115    ) -> Result<Packet, PacketDecodeError> {
+116        use ProtectedHeader::*;
+117        let Self {
+118            plain_header,
+119            mut buf,
+120        } = self;
+121
+122        if let Initial(ProtectedInitialHeader {
+123            dst_cid,
+124            src_cid,
+125            token_pos,
+126            version,
+127            ..
+128        }) = plain_header
+129        {
+130            let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+131            let header_len = buf.position() as usize;
+132            let mut bytes = buf.into_inner();
+133
+134            let header_data = bytes.split_to(header_len).freeze();
+135            let token = header_data.slice(token_pos.start..token_pos.end);
+136            return Ok(Packet {
+137                header: Header::Initial(InitialHeader {
+138                    dst_cid,
+139                    src_cid,
+140                    token,
+141                    number,
+142                    version,
+143                }),
+144                header_data,
+145                payload: bytes,
+146            });
+147        }
+148
+149        let header = match plain_header {
+150            Long {
+151                ty,
+152                dst_cid,
+153                src_cid,
+154                version,
+155                ..
+156            } => Header::Long {
+157                ty,
+158                dst_cid,
+159                src_cid,
+160                number: Self::decrypt_header(&mut buf, header_crypto.unwrap())?,
+161                version,
+162            },
+163            Retry {
+164                dst_cid,
+165                src_cid,
+166                version,
+167            } => Header::Retry {
+168                dst_cid,
+169                src_cid,
+170                version,
+171            },
+172            Short { spin, dst_cid, .. } => {
+173                let number = Self::decrypt_header(&mut buf, header_crypto.unwrap())?;
+174                let key_phase = buf.get_ref()[0] & KEY_PHASE_BIT != 0;
+175                Header::Short {
+176                    spin,
+177                    key_phase,
+178                    dst_cid,
+179                    number,
+180                }
+181            }
+182            VersionNegotiate {
+183                random,
+184                dst_cid,
+185                src_cid,
+186            } => Header::VersionNegotiate {
+187                random,
+188                dst_cid,
+189                src_cid,
+190            },
+191            Initial { .. } => unreachable!(),
+192        };
+193
+194        let header_len = buf.position() as usize;
+195        let mut bytes = buf.into_inner();
+196        Ok(Packet {
+197            header,
+198            header_data: bytes.split_to(header_len).freeze(),
+199            payload: bytes,
+200        })
+201    }
+202
+203    fn decrypt_header(
+204        buf: &mut io::Cursor<BytesMut>,
+205        header_crypto: &dyn crypto::HeaderKey,
+206    ) -> Result<PacketNumber, PacketDecodeError> {
+207        let packet_length = buf.get_ref().len();
+208        let pn_offset = buf.position() as usize;
+209        if packet_length < pn_offset + 4 + header_crypto.sample_size() {
+210            return Err(PacketDecodeError::InvalidHeader(
+211                "packet too short to extract header protection sample",
+212            ));
+213        }
+214
+215        header_crypto.decrypt(pn_offset, buf.get_mut());
+216
+217        let len = PacketNumber::decode_len(buf.get_ref()[0]);
+218        PacketNumber::decode(len, buf)
+219    }
+220}
+221
+222/// A buffer that can tell how much has been written to it already
+223///
+224/// This is commonly used for when a buffer is passed and the user may not write past a
+225/// given size. It allows the user of such a buffer to know the current cursor position in
+226/// the buffer. The maximum write size is usually passed in the same unit as
+227/// [`BufLen::len`]: bytes since the buffer start.
+228pub(crate) trait BufLen {
+229    /// Returns the number of bytes written into the buffer so far
+230    fn len(&self) -> usize;
+231}
+232
+233impl BufLen for Vec<u8> {
+234    fn len(&self) -> usize {
+235        self.len()
+236    }
+237}
+238
+239pub(crate) struct Packet {
+240    pub(crate) header: Header,
+241    pub(crate) header_data: Bytes,
+242    pub(crate) payload: BytesMut,
+243}
+244
+245impl Packet {
+246    pub(crate) fn reserved_bits_valid(&self) -> bool {
+247        let mask = match self.header {
+248            Header::Short { .. } => SHORT_RESERVED_BITS,
+249            _ => LONG_RESERVED_BITS,
+250        };
+251        self.header_data[0] & mask == 0
+252    }
+253}
+254
+255pub(crate) struct InitialPacket {
+256    pub(crate) header: InitialHeader,
+257    pub(crate) header_data: Bytes,
+258    pub(crate) payload: BytesMut,
+259}
+260
+261impl From<InitialPacket> for Packet {
+262    fn from(x: InitialPacket) -> Self {
+263        Self {
+264            header: Header::Initial(x.header),
+265            header_data: x.header_data,
+266            payload: x.payload,
+267        }
+268    }
+269}
+270
+271#[cfg_attr(test, derive(Clone))]
+272#[derive(Debug)]
+273pub(crate) enum Header {
+274    Initial(InitialHeader),
+275    Long {
+276        ty: LongType,
+277        dst_cid: ConnectionId,
+278        src_cid: ConnectionId,
+279        number: PacketNumber,
+280        version: u32,
+281    },
+282    Retry {
+283        dst_cid: ConnectionId,
+284        src_cid: ConnectionId,
+285        version: u32,
+286    },
+287    Short {
+288        spin: bool,
+289        key_phase: bool,
+290        dst_cid: ConnectionId,
+291        number: PacketNumber,
+292    },
+293    VersionNegotiate {
+294        random: u8,
+295        src_cid: ConnectionId,
+296        dst_cid: ConnectionId,
+297    },
+298}
+299
+300impl Header {
+301    pub(crate) fn encode(&self, w: &mut (impl BufMut + BufLen)) -> PartialEncode {
+302        use Header::*;
+303        let start = w.len();
+304        match *self {
+305            Initial(InitialHeader {
+306                ref dst_cid,
+307                ref src_cid,
+308                ref token,
+309                number,
+310                version,
+311            }) => {
+312                w.write(u8::from(LongHeaderType::Initial) | number.tag());
+313                w.write(version);
+314                dst_cid.encode_long(w);
+315                src_cid.encode_long(w);
+316                w.write_var(token.len() as u64);
+317                w.put_slice(token);
+318                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+319                number.encode(w);
+320                PartialEncode {
+321                    start,
+322                    header_len: w.len() - start,
+323                    pn: Some((number.len(), true)),
+324                }
+325            }
+326            Long {
+327                ty,
+328                ref dst_cid,
+329                ref src_cid,
+330                number,
+331                version,
+332            } => {
+333                w.write(u8::from(LongHeaderType::Standard(ty)) | number.tag());
+334                w.write(version);
+335                dst_cid.encode_long(w);
+336                src_cid.encode_long(w);
+337                w.write::<u16>(0); // Placeholder for payload length; see `set_payload_length`
+338                number.encode(w);
+339                PartialEncode {
+340                    start,
+341                    header_len: w.len() - start,
+342                    pn: Some((number.len(), true)),
+343                }
+344            }
+345            Retry {
+346                ref dst_cid,
+347                ref src_cid,
+348                version,
+349            } => {
+350                w.write(u8::from(LongHeaderType::Retry));
+351                w.write(version);
+352                dst_cid.encode_long(w);
+353                src_cid.encode_long(w);
+354                PartialEncode {
+355                    start,
+356                    header_len: w.len() - start,
+357                    pn: None,
+358                }
+359            }
+360            Short {
+361                spin,
+362                key_phase,
+363                ref dst_cid,
+364                number,
+365            } => {
+366                w.write(
+367                    FIXED_BIT
+368                        | if key_phase { KEY_PHASE_BIT } else { 0 }
+369                        | if spin { SPIN_BIT } else { 0 }
+370                        | number.tag(),
+371                );
+372                w.put_slice(dst_cid);
+373                number.encode(w);
+374                PartialEncode {
+375                    start,
+376                    header_len: w.len() - start,
+377                    pn: Some((number.len(), false)),
+378                }
+379            }
+380            VersionNegotiate {
+381                ref random,
+382                ref dst_cid,
+383                ref src_cid,
+384            } => {
+385                w.write(0x80u8 | random);
+386                w.write::<u32>(0);
+387                dst_cid.encode_long(w);
+388                src_cid.encode_long(w);
+389                PartialEncode {
+390                    start,
+391                    header_len: w.len() - start,
+392                    pn: None,
+393                }
+394            }
+395        }
+396    }
+397
+398    /// Whether the packet is encrypted on the wire
+399    pub(crate) fn is_protected(&self) -> bool {
+400        !matches!(*self, Self::Retry { .. } | Self::VersionNegotiate { .. })
+401    }
+402
+403    pub(crate) fn number(&self) -> Option<PacketNumber> {
+404        use Header::*;
+405        Some(match *self {
+406            Initial(InitialHeader { number, .. }) => number,
+407            Long { number, .. } => number,
+408            Short { number, .. } => number,
+409            _ => {
+410                return None;
+411            }
+412        })
+413    }
+414
+415    pub(crate) fn space(&self) -> SpaceId {
+416        use Header::*;
+417        match *self {
+418            Short { .. } => SpaceId::Data,
+419            Long {
+420                ty: LongType::ZeroRtt,
+421                ..
+422            } => SpaceId::Data,
+423            Long {
+424                ty: LongType::Handshake,
+425                ..
+426            } => SpaceId::Handshake,
+427            _ => SpaceId::Initial,
+428        }
+429    }
+430
+431    pub(crate) fn key_phase(&self) -> bool {
+432        match *self {
+433            Self::Short { key_phase, .. } => key_phase,
+434            _ => false,
+435        }
+436    }
+437
+438    pub(crate) fn is_short(&self) -> bool {
+439        matches!(*self, Self::Short { .. })
+440    }
+441
+442    pub(crate) fn is_1rtt(&self) -> bool {
+443        self.is_short()
+444    }
+445
+446    pub(crate) fn is_0rtt(&self) -> bool {
+447        matches!(
+448            *self,
+449            Self::Long {
+450                ty: LongType::ZeroRtt,
+451                ..
+452            }
+453        )
+454    }
+455
+456    pub(crate) fn dst_cid(&self) -> ConnectionId {
+457        use Header::*;
+458        match *self {
+459            Initial(InitialHeader { dst_cid, .. }) => dst_cid,
+460            Long { dst_cid, .. } => dst_cid,
+461            Retry { dst_cid, .. } => dst_cid,
+462            Short { dst_cid, .. } => dst_cid,
+463            VersionNegotiate { dst_cid, .. } => dst_cid,
+464        }
+465    }
+466
+467    /// Whether the payload of this packet contains QUIC frames
+468    pub(crate) fn has_frames(&self) -> bool {
+469        use Header::*;
+470        match *self {
+471            Initial(_) => true,
+472            Long { .. } => true,
+473            Retry { .. } => false,
+474            Short { .. } => true,
+475            VersionNegotiate { .. } => false,
+476        }
+477    }
+478
+479    #[cfg(feature = "qlog")]
+480    pub(crate) fn src_cid(&self) -> Option<ConnectionId> {
+481        match self {
+482            Self::Initial(initial_header) => Some(initial_header.src_cid),
+483            Self::Long { src_cid, .. } => Some(*src_cid),
+484            Self::Retry { src_cid, .. } => Some(*src_cid),
+485            Self::Short { .. } => None,
+486            Self::VersionNegotiate { src_cid, .. } => Some(*src_cid),
+487        }
+488    }
+489}
+490
+491pub(crate) struct PartialEncode {
+492    pub(crate) start: usize,
+493    pub(crate) header_len: usize,
+494    // Packet number length, payload length needed
+495    pn: Option<(usize, bool)>,
+496}
+497
+498impl PartialEncode {
+499    pub(crate) fn finish(
+500        self,
+501        buf: &mut [u8],
+502        header_crypto: &dyn crypto::HeaderKey,
+503        crypto: Option<(u64, PathId, &dyn crypto::PacketKey)>,
+504    ) {
+505        let Self { header_len, pn, .. } = self;
+506        let (pn_len, write_len) = match pn {
+507            Some((pn_len, write_len)) => (pn_len, write_len),
+508            None => return,
+509        };
+510
+511        let pn_pos = header_len - pn_len;
+512        if write_len {
+513            let len = buf.len() - header_len + pn_len;
+514            assert!(len < 2usize.pow(14)); // Fits in reserved space
+515            let mut slice = &mut buf[pn_pos - 2..pn_pos];
+516            slice.put_u16(len as u16 | (0b01 << 14));
+517        }
+518
+519        if let Some((packet_number, path_id, crypto)) = crypto {
+520            crypto.encrypt(path_id, packet_number, buf, header_len);
+521        }
+522
+523        debug_assert!(
+524            pn_pos + 4 + header_crypto.sample_size() <= buf.len(),
+525            "packet must be padded to at least {} bytes for header protection sampling",
+526            pn_pos + 4 + header_crypto.sample_size()
+527        );
+528        header_crypto.encrypt(pn_pos, buf);
+529    }
+530}
+531
+532/// Plain packet header
+533#[derive(Clone, Debug)]
+534pub enum ProtectedHeader {
+535    /// An Initial packet header
+536    Initial(ProtectedInitialHeader),
+537    /// A Long packet header, as used during the handshake
+538    Long {
+539        /// Type of the Long header packet
+540        ty: LongType,
+541        /// Destination Connection ID
+542        dst_cid: ConnectionId,
+543        /// Source Connection ID
+544        src_cid: ConnectionId,
+545        /// Length of the packet payload
+546        len: u64,
+547        /// QUIC version
+548        version: u32,
+549    },
+550    /// A Retry packet header
+551    Retry {
+552        /// Destination Connection ID
+553        dst_cid: ConnectionId,
+554        /// Source Connection ID
+555        src_cid: ConnectionId,
+556        /// QUIC version
+557        version: u32,
+558    },
+559    /// A short packet header, as used during the data phase
+560    Short {
+561        /// Spin bit
+562        spin: bool,
+563        /// Destination Connection ID
+564        dst_cid: ConnectionId,
+565    },
+566    /// A Version Negotiation packet header
+567    VersionNegotiate {
+568        /// Random value
+569        random: u8,
+570        /// Destination Connection ID
+571        dst_cid: ConnectionId,
+572        /// Source Connection ID
+573        src_cid: ConnectionId,
+574    },
+575}
+576
+577impl ProtectedHeader {
+578    fn as_initial(&self) -> Option<&ProtectedInitialHeader> {
+579        match self {
+580            Self::Initial(x) => Some(x),
+581            _ => None,
+582        }
+583    }
+584
+585    /// The destination Connection ID of the packet
+586    pub fn dst_cid(&self) -> ConnectionId {
+587        use ProtectedHeader::*;
+588        match self {
+589            Initial(header) => header.dst_cid,
+590            &Long { dst_cid, .. } => dst_cid,
+591            &Retry { dst_cid, .. } => dst_cid,
+592            &Short { dst_cid, .. } => dst_cid,
+593            &VersionNegotiate { dst_cid, .. } => dst_cid,
+594        }
+595    }
+596
+597    fn payload_len(&self) -> Option<u64> {
+598        use ProtectedHeader::*;
+599        match self {
+600            Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len),
+601            _ => None,
+602        }
+603    }
+604
+605    /// Decode a plain header from given buffer, with given [`ConnectionIdParser`].
+606    pub fn decode(
+607        buf: &mut io::Cursor<BytesMut>,
+608        cid_parser: &(impl ConnectionIdParser + ?Sized),
+609        supported_versions: &[u32],
+610        grease_quic_bit: bool,
+611    ) -> Result<Self, PacketDecodeError> {
+612        let first = buf.get::<u8>()?;
+613        if !grease_quic_bit && first & FIXED_BIT == 0 {
+614            return Err(PacketDecodeError::InvalidHeader("fixed bit unset"));
+615        }
+616        if first & LONG_HEADER_FORM == 0 {
+617            let spin = first & SPIN_BIT != 0;
+618
+619            Ok(Self::Short {
+620                spin,
+621                dst_cid: cid_parser.parse(buf)?,
+622            })
+623        } else {
+624            let version = buf.get::<u32>()?;
+625
+626            let dst_cid = ConnectionId::decode_long(buf)
+627                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+628            let src_cid = ConnectionId::decode_long(buf)
+629                .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?;
+630
+631            // TODO: Support long CIDs for compatibility with future QUIC versions
+632            if version == 0 {
+633                let random = first & !LONG_HEADER_FORM;
+634                return Ok(Self::VersionNegotiate {
+635                    random,
+636                    dst_cid,
+637                    src_cid,
+638                });
+639            }
+640
+641            if !supported_versions.contains(&version) {
+642                return Err(PacketDecodeError::UnsupportedVersion {
+643                    src_cid,
+644                    dst_cid,
+645                    version,
+646                });
+647            }
+648
+649            match LongHeaderType::from_byte(first)? {
+650                LongHeaderType::Initial => {
+651                    let token_len = buf.get_var()? as usize;
+652                    let token_start = buf.position() as usize;
+653                    if token_len > buf.remaining() {
+654                        return Err(PacketDecodeError::InvalidHeader("token out of bounds"));
+655                    }
+656                    buf.advance(token_len);
+657
+658                    let len = buf.get_var()?;
+659                    Ok(Self::Initial(ProtectedInitialHeader {
+660                        dst_cid,
+661                        src_cid,
+662                        token_pos: token_start..token_start + token_len,
+663                        len,
+664                        version,
+665                    }))
+666                }
+667                LongHeaderType::Retry => Ok(Self::Retry {
+668                    dst_cid,
+669                    src_cid,
+670                    version,
+671                }),
+672                LongHeaderType::Standard(ty) => Ok(Self::Long {
+673                    ty,
+674                    dst_cid,
+675                    src_cid,
+676                    len: buf.get_var()?,
+677                    version,
+678                }),
+679            }
+680        }
+681    }
+682}
+683
+684/// Header of an Initial packet, before decryption
+685#[derive(Clone, Debug)]
+686pub struct ProtectedInitialHeader {
+687    /// Destination Connection ID
+688    pub dst_cid: ConnectionId,
+689    /// Source Connection ID
+690    pub src_cid: ConnectionId,
+691    /// The position of a token in the packet buffer
+692    pub token_pos: Range<usize>,
+693    /// Length of the packet payload
+694    pub len: u64,
+695    /// QUIC version
+696    pub version: u32,
+697}
+698
+699#[derive(Clone, Debug)]
+700pub(crate) struct InitialHeader {
+701    pub(crate) dst_cid: ConnectionId,
+702    pub(crate) src_cid: ConnectionId,
+703    pub(crate) token: Bytes,
+704    pub(crate) number: PacketNumber,
+705    pub(crate) version: u32,
+706}
+707
+708// An encoded packet number
+709#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+710pub(crate) enum PacketNumber {
+711    U8(u8),
+712    U16(u16),
+713    U24(u32),
+714    U32(u32),
+715}
+716
+717impl PacketNumber {
+718    pub(crate) fn new(n: u64, largest_acked: u64) -> Self {
+719        let range = (n - largest_acked) * 2;
+720        if range < 1 << 8 {
+721            Self::U8(n as u8)
+722        } else if range < 1 << 16 {
+723            Self::U16(n as u16)
+724        } else if range < 1 << 24 {
+725            Self::U24(n as u32)
+726        } else if range < 1 << 32 {
+727            Self::U32(n as u32)
+728        } else {
+729            panic!("packet number too large to encode")
+730        }
+731    }
+732
+733    pub(crate) fn len(self) -> usize {
+734        use PacketNumber::*;
+735        match self {
+736            U8(_) => 1,
+737            U16(_) => 2,
+738            U24(_) => 3,
+739            U32(_) => 4,
+740        }
+741    }
+742
+743    pub(crate) fn encode<W: BufMut>(self, w: &mut W) {
+744        use PacketNumber::*;
+745        match self {
+746            U8(x) => w.write(x),
+747            U16(x) => w.write(x),
+748            U24(x) => w.put_uint(u64::from(x), 3),
+749            U32(x) => w.write(x),
+750        }
+751    }
+752
+753    pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> {
+754        use PacketNumber::*;
+755        let pn = match len {
+756            1 => U8(r.get()?),
+757            2 => U16(r.get()?),
+758            3 => U24(r.get_uint(3) as u32),
+759            4 => U32(r.get()?),
+760            _ => unreachable!(),
+761        };
+762        Ok(pn)
+763    }
+764
+765    pub(crate) fn decode_len(tag: u8) -> usize {
+766        1 + (tag & 0x03) as usize
+767    }
+768
+769    fn tag(self) -> u8 {
+770        use PacketNumber::*;
+771        match self {
+772            U8(_) => 0b00,
+773            U16(_) => 0b01,
+774            U24(_) => 0b10,
+775            U32(_) => 0b11,
+776        }
+777    }
+778
+779    pub(crate) fn expand(self, expected: u64) -> u64 {
+780        // From Appendix A
+781        use PacketNumber::*;
+782        let truncated = match self {
+783            U8(x) => u64::from(x),
+784            U16(x) => u64::from(x),
+785            U24(x) => u64::from(x),
+786            U32(x) => u64::from(x),
+787        };
+788        let nbits = self.len() * 8;
+789        let win = 1 << nbits;
+790        let hwin = win / 2;
+791        let mask = win - 1;
+792        // The incoming packet number should be greater than expected - hwin and less than or equal
+793        // to expected + hwin
+794        //
+795        // This means we can't just strip the trailing bits from expected and add the truncated
+796        // because that might yield a value outside the window.
+797        //
+798        // The following code calculates a candidate value and makes sure it's within the packet
+799        // number window.
+800        let candidate = (expected & !mask) | truncated;
+801        if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) {
+802            candidate + win
+803        } else if candidate > expected + hwin && candidate > win {
+804            candidate - win
+805        } else {
+806            candidate
+807        }
+808    }
+809}
+810
+811/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length
+812pub struct FixedLengthConnectionIdParser {
+813    expected_len: usize,
+814}
+815
+816impl FixedLengthConnectionIdParser {
+817    /// Create a new instance of `FixedLengthConnectionIdParser`
+818    pub fn new(expected_len: usize) -> Self {
+819        Self { expected_len }
+820    }
+821}
+822
+823impl ConnectionIdParser for FixedLengthConnectionIdParser {
+824    fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> {
+825        (buffer.remaining() >= self.expected_len)
+826            .then(|| ConnectionId::from_buf(buffer, self.expected_len))
+827            .ok_or(PacketDecodeError::InvalidHeader("packet too small"))
+828    }
+829}
+830
+831/// Parse connection id in short header packet
+832pub trait ConnectionIdParser {
+833    /// Parse a connection id from given buffer
+834    fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>;
+835}
+836
+837/// Long packet type including non-uniform cases
+838#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+839pub(crate) enum LongHeaderType {
+840    Initial,
+841    Retry,
+842    Standard(LongType),
+843}
+844
+845impl LongHeaderType {
+846    fn from_byte(b: u8) -> Result<Self, PacketDecodeError> {
+847        use {LongHeaderType::*, LongType::*};
+848        debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet");
+849        Ok(match (b & 0x30) >> 4 {
+850            0x0 => Initial,
+851            0x1 => Standard(ZeroRtt),
+852            0x2 => Standard(Handshake),
+853            0x3 => Retry,
+854            _ => unreachable!(),
+855        })
+856    }
+857}
+858
+859impl From<LongHeaderType> for u8 {
+860    fn from(ty: LongHeaderType) -> Self {
+861        use {LongHeaderType::*, LongType::*};
+862        match ty {
+863            Initial => LONG_HEADER_FORM | FIXED_BIT,
+864            Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4),
+865            Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4),
+866            Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4),
+867        }
+868    }
+869}
+870
+871/// Long packet types with uniform header structure
+872#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+873pub enum LongType {
+874    /// Handshake packet
+875    Handshake,
+876    /// 0-RTT packet
+877    ZeroRtt,
+878}
+879
+880/// Packet decode error
+881#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+882pub enum PacketDecodeError {
+883    /// Packet uses a QUIC version that is not supported
+884    #[error("unsupported version {version:x}")]
+885    UnsupportedVersion {
+886        /// Source Connection ID
+887        src_cid: ConnectionId,
+888        /// Destination Connection ID
+889        dst_cid: ConnectionId,
+890        /// The version that was unsupported
+891        version: u32,
+892    },
+893    /// The packet header is invalid
+894    #[error("invalid header: {0}")]
+895    InvalidHeader(&'static str),
+896}
+897
+898impl From<coding::UnexpectedEnd> for PacketDecodeError {
+899    fn from(_: coding::UnexpectedEnd) -> Self {
+900        Self::InvalidHeader("unexpected end of packet")
+901    }
+902}
+903
+904pub(crate) const LONG_HEADER_FORM: u8 = 0x80;
+905pub(crate) const FIXED_BIT: u8 = 0x40;
+906pub(crate) const SPIN_BIT: u8 = 0x20;
+907const SHORT_RESERVED_BITS: u8 = 0x18;
+908const LONG_RESERVED_BITS: u8 = 0x0c;
+909const KEY_PHASE_BIT: u8 = 0x04;
+910
+911/// Packet number space identifiers
+912#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+913pub enum SpaceId {
+914    /// Unprotected packets, used to bootstrap the handshake
+915    Initial = 0,
+916    Handshake = 1,
+917    /// Application data space, used for 0-RTT and post-handshake/1-RTT packets
+918    Data = 2,
+919}
+920
+921impl SpaceId {
+922    pub fn iter() -> impl Iterator<Item = Self> {
+923        [Self::Initial, Self::Handshake, Self::Data].iter().cloned()
+924    }
+925
+926    /// Returns the next higher packet space.
+927    ///
+928    /// Keeps returning [`SpaceId::Data`] as the highest space.
+929    pub fn next(&self) -> Self {
+930        match self {
+931            Self::Initial => Self::Handshake,
+932            Self::Handshake => Self::Data,
+933            Self::Data => Self::Data,
+934        }
+935    }
+936}
+937
+938#[cfg(test)]
+939mod tests {
+940    use super::*;
+941    use hex_literal::hex;
+942    use std::io;
+943
+944    fn check_pn(typed: PacketNumber, encoded: &[u8]) {
+945        let mut buf = Vec::new();
+946        typed.encode(&mut buf);
+947        assert_eq!(&buf[..], encoded);
+948        let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap();
+949        assert_eq!(typed, decoded);
+950    }
+951
+952    #[test]
+953    fn roundtrip_packet_numbers() {
+954        check_pn(PacketNumber::U8(0x7f), &hex!("7f"));
+955        check_pn(PacketNumber::U16(0x80), &hex!("0080"));
+956        check_pn(PacketNumber::U16(0x3fff), &hex!("3fff"));
+957        check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000"));
+958        check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff"));
+959    }
+960
+961    #[test]
+962    fn pn_encode() {
+963        check_pn(PacketNumber::new(0x10, 0), &hex!("10"));
+964        check_pn(PacketNumber::new(0x100, 0), &hex!("0100"));
+965        check_pn(PacketNumber::new(0x10000, 0), &hex!("010000"));
+966    }
+967
+968    #[test]
+969    fn pn_expand_roundtrip() {
+970        for expected in 0..1024 {
+971            for actual in expected..1024 {
+972                assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected));
+973            }
+974        }
+975    }
+976
+977    #[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))]
+978    #[test]
+979    fn header_encoding() {
+980        use crate::Side;
+981        use crate::crypto::rustls::{initial_keys, initial_suite_from_provider};
+982        #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))]
+983        use rustls::crypto::aws_lc_rs::default_provider;
+984        #[cfg(feature = "rustls-ring")]
+985        use rustls::crypto::ring::default_provider;
+986        use rustls::quic::Version;
+987
+988        let dcid = ConnectionId::new(&hex!("06b858ec6f80452b"));
+989        let provider = default_provider();
+990
+991        let suite = initial_suite_from_provider(&std::sync::Arc::new(provider)).unwrap();
+992        let client = initial_keys(Version::V1, dcid, Side::Client, &suite);
+993        let mut buf = Vec::new();
+994        let header = Header::Initial(InitialHeader {
+995            number: PacketNumber::U8(0),
+996            src_cid: ConnectionId::new(&[]),
+997            dst_cid: dcid,
+998            token: Bytes::new(),
+999            version: crate::DEFAULT_SUPPORTED_VERSIONS[0],
+1000        });
+1001        let encode = header.encode(&mut buf);
+1002        let header_len = buf.len();
+1003        buf.resize(header_len + 16 + client.packet.local.tag_len(), 0);
+1004        encode.finish(
+1005            &mut buf,
+1006            &*client.header.local,
+1007            Some((0, PathId::ZERO, &*client.packet.local)),
+1008        );
+1009
+1010        for byte in &buf {
+1011            print!("{byte:02x}");
+1012        }
+1013        println!();
+1014        assert_eq!(
+1015            buf[..],
+1016            hex!(
+1017                "c8000000010806b858ec6f80452b00004021be
+1018                 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1"
+1019            )[..]
+1020        );
+1021
+1022        let server = initial_keys(Version::V1, dcid, Side::Server, &suite);
+1023        let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec();
+1024        let decode = PartialDecode::new(
+1025            buf.as_slice().into(),
+1026            &FixedLengthConnectionIdParser::new(0),
+1027            &supported_versions,
+1028            false,
+1029        )
+1030        .unwrap()
+1031        .0;
+1032        let mut packet = decode.finish(Some(&*server.header.remote)).unwrap();
+1033        assert_eq!(
+1034            packet.header_data[..],
+1035            hex!("c0000000010806b858ec6f80452b0000402100")[..]
+1036        );
+1037        server
+1038            .packet
+1039            .remote
+1040            .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload)
+1041            .unwrap();
+1042        assert_eq!(packet.payload[..], [0; 16]);
+1043        match packet.header {
+1044            Header::Initial(InitialHeader {
+1045                number: PacketNumber::U8(0),
+1046                ..
+1047            }) => {}
+1048            _ => {
+1049                panic!("unexpected header {:?}", packet.header);
+1050            }
+1051        }
+1052    }
+1053}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html b/pr/234/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html new file mode 100644 index 000000000..59732843c --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/range_set/array_range_set.rs.html @@ -0,0 +1,210 @@ +array_range_set.rs - source

iroh_quinn_proto/range_set/
array_range_set.rs

1use std::ops::Range;
+2
+3use tinyvec::TinyVec;
+4
+5/// A set of u64 values optimized for long runs and random insert/delete/contains
+6///
+7/// `ArrayRangeSet` uses an array representation, where each array entry represents
+8/// a range.
+9///
+10/// The array-based RangeSet provides 2 benefits:
+11/// - There exists an inline representation, which avoids the need of heap
+12///   allocating ACK ranges for SentFrames for small ranges.
+13/// - Iterating over ranges should usually be faster since there is only
+14///   a single cache-friendly contiguous range.
+15///
+16/// `ArrayRangeSet` is especially useful for tracking ACK ranges where the amount
+17/// of ranges is usually very low (since ACK numbers are in consecutive fashion
+18/// unless reordering or packet loss occur).
+19#[derive(Debug, Default)]
+20pub struct ArrayRangeSet(TinyVec<[Range<u64>; ARRAY_RANGE_SET_INLINE_CAPACITY]>);
+21
+22/// The capacity of elements directly stored in [`ArrayRangeSet`]
+23///
+24/// An inline capacity of 2 is chosen to keep `SentFrame` below 128 bytes.
+25const ARRAY_RANGE_SET_INLINE_CAPACITY: usize = 2;
+26
+27impl Clone for ArrayRangeSet {
+28    fn clone(&self) -> Self {
+29        // tinyvec keeps the heap representation after clones.
+30        // We rather prefer the inline representation for clones if possible,
+31        // since clones (e.g. for storage in `SentFrames`) are rarely mutated
+32        if self.0.is_inline() || self.0.len() > ARRAY_RANGE_SET_INLINE_CAPACITY {
+33            return Self(self.0.clone());
+34        }
+35
+36        let mut vec = TinyVec::new();
+37        vec.extend_from_slice(self.0.as_slice());
+38        Self(vec)
+39    }
+40}
+41
+42impl ArrayRangeSet {
+43    pub fn new() -> Self {
+44        Default::default()
+45    }
+46
+47    pub fn iter(&self) -> impl DoubleEndedIterator<Item = Range<u64>> + '_ {
+48        self.0.iter().cloned()
+49    }
+50
+51    pub fn elts(&self) -> impl Iterator<Item = u64> + '_ {
+52        self.iter().flatten()
+53    }
+54
+55    pub fn len(&self) -> usize {
+56        self.0.len()
+57    }
+58
+59    pub fn contains(&self, x: u64) -> bool {
+60        for range in self.0.iter() {
+61            if range.start > x {
+62                // We only get here if there was no prior range that contained x
+63                return false;
+64            } else if range.contains(&x) {
+65                return true;
+66            }
+67        }
+68        false
+69    }
+70
+71    pub fn iter_range(&self, range: Range<u64>) -> impl Iterator<Item = Range<u64>> + '_ {
+72        self.iter().filter_map(move |r| {
+73            if r.end > range.start && r.start < range.end {
+74                Some(r.start.max(range.start)..r.end.min(range.end))
+75            } else {
+76                None
+77            }
+78        })
+79    }
+80
+81    pub fn insert_one(&mut self, x: u64) -> bool {
+82        self.insert(x..x + 1)
+83    }
+84
+85    pub fn insert(&mut self, x: Range<u64>) -> bool {
+86        let mut result = false;
+87
+88        if x.is_empty() {
+89            // Don't try to deal with ranges where x.end <= x.start
+90            return false;
+91        }
+92
+93        let mut idx = 0;
+94        while idx != self.0.len() {
+95            let range = &mut self.0[idx];
+96
+97            if range.start > x.end {
+98                // The range is fully before this range and therefore not extensible.
+99                // Add a new range to the left
+100                self.0.insert(idx, x);
+101                return true;
+102            } else if range.start > x.start {
+103                // The new range starts before this range but overlaps.
+104                // Extend the current range to the left
+105                // Note that we don't have to merge a potential left range, since
+106                // this case would have been captured by merging the right range
+107                // in the previous loop iteration
+108                result = true;
+109                range.start = x.start;
+110            }
+111
+112            // At this point we have handled all parts of the new range which
+113            // are in front of the current range. Now we handle everything from
+114            // the start of the current range
+115
+116            if x.end <= range.end {
+117                // Fully contained
+118                return result;
+119            } else if x.start <= range.end {
+120                // Extend the current range to the end of the new range.
+121                // Since it's not contained it must be bigger
+122                range.end = x.end;
+123
+124                // Merge all follow-up ranges which overlap
+125                while idx != self.0.len() - 1 {
+126                    let curr = self.0[idx].clone();
+127                    let next = self.0[idx + 1].clone();
+128                    if curr.end >= next.start {
+129                        self.0[idx].end = next.end.max(curr.end);
+130                        self.0.remove(idx + 1);
+131                    } else {
+132                        break;
+133                    }
+134                }
+135
+136                return true;
+137            }
+138
+139            idx += 1;
+140        }
+141
+142        // Insert a range at the end
+143        self.0.push(x);
+144        true
+145    }
+146
+147    pub fn remove(&mut self, x: Range<u64>) -> bool {
+148        let mut result = false;
+149
+150        if x.is_empty() {
+151            // Don't try to deal with ranges where x.end <= x.start
+152            return false;
+153        }
+154
+155        let mut idx = 0;
+156        while idx != self.0.len() && x.start != x.end {
+157            let range = self.0[idx].clone();
+158
+159            if x.end <= range.start {
+160                // The range is fully before this range
+161                return result;
+162            } else if x.start >= range.end {
+163                // The range is fully after this range
+164                idx += 1;
+165                continue;
+166            }
+167
+168            // The range overlaps with this range
+169            result = true;
+170
+171            let left = range.start..x.start;
+172            let right = x.end..range.end;
+173            if left.is_empty() && right.is_empty() {
+174                self.0.remove(idx);
+175            } else if left.is_empty() {
+176                self.0[idx] = right;
+177                idx += 1;
+178            } else if right.is_empty() {
+179                self.0[idx] = left;
+180                idx += 1;
+181            } else {
+182                self.0[idx] = right;
+183                self.0.insert(idx, left);
+184                idx += 2;
+185            }
+186        }
+187
+188        result
+189    }
+190
+191    pub fn is_empty(&self) -> bool {
+192        self.0.is_empty()
+193    }
+194
+195    pub fn pop_min(&mut self) -> Option<Range<u64>> {
+196        if !self.0.is_empty() {
+197            Some(self.0.remove(0))
+198        } else {
+199            None
+200        }
+201    }
+202
+203    pub fn min(&self) -> Option<u64> {
+204        self.iter().next().map(|x| x.start)
+205    }
+206
+207    pub fn max(&self) -> Option<u64> {
+208        self.iter().next_back().map(|x| x.end - 1)
+209    }
+210}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/range_set/mod.rs.html b/pr/234/docs/src/iroh_quinn_proto/range_set/mod.rs.html new file mode 100644 index 000000000..d8326bb7f --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/range_set/mod.rs.html @@ -0,0 +1,5 @@ +mod.rs - source

iroh_quinn_proto/range_set/
mod.rs

1mod array_range_set;
+2#[cfg(test)]
+3mod tests;
+4
+5pub(crate) use array_range_set::ArrayRangeSet;
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/shared.rs.html b/pr/234/docs/src/iroh_quinn_proto/shared.rs.html new file mode 100644 index 000000000..02e444e51 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/shared.rs.html @@ -0,0 +1,198 @@ +shared.rs - source

iroh_quinn_proto/
shared.rs

1use std::{fmt, net::SocketAddr};
+2
+3use bytes::{Buf, BufMut, BytesMut};
+4
+5use crate::PathId;
+6use crate::{Duration, Instant, MAX_CID_SIZE, ResetToken, coding::BufExt, packet::PartialDecode};
+7
+8/// Events sent from an Endpoint to a Connection
+9#[derive(Debug)]
+10pub struct ConnectionEvent(pub(crate) ConnectionEventInner);
+11
+12#[derive(Debug)]
+13pub(crate) enum ConnectionEventInner {
+14    /// A datagram has been received for the Connection
+15    Datagram(DatagramConnectionEvent),
+16    /// New connection identifiers have been issued for the Connection
+17    NewIdentifiers(Vec<IssuedCid>, Instant, usize, Option<Duration>),
+18}
+19
+20/// Variant of [`ConnectionEventInner`].
+21#[derive(Debug)]
+22pub(crate) struct DatagramConnectionEvent {
+23    pub(crate) now: Instant,
+24    pub(crate) remote: SocketAddr,
+25    pub(crate) path_id: PathId,
+26    pub(crate) ecn: Option<EcnCodepoint>,
+27    pub(crate) first_decode: PartialDecode,
+28    pub(crate) remaining: Option<BytesMut>,
+29}
+30
+31/// Events sent from a Connection to an Endpoint
+32#[derive(Debug)]
+33pub struct EndpointEvent(pub(crate) EndpointEventInner);
+34
+35impl EndpointEvent {
+36    /// Construct an event that indicating that a `Connection` will no longer emit events
+37    ///
+38    /// Useful for notifying an `Endpoint` that a `Connection` has been destroyed outside of the
+39    /// usual state machine flow, e.g. when being dropped by the user.
+40    pub fn drained() -> Self {
+41        Self(EndpointEventInner::Drained)
+42    }
+43
+44    /// Determine whether this is the last event a `Connection` will emit
+45    ///
+46    /// Useful for determining when connection-related event loop state can be freed.
+47    pub fn is_drained(&self) -> bool {
+48        self.0 == EndpointEventInner::Drained
+49    }
+50}
+51
+52#[derive(Clone, Debug, Eq, PartialEq)]
+53pub(crate) enum EndpointEventInner {
+54    /// The connection has been drained
+55    Drained,
+56    /// The connection has a new active reset token
+57    ///
+58    /// Whenever the connection switches to a new remote CID issued by the peer, it also
+59    /// switches the matching reset token that can be used to abort this connection. This
+60    /// event provides a new reset token for the active remote CID.
+61    ResetToken(PathId, SocketAddr, ResetToken),
+62    /// Retire the remotely issued reset token for a path, without replacing it with a new one
+63    ///
+64    /// This is like `ResetToken` above, but without replacing the `ResetToken` with a new
+65    /// one. See `ConnectionIndex::connection_reset_tokens`.
+66    RetireResetToken(PathId),
+67    /// The connection needs connection identifiers
+68    NeedIdentifiers(PathId, Instant, u64),
+69    /// Retire a locally issued CID
+70    ///
+71    /// Stop routing connection ID for this sequence number to the connection
+72    /// When `bool == true`, a new connection ID will be issued to peer
+73    RetireConnectionId(Instant, PathId, u64, bool),
+74}
+75
+76/// Protocol-level identifier for a connection.
+77///
+78/// Mainly useful for identifying this connection's packets on the wire with tools like Wireshark.
+79#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
+80pub struct ConnectionId {
+81    /// length of CID
+82    len: u8,
+83    /// CID in byte array
+84    bytes: [u8; MAX_CID_SIZE],
+85}
+86
+87impl ConnectionId {
+88    /// Construct cid from byte array
+89    pub fn new(bytes: &[u8]) -> Self {
+90        debug_assert!(bytes.len() <= MAX_CID_SIZE);
+91        let mut res = Self {
+92            len: bytes.len() as u8,
+93            bytes: [0; MAX_CID_SIZE],
+94        };
+95        res.bytes[..bytes.len()].copy_from_slice(bytes);
+96        res
+97    }
+98
+99    /// Constructs cid by reading `len` bytes from a `Buf`
+100    ///
+101    /// Callers need to assure that `buf.remaining() >= len`
+102    pub fn from_buf(buf: &mut (impl Buf + ?Sized), len: usize) -> Self {
+103        debug_assert!(len <= MAX_CID_SIZE);
+104        let mut res = Self {
+105            len: len as u8,
+106            bytes: [0; MAX_CID_SIZE],
+107        };
+108        buf.copy_to_slice(&mut res[..len]);
+109        res
+110    }
+111
+112    pub(crate) fn len(&self) -> usize {
+113        self.len as usize
+114    }
+115
+116    /// Decode from long header format
+117    pub(crate) fn decode_long(buf: &mut impl Buf) -> Option<Self> {
+118        let len = buf.get::<u8>().ok()? as usize;
+119        match len > MAX_CID_SIZE || buf.remaining() < len {
+120            false => Some(Self::from_buf(buf, len)),
+121            true => None,
+122        }
+123    }
+124
+125    /// Encode in long header format
+126    pub(crate) fn encode_long(&self, buf: &mut impl BufMut) {
+127        buf.put_u8(self.len() as u8);
+128        buf.put_slice(self);
+129    }
+130}
+131
+132impl ::std::ops::Deref for ConnectionId {
+133    type Target = [u8];
+134    fn deref(&self) -> &[u8] {
+135        &self.bytes[0..self.len as usize]
+136    }
+137}
+138
+139impl ::std::ops::DerefMut for ConnectionId {
+140    fn deref_mut(&mut self) -> &mut [u8] {
+141        &mut self.bytes[0..self.len as usize]
+142    }
+143}
+144
+145impl fmt::Debug for ConnectionId {
+146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+147        self.bytes[0..self.len as usize].fmt(f)
+148    }
+149}
+150
+151impl fmt::Display for ConnectionId {
+152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+153        for byte in self.iter() {
+154            write!(f, "{byte:02x}")?;
+155        }
+156        Ok(())
+157    }
+158}
+159
+160/// Explicit congestion notification codepoint
+161#[repr(u8)]
+162#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+163pub enum EcnCodepoint {
+164    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+165    Ect0 = 0b10,
+166    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+167    Ect1 = 0b01,
+168    /// The CE codepoint, signalling that congestion was experienced
+169    Ce = 0b11,
+170}
+171
+172impl EcnCodepoint {
+173    /// Create new object from the given bits
+174    pub fn from_bits(x: u8) -> Option<Self> {
+175        use EcnCodepoint::*;
+176        Some(match x & 0b11 {
+177            0b10 => Ect0,
+178            0b01 => Ect1,
+179            0b11 => Ce,
+180            _ => {
+181                return None;
+182            }
+183        })
+184    }
+185
+186    /// Returns whether the codepoint is a CE, signalling that congestion was experienced
+187    pub fn is_ce(self) -> bool {
+188        matches!(self, Self::Ce)
+189    }
+190}
+191
+192#[derive(Debug, Copy, Clone)]
+193pub(crate) struct IssuedCid {
+194    pub(crate) path_id: PathId,
+195    pub(crate) sequence: u64,
+196    pub(crate) id: ConnectionId,
+197    pub(crate) reset_token: ResetToken,
+198}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/token.rs.html b/pr/234/docs/src/iroh_quinn_proto/token.rs.html new file mode 100644 index 000000000..ca26f97ef --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/token.rs.html @@ -0,0 +1,506 @@ +token.rs - source

iroh_quinn_proto/
token.rs

1use std::{
+2    fmt,
+3    mem::size_of,
+4    net::{IpAddr, SocketAddr},
+5};
+6
+7use bytes::{Buf, BufMut, Bytes};
+8use rand::Rng;
+9
+10use crate::{
+11    Duration, RESET_TOKEN_SIZE, ServerConfig, SystemTime, UNIX_EPOCH,
+12    coding::{BufExt, BufMutExt},
+13    crypto::{HandshakeTokenKey, HmacKey},
+14    packet::InitialHeader,
+15    shared::ConnectionId,
+16};
+17
+18/// Responsible for limiting clients' ability to reuse validation tokens
+19///
+20/// [_RFC 9000 § 8.1.4:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.4)
+21///
+22/// > Attackers could replay tokens to use servers as amplifiers in DDoS attacks. To protect
+23/// > against such attacks, servers MUST ensure that replay of tokens is prevented or limited.
+24/// > Servers SHOULD ensure that tokens sent in Retry packets are only accepted for a short time,
+25/// > as they are returned immediately by clients. Tokens that are provided in NEW_TOKEN frames
+26/// > (Section 19.7) need to be valid for longer but SHOULD NOT be accepted multiple times.
+27/// > Servers are encouraged to allow tokens to be used only once, if possible; tokens MAY include
+28/// > additional information about clients to further narrow applicability or reuse.
+29///
+30/// `TokenLog` pertains only to tokens provided in NEW_TOKEN frames.
+31pub trait TokenLog: Send + Sync {
+32    /// Record that the token was used and, ideally, return a token reuse error if the token may
+33    /// have been already used previously
+34    ///
+35    /// False negatives and false positives are both permissible. Called when a client uses an
+36    /// address validation token.
+37    ///
+38    /// Parameters:
+39    /// - `nonce`: A server-generated random unique value for the token.
+40    /// - `issued`: The time the server issued the token.
+41    /// - `lifetime`: The expiration time of address validation tokens sent via NEW_TOKEN frames,
+42    ///   as configured by [`ServerValidationTokenConfig::lifetime`][1].
+43    ///
+44    /// [1]: crate::ValidationTokenConfig::lifetime
+45    ///
+46    /// ## Security & Performance
+47    ///
+48    /// To the extent that it is possible to repeatedly trigger false negatives (returning `Ok` for
+49    /// a token which has been reused), an attacker could use the server to perform [amplification
+50    /// attacks][2]. The QUIC specification requires that this be limited, if not prevented fully.
+51    ///
+52    /// A false positive (returning `Err` for a token which has never been used) is not a security
+53    /// vulnerability; it is permissible for a `TokenLog` to always return `Err`. A false positive
+54    /// causes the token to be ignored, which may cause the transmission of some 0.5-RTT data to be
+55    /// delayed until the handshake completes, if a sufficient amount of 0.5-RTT data it sent.
+56    ///
+57    /// [2]: https://en.wikipedia.org/wiki/Denial-of-service_attack#Amplification
+58    fn check_and_insert(
+59        &self,
+60        nonce: u128,
+61        issued: SystemTime,
+62        lifetime: Duration,
+63    ) -> Result<(), TokenReuseError>;
+64}
+65
+66/// Error for when a validation token may have been reused
+67pub struct TokenReuseError;
+68
+69/// Null implementation of [`TokenLog`], which never accepts tokens
+70pub struct NoneTokenLog;
+71
+72impl TokenLog for NoneTokenLog {
+73    fn check_and_insert(&self, _: u128, _: SystemTime, _: Duration) -> Result<(), TokenReuseError> {
+74        Err(TokenReuseError)
+75    }
+76}
+77
+78/// Responsible for storing validation tokens received from servers and retrieving them for use in
+79/// subsequent connections
+80pub trait TokenStore: Send + Sync {
+81    /// Potentially store a token for later one-time use
+82    ///
+83    /// Called when a NEW_TOKEN frame is received from the server.
+84    fn insert(&self, server_name: &str, token: Bytes);
+85
+86    /// Try to find and take a token that was stored with the given server name
+87    ///
+88    /// The same token must never be returned from `take` twice, as doing so can be used to
+89    /// de-anonymize a client's traffic.
+90    ///
+91    /// Called when trying to connect to a server. It is always ok for this to return `None`.
+92    fn take(&self, server_name: &str) -> Option<Bytes>;
+93}
+94
+95/// Null implementation of [`TokenStore`], which does not store any tokens
+96pub struct NoneTokenStore;
+97
+98impl TokenStore for NoneTokenStore {
+99    fn insert(&self, _: &str, _: Bytes) {}
+100    fn take(&self, _: &str) -> Option<Bytes> {
+101        None
+102    }
+103}
+104
+105/// State in an `Incoming` determined by a token or lack thereof
+106#[derive(Debug)]
+107pub(crate) struct IncomingToken {
+108    pub(crate) retry_src_cid: Option<ConnectionId>,
+109    pub(crate) orig_dst_cid: ConnectionId,
+110    pub(crate) validated: bool,
+111}
+112
+113impl IncomingToken {
+114    /// Construct for an `Incoming` given the first packet header, or error if the connection
+115    /// cannot be established
+116    pub(crate) fn from_header(
+117        header: &InitialHeader,
+118        server_config: &ServerConfig,
+119        remote_address: SocketAddr,
+120    ) -> Result<Self, InvalidRetryTokenError> {
+121        let unvalidated = Self {
+122            retry_src_cid: None,
+123            orig_dst_cid: header.dst_cid,
+124            validated: false,
+125        };
+126
+127        // Decode token or short-circuit
+128        if header.token.is_empty() {
+129            return Ok(unvalidated);
+130        }
+131
+132        // In cases where a token cannot be decrypted/decoded, we must allow for the possibility
+133        // that this is caused not by client malfeasance, but by the token having been generated by
+134        // an incompatible endpoint, e.g. a different version or a neighbor behind the same load
+135        // balancer. In such cases we proceed as if there was no token.
+136        //
+137        // [_RFC 9000 § 8.1.3:_](https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1.3-10)
+138        //
+139        // > If the token is invalid, then the server SHOULD proceed as if the client did not have
+140        // > a validated address, including potentially sending a Retry packet.
+141        let Some(retry) = Token::decode(&*server_config.token_key, &header.token) else {
+142            return Ok(unvalidated);
+143        };
+144
+145        // Validate token, then convert into Self
+146        match retry.payload {
+147            TokenPayload::Retry {
+148                address,
+149                orig_dst_cid,
+150                issued,
+151            } => {
+152                if address != remote_address {
+153                    return Err(InvalidRetryTokenError);
+154                }
+155                if issued + server_config.retry_token_lifetime < server_config.time_source.now() {
+156                    return Err(InvalidRetryTokenError);
+157                }
+158
+159                Ok(Self {
+160                    retry_src_cid: Some(header.dst_cid),
+161                    orig_dst_cid,
+162                    validated: true,
+163                })
+164            }
+165            TokenPayload::Validation { ip, issued } => {
+166                if ip != remote_address.ip() {
+167                    return Ok(unvalidated);
+168                }
+169                if issued + server_config.validation_token.lifetime
+170                    < server_config.time_source.now()
+171                {
+172                    return Ok(unvalidated);
+173                }
+174                if server_config
+175                    .validation_token
+176                    .log
+177                    .check_and_insert(retry.nonce, issued, server_config.validation_token.lifetime)
+178                    .is_err()
+179                {
+180                    return Ok(unvalidated);
+181                }
+182
+183                Ok(Self {
+184                    retry_src_cid: None,
+185                    orig_dst_cid: header.dst_cid,
+186                    validated: true,
+187                })
+188            }
+189        }
+190    }
+191}
+192
+193/// Error for a token being unambiguously from a Retry packet, and not valid
+194///
+195/// The connection cannot be established.
+196pub(crate) struct InvalidRetryTokenError;
+197
+198/// Retry or validation token
+199pub(crate) struct Token {
+200    /// Content that is encrypted from the client
+201    pub(crate) payload: TokenPayload,
+202    /// Randomly generated value, which must be unique, and is visible to the client
+203    nonce: u128,
+204}
+205
+206impl Token {
+207    /// Construct with newly sampled randomness
+208    pub(crate) fn new(payload: TokenPayload, rng: &mut impl Rng) -> Self {
+209        Self {
+210            nonce: rng.random(),
+211            payload,
+212        }
+213    }
+214
+215    /// Encode and encrypt
+216    pub(crate) fn encode(&self, key: &dyn HandshakeTokenKey) -> Vec<u8> {
+217        let mut buf = Vec::new();
+218
+219        // Encode payload
+220        match self.payload {
+221            TokenPayload::Retry {
+222                address,
+223                orig_dst_cid,
+224                issued,
+225            } => {
+226                buf.put_u8(TokenType::Retry as u8);
+227                encode_addr(&mut buf, address);
+228                orig_dst_cid.encode_long(&mut buf);
+229                encode_unix_secs(&mut buf, issued);
+230            }
+231            TokenPayload::Validation { ip, issued } => {
+232                buf.put_u8(TokenType::Validation as u8);
+233                encode_ip(&mut buf, ip);
+234                encode_unix_secs(&mut buf, issued);
+235            }
+236        }
+237
+238        // Encrypt
+239        let aead_key = key.aead_from_hkdf(&self.nonce.to_le_bytes());
+240        aead_key.seal(&mut buf, &[]).unwrap();
+241        buf.extend(&self.nonce.to_le_bytes());
+242
+243        buf
+244    }
+245
+246    /// Decode and decrypt
+247    fn decode(key: &dyn HandshakeTokenKey, raw_token_bytes: &[u8]) -> Option<Self> {
+248        // Decrypt
+249
+250        let nonce_slice_start = raw_token_bytes.len().checked_sub(size_of::<u128>())?;
+251        let (sealed_token, nonce_bytes) = raw_token_bytes.split_at_checked(nonce_slice_start)?;
+252
+253        let nonce = u128::from_le_bytes(nonce_bytes.try_into().unwrap());
+254
+255        let aead_key = key.aead_from_hkdf(nonce_bytes);
+256        let mut sealed_token = sealed_token.to_vec();
+257        let data = aead_key.open(&mut sealed_token, &[]).ok()?;
+258
+259        // Decode payload
+260        let mut reader = &data[..];
+261        let payload = match TokenType::from_byte((&mut reader).get::<u8>().ok()?)? {
+262            TokenType::Retry => TokenPayload::Retry {
+263                address: decode_addr(&mut reader)?,
+264                orig_dst_cid: ConnectionId::decode_long(&mut reader)?,
+265                issued: decode_unix_secs(&mut reader)?,
+266            },
+267            TokenType::Validation => TokenPayload::Validation {
+268                ip: decode_ip(&mut reader)?,
+269                issued: decode_unix_secs(&mut reader)?,
+270            },
+271        };
+272
+273        if !reader.is_empty() {
+274            // Consider extra bytes a decoding error (it may be from an incompatible endpoint)
+275            return None;
+276        }
+277
+278        Some(Self { nonce, payload })
+279    }
+280}
+281
+282/// Content of a [`Token`] that is encrypted from the client
+283pub(crate) enum TokenPayload {
+284    /// Token originating from a Retry packet
+285    Retry {
+286        /// The client's address
+287        address: SocketAddr,
+288        /// The destination connection ID set in the very first packet from the client
+289        orig_dst_cid: ConnectionId,
+290        /// The time at which this token was issued
+291        issued: SystemTime,
+292    },
+293    /// Token originating from a NEW_TOKEN frame
+294    Validation {
+295        /// The client's IP address (its port is likely to change between sessions)
+296        ip: IpAddr,
+297        /// The time at which this token was issued
+298        issued: SystemTime,
+299    },
+300}
+301
+302/// Variant tag for a [`TokenPayload`]
+303#[derive(Copy, Clone)]
+304#[repr(u8)]
+305enum TokenType {
+306    Retry = 0,
+307    Validation = 1,
+308}
+309
+310impl TokenType {
+311    fn from_byte(n: u8) -> Option<Self> {
+312        use TokenType::*;
+313        [Retry, Validation].into_iter().find(|ty| *ty as u8 == n)
+314    }
+315}
+316
+317fn encode_addr(buf: &mut Vec<u8>, address: SocketAddr) {
+318    encode_ip(buf, address.ip());
+319    buf.put_u16(address.port());
+320}
+321
+322fn decode_addr<B: Buf>(buf: &mut B) -> Option<SocketAddr> {
+323    let ip = decode_ip(buf)?;
+324    let port = buf.get().ok()?;
+325    Some(SocketAddr::new(ip, port))
+326}
+327
+328fn encode_ip(buf: &mut Vec<u8>, ip: IpAddr) {
+329    match ip {
+330        IpAddr::V4(x) => {
+331            buf.put_u8(0);
+332            buf.put_slice(&x.octets());
+333        }
+334        IpAddr::V6(x) => {
+335            buf.put_u8(1);
+336            buf.put_slice(&x.octets());
+337        }
+338    }
+339}
+340
+341fn decode_ip<B: Buf>(buf: &mut B) -> Option<IpAddr> {
+342    match buf.get::<u8>().ok()? {
+343        0 => buf.get().ok().map(IpAddr::V4),
+344        1 => buf.get().ok().map(IpAddr::V6),
+345        _ => None,
+346    }
+347}
+348
+349fn encode_unix_secs(buf: &mut Vec<u8>, time: SystemTime) {
+350    buf.write::<u64>(
+351        time.duration_since(UNIX_EPOCH)
+352            .unwrap_or_default()
+353            .as_secs(),
+354    );
+355}
+356
+357fn decode_unix_secs<B: Buf>(buf: &mut B) -> Option<SystemTime> {
+358    Some(UNIX_EPOCH + Duration::from_secs(buf.get::<u64>().ok()?))
+359}
+360
+361/// Stateless reset token
+362///
+363/// Used for an endpoint to securely communicate that it has lost state for a connection.
+364#[allow(clippy::derived_hash_with_manual_eq)] // Custom PartialEq impl matches derived semantics
+365#[derive(Debug, Copy, Clone, Hash)]
+366pub(crate) struct ResetToken([u8; RESET_TOKEN_SIZE]);
+367
+368impl ResetToken {
+369    pub(crate) fn new(key: &dyn HmacKey, id: ConnectionId) -> Self {
+370        let mut signature = vec![0; key.signature_len()];
+371        key.sign(&id, &mut signature);
+372        // TODO: Server ID??
+373        let mut result = [0; RESET_TOKEN_SIZE];
+374        result.copy_from_slice(&signature[..RESET_TOKEN_SIZE]);
+375        result.into()
+376    }
+377}
+378
+379impl PartialEq for ResetToken {
+380    fn eq(&self, other: &Self) -> bool {
+381        crate::constant_time::eq(&self.0, &other.0)
+382    }
+383}
+384
+385impl Eq for ResetToken {}
+386
+387impl From<[u8; RESET_TOKEN_SIZE]> for ResetToken {
+388    fn from(x: [u8; RESET_TOKEN_SIZE]) -> Self {
+389        Self(x)
+390    }
+391}
+392
+393impl std::ops::Deref for ResetToken {
+394    type Target = [u8];
+395    fn deref(&self) -> &[u8] {
+396        &self.0
+397    }
+398}
+399
+400impl fmt::Display for ResetToken {
+401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+402        for byte in self.iter() {
+403            write!(f, "{byte:02x}")?;
+404        }
+405        Ok(())
+406    }
+407}
+408
+409#[cfg(all(test, any(feature = "aws-lc-rs", feature = "ring")))]
+410mod test {
+411    use super::*;
+412    #[cfg(all(feature = "aws-lc-rs", not(feature = "ring")))]
+413    use aws_lc_rs::hkdf;
+414    use rand::prelude::*;
+415    #[cfg(feature = "ring")]
+416    use ring::hkdf;
+417
+418    fn token_round_trip(payload: TokenPayload) -> TokenPayload {
+419        let rng = &mut rand::rng();
+420        let token = Token::new(payload, rng);
+421        let mut master_key = [0; 64];
+422        rng.fill_bytes(&mut master_key);
+423        let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+424        let encoded = token.encode(&prk);
+425        let decoded = Token::decode(&prk, &encoded).expect("token didn't decrypt / decode");
+426        assert_eq!(token.nonce, decoded.nonce);
+427        decoded.payload
+428    }
+429
+430    #[test]
+431    fn retry_token_sanity() {
+432        use crate::MAX_CID_SIZE;
+433        use crate::cid_generator::{ConnectionIdGenerator, RandomConnectionIdGenerator};
+434        use crate::{Duration, UNIX_EPOCH};
+435
+436        use std::net::Ipv6Addr;
+437
+438        let address_1 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), 4433);
+439        let orig_dst_cid_1 = RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid();
+440        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+441        let payload_1 = TokenPayload::Retry {
+442            address: address_1,
+443            orig_dst_cid: orig_dst_cid_1,
+444            issued: issued_1,
+445        };
+446        let TokenPayload::Retry {
+447            address: address_2,
+448            orig_dst_cid: orig_dst_cid_2,
+449            issued: issued_2,
+450        } = token_round_trip(payload_1)
+451        else {
+452            panic!("token decoded as wrong variant");
+453        };
+454
+455        assert_eq!(address_1, address_2);
+456        assert_eq!(orig_dst_cid_1, orig_dst_cid_2);
+457        assert_eq!(issued_1, issued_2);
+458    }
+459
+460    #[test]
+461    fn validation_token_sanity() {
+462        use crate::{Duration, UNIX_EPOCH};
+463
+464        use std::net::Ipv6Addr;
+465
+466        let ip_1 = Ipv6Addr::LOCALHOST.into();
+467        let issued_1 = UNIX_EPOCH + Duration::from_secs(42); // Fractional seconds would be lost
+468
+469        let payload_1 = TokenPayload::Validation {
+470            ip: ip_1,
+471            issued: issued_1,
+472        };
+473        let TokenPayload::Validation {
+474            ip: ip_2,
+475            issued: issued_2,
+476        } = token_round_trip(payload_1)
+477        else {
+478            panic!("token decoded as wrong variant");
+479        };
+480
+481        assert_eq!(ip_1, ip_2);
+482        assert_eq!(issued_1, issued_2);
+483    }
+484
+485    #[test]
+486    fn invalid_token_returns_err() {
+487        use super::*;
+488        use rand::RngCore;
+489
+490        let rng = &mut rand::rng();
+491
+492        let mut master_key = [0; 64];
+493        rng.fill_bytes(&mut master_key);
+494
+495        let prk = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]).extract(&master_key);
+496
+497        let mut invalid_token = Vec::new();
+498
+499        let mut random_data = [0; 32];
+500        rand::rng().fill_bytes(&mut random_data);
+501        invalid_token.put_slice(&random_data);
+502
+503        // Assert: garbage sealed data returns err
+504        assert!(Token::decode(&prk, &invalid_token).is_none());
+505    }
+506}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/token_memory_cache.rs.html b/pr/234/docs/src/iroh_quinn_proto/token_memory_cache.rs.html new file mode 100644 index 000000000..45007f185 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/token_memory_cache.rs.html @@ -0,0 +1,247 @@ +token_memory_cache.rs - source

iroh_quinn_proto/
token_memory_cache.rs

1//! Storing tokens sent from servers in NEW_TOKEN frames and using them in subsequent connections
+2
+3use std::{
+4    collections::{HashMap, VecDeque, hash_map},
+5    sync::{Arc, Mutex},
+6};
+7
+8use bytes::Bytes;
+9use lru_slab::LruSlab;
+10use tracing::trace;
+11
+12use crate::token::TokenStore;
+13
+14/// `TokenStore` implementation that stores up to `N` tokens per server name for up to a
+15/// limited number of server names, in-memory
+16#[derive(Debug)]
+17pub struct TokenMemoryCache(Mutex<State>);
+18
+19impl TokenMemoryCache {
+20    /// Construct empty
+21    pub fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self {
+22        Self(Mutex::new(State::new(
+23            max_server_names,
+24            max_tokens_per_server,
+25        )))
+26    }
+27}
+28
+29impl TokenStore for TokenMemoryCache {
+30    fn insert(&self, server_name: &str, token: Bytes) {
+31        trace!(%server_name, "storing token");
+32        self.0.lock().unwrap().store(server_name, token)
+33    }
+34
+35    fn take(&self, server_name: &str) -> Option<Bytes> {
+36        let token = self.0.lock().unwrap().take(server_name);
+37        trace!(%server_name, found=%token.is_some(), "taking token");
+38        token
+39    }
+40}
+41
+42/// Defaults to a maximum of 256 servers and 2 tokens per server
+43impl Default for TokenMemoryCache {
+44    fn default() -> Self {
+45        Self::new(256, 2)
+46    }
+47}
+48
+49/// Lockable inner state of `TokenMemoryCache`
+50#[derive(Debug)]
+51struct State {
+52    max_server_names: u32,
+53    max_tokens_per_server: usize,
+54    // map from server name to index in lru
+55    lookup: HashMap<Arc<str>, u32>,
+56    lru: LruSlab<CacheEntry>,
+57}
+58
+59impl State {
+60    fn new(max_server_names: u32, max_tokens_per_server: usize) -> Self {
+61        Self {
+62            max_server_names,
+63            max_tokens_per_server,
+64            lookup: HashMap::new(),
+65            lru: LruSlab::default(),
+66        }
+67    }
+68
+69    fn store(&mut self, server_name: &str, token: Bytes) {
+70        if self.max_server_names == 0 {
+71            // the rest of this method assumes that we can always insert a new entry so long as
+72            // we're willing to evict a pre-existing entry. thus, an entry limit of 0 is an edge
+73            // case we must short-circuit on now.
+74            return;
+75        }
+76        if self.max_tokens_per_server == 0 {
+77            // similarly to above, the rest of this method assumes that we can always push a new
+78            // token to a queue so long as we're willing to evict a pre-existing token, so we
+79            // short-circuit on the edge case of a token limit of 0.
+80            return;
+81        }
+82
+83        let server_name = Arc::<str>::from(server_name);
+84        match self.lookup.entry(server_name.clone()) {
+85            hash_map::Entry::Occupied(hmap_entry) => {
+86                // key already exists, push the new token to its token queue
+87                let tokens = &mut self.lru.get_mut(*hmap_entry.get()).tokens;
+88                if tokens.len() >= self.max_tokens_per_server {
+89                    debug_assert!(tokens.len() == self.max_tokens_per_server);
+90                    tokens.pop_front().unwrap();
+91                }
+92                tokens.push_back(token);
+93            }
+94            hash_map::Entry::Vacant(hmap_entry) => {
+95                // key does not yet exist, create a new one, evicting the oldest if necessary
+96                let removed_key = if self.lru.len() >= self.max_server_names {
+97                    // unwrap safety: max_server_names is > 0, so there's at least one entry, so
+98                    //                lru() is some
+99                    Some(self.lru.remove(self.lru.lru().unwrap()).server_name)
+100                } else {
+101                    None
+102                };
+103
+104                hmap_entry.insert(self.lru.insert(CacheEntry::new(server_name, token)));
+105
+106                // for borrowing reasons, we must defer removing the evicted hmap entry to here
+107                if let Some(removed_slot) = removed_key {
+108                    let removed = self.lookup.remove(&removed_slot);
+109                    debug_assert!(removed.is_some());
+110                }
+111            }
+112        };
+113    }
+114
+115    fn take(&mut self, server_name: &str) -> Option<Bytes> {
+116        let slab_key = *self.lookup.get(server_name)?;
+117
+118        // pop from entry's token queue
+119        let entry = self.lru.get_mut(slab_key);
+120        // unwrap safety: we never leave tokens empty
+121        let token = entry.tokens.pop_front().unwrap();
+122
+123        if entry.tokens.is_empty() {
+124            // token stack emptied, remove entry
+125            self.lru.remove(slab_key);
+126            self.lookup.remove(server_name);
+127        }
+128
+129        Some(token)
+130    }
+131}
+132
+133/// Cache entry within `TokenMemoryCache`'s LRU slab
+134#[derive(Debug)]
+135struct CacheEntry {
+136    server_name: Arc<str>,
+137    // invariant: tokens is never empty
+138    tokens: VecDeque<Bytes>,
+139}
+140
+141impl CacheEntry {
+142    /// Construct with a single token
+143    fn new(server_name: Arc<str>, token: Bytes) -> Self {
+144        let mut tokens = VecDeque::new();
+145        tokens.push_back(token);
+146        Self {
+147            server_name,
+148            tokens,
+149        }
+150    }
+151}
+152
+153#[cfg(test)]
+154mod tests {
+155    use std::collections::VecDeque;
+156
+157    use super::*;
+158    use rand::prelude::*;
+159    use rand_pcg::Pcg32;
+160    use tracing::info;
+161
+162    fn new_rng() -> impl Rng {
+163        Pcg32::from_seed(0xdeadbeefdeadbeefdeadbeefdeadbeefu128.to_le_bytes())
+164    }
+165
+166    #[test]
+167    fn cache_test() {
+168        let mut rng = new_rng();
+169        const N: usize = 2;
+170
+171        for _ in 0..10 {
+172            let mut cache_1: Vec<(u32, VecDeque<Bytes>)> = Vec::new(); // keep it sorted oldest to newest
+173            let cache_2 = TokenMemoryCache::new(20, 2);
+174
+175            for i in 0..200 {
+176                let server_name = rng.random::<u32>() % 10;
+177                if rng.random_bool(0.666) {
+178                    // store
+179                    let token = Bytes::from(vec![i]);
+180                    info!("STORE {server_name} {token:?}");
+181                    if let Some((j, _)) = cache_1
+182                        .iter()
+183                        .enumerate()
+184                        .find(|&(_, &(server_name_2, _))| server_name_2 == server_name)
+185                    {
+186                        let (_, mut queue) = cache_1.remove(j);
+187                        queue.push_back(token.clone());
+188                        if queue.len() > N {
+189                            queue.pop_front();
+190                        }
+191                        cache_1.push((server_name, queue));
+192                    } else {
+193                        let mut queue = VecDeque::new();
+194                        queue.push_back(token.clone());
+195                        cache_1.push((server_name, queue));
+196                        if cache_1.len() > 20 {
+197                            cache_1.remove(0);
+198                        }
+199                    }
+200                    cache_2.insert(&server_name.to_string(), token);
+201                } else {
+202                    // take
+203                    info!("TAKE {server_name}");
+204                    let expecting = cache_1
+205                        .iter()
+206                        .enumerate()
+207                        .find(|&(_, &(server_name_2, _))| server_name_2 == server_name)
+208                        .map(|(j, _)| j)
+209                        .map(|j| {
+210                            let (_, mut queue) = cache_1.remove(j);
+211                            let token = queue.pop_front().unwrap();
+212                            if !queue.is_empty() {
+213                                cache_1.push((server_name, queue));
+214                            }
+215                            token
+216                        });
+217                    info!("EXPECTING {expecting:?}");
+218                    assert_eq!(cache_2.take(&server_name.to_string()), expecting);
+219                }
+220            }
+221        }
+222    }
+223
+224    #[test]
+225    fn zero_max_server_names() {
+226        // test that this edge case doesn't panic
+227        let cache = TokenMemoryCache::new(0, 2);
+228        for i in 0..10 {
+229            cache.insert(&i.to_string(), Bytes::from(vec![i]));
+230            for j in 0..10 {
+231                assert!(cache.take(&j.to_string()).is_none());
+232            }
+233        }
+234    }
+235
+236    #[test]
+237    fn zero_queue_length() {
+238        // test that this edge case doesn't panic
+239        let cache = TokenMemoryCache::new(256, 0);
+240        for i in 0..10 {
+241            cache.insert(&i.to_string(), Bytes::from(vec![i]));
+242            for j in 0..10 {
+243                assert!(cache.take(&j.to_string()).is_none());
+244            }
+245        }
+246    }
+247}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/transport_error.rs.html b/pr/234/docs/src/iroh_quinn_proto/transport_error.rs.html new file mode 100644 index 000000000..06b786f49 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/transport_error.rs.html @@ -0,0 +1,162 @@ +transport_error.rs - source

iroh_quinn_proto/
transport_error.rs

1use std::{fmt, sync::Arc};
+2
+3use bytes::{Buf, BufMut};
+4
+5use crate::{
+6    VarInt,
+7    coding::{self, BufExt, BufMutExt},
+8    frame,
+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: Option<frame::FrameType>,
+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: 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 let Some(frame) = self.frame {
+53            write!(f, " in {frame}")?;
+54        }
+55        if !self.reason.is_empty() {
+56            write!(f, ": {}", self.reason)?;
+57        }
+58        Ok(())
+59    }
+60}
+61
+62impl std::error::Error for Error {}
+63
+64/// Transport-level error code
+65#[derive(Copy, Clone, Eq, PartialEq)]
+66pub struct Code(u64);
+67
+68impl Code {
+69    /// Create QUIC error code from TLS alert code
+70    pub fn crypto(code: u8) -> Self {
+71        Self(0x100 | u64::from(code))
+72    }
+73}
+74
+75impl coding::Codec for Code {
+76    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+77        Ok(Self(buf.get_var()?))
+78    }
+79    fn encode<B: BufMut>(&self, buf: &mut B) {
+80        buf.write_var(self.0)
+81    }
+82}
+83
+84impl From<Code> for u64 {
+85    fn from(x: Code) -> Self {
+86        x.0
+87    }
+88}
+89
+90impl From<VarInt> for Code {
+91    fn from(value: VarInt) -> Self {
+92        Self(value.0)
+93    }
+94}
+95
+96impl From<Code> for VarInt {
+97    fn from(value: Code) -> Self {
+98        Self(value.0)
+99    }
+100}
+101
+102macro_rules! errors {
+103    {$($name:ident($val:expr) $desc:expr;)*} => {
+104        #[allow(non_snake_case, unused)]
+105        impl Error {
+106            $(
+107            pub(crate) fn $name<T>(reason: T) -> Self where T: Into<String> {
+108                Self::new(Code::$name, reason.into())
+109            }
+110            )*
+111        }
+112
+113        impl Code {
+114            $(#[doc = $desc] pub const $name: Self = Code($val);)*
+115        }
+116
+117        impl fmt::Debug for Code {
+118            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+119                match self.0 {
+120                    $($val => f.write_str(stringify!($name)),)*
+121                    x if (0x100..0x200).contains(&x) => write!(f, "Code::crypto({:02x})", self.0 as u8),
+122                    _ => write!(f, "Code({:x})", self.0),
+123                }
+124            }
+125        }
+126
+127        impl fmt::Display for Code {
+128            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+129                match self.0 {
+130                    $($val => f.write_str($desc),)*
+131                    // We're trying to be abstract over the crypto protocol, so human-readable descriptions here is tricky.
+132                    _ if self.0 >= 0x100 && self.0 < 0x200 => write!(f, "the cryptographic handshake failed: error {}", self.0 & 0xFF),
+133                    _ => f.write_str("unknown error"),
+134                }
+135            }
+136        }
+137    }
+138}
+139
+140errors! {
+141    NO_ERROR(0x0) "the connection is being closed abruptly in the absence of any error";
+142    INTERNAL_ERROR(0x1) "the endpoint encountered an internal error and cannot continue with the connection";
+143    CONNECTION_REFUSED(0x2) "the server refused to accept a new connection";
+144    FLOW_CONTROL_ERROR(0x3) "received more data than permitted in advertised data limits";
+145    STREAM_LIMIT_ERROR(0x4) "received a frame for a stream identifier that exceeded advertised the stream limit for the corresponding stream type";
+146    STREAM_STATE_ERROR(0x5) "received a frame for a stream that was not in a state that permitted that frame";
+147    FINAL_SIZE_ERROR(0x6) "received a STREAM frame or a RESET_STREAM frame containing a different final size to the one already established";
+148    FRAME_ENCODING_ERROR(0x7) "received a frame that was badly formatted";
+149    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";
+150    CONNECTION_ID_LIMIT_ERROR(0x9) "the number of connection IDs provided by the peer exceeds the advertised active_connection_id_limit";
+151    PROTOCOL_VIOLATION(0xA) "detected an error with protocol compliance that was not covered by more specific error codes";
+152    INVALID_TOKEN(0xB) "received an invalid Retry Token in a client Initial";
+153    APPLICATION_ERROR(0xC) "the application or application protocol caused the connection to be closed during the handshake";
+154    CRYPTO_BUFFER_EXCEEDED(0xD) "received more data in CRYPTO frames than can be buffered";
+155    KEY_UPDATE_ERROR(0xE) "key update error";
+156    AEAD_LIMIT_REACHED(0xF) "the endpoint has reached the confidentiality or integrity limit for the AEAD algorithm";
+157    NO_VIABLE_PATH(0x10) "no viable network path exists";
+158    APPLICATION_ABANDON_PATH(0x004150504142414e) "Path abandoned at the application's request";
+159    PATH_RESOURCE_LIMIT_REACHED(0x0052534c494d4954) "Path abandoned due to resource limitations in the transport";
+160    PATH_UNSTABLE_OR_POOR(0x00554e5f494e5446) "Path abandoned due to unstable interfaces";
+161    NO_CID_AVAILABLE_FOR_PATH(0x004e4f5f4349445f) "Path abandoned due to no available connection IDs for the path";
+162}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/transport_parameters.rs.html b/pr/234/docs/src/iroh_quinn_proto/transport_parameters.rs.html new file mode 100644 index 000000000..ec1947c1f --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/transport_parameters.rs.html @@ -0,0 +1,978 @@ +transport_parameters.rs - source

iroh_quinn_proto/
transport_parameters.rs

1//! QUIC connection transport parameters
+2//!
+3//! The `TransportParameters` type is used to represent the transport parameters
+4//! negotiated by peers while establishing a QUIC connection. This process
+5//! happens as part of the establishment of the TLS session. As such, the types
+6//! contained in this modules should generally only be referred to by custom
+7//! implementations of the `crypto::Session` trait.
+8
+9use std::{
+10    convert::TryFrom,
+11    net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
+12    num::NonZeroU8,
+13};
+14
+15use bytes::{Buf, BufMut};
+16use rand::{Rng as _, RngCore, seq::SliceRandom as _};
+17use thiserror::Error;
+18
+19use crate::{
+20    LOC_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
+21    TIMER_GRANULARITY, TransportError, VarInt, address_discovery,
+22    cid_generator::ConnectionIdGenerator,
+23    cid_queue::CidQueue,
+24    coding::{BufExt, BufMutExt, UnexpectedEnd},
+25    config::{EndpointConfig, ServerConfig, TransportConfig},
+26    connection::PathId,
+27    shared::ConnectionId,
+28};
+29
+30// Apply a given macro to a list of all the transport parameters having integer types, along with
+31// their codes and default values. Using this helps us avoid error-prone duplication of the
+32// contained information across decoding, encoding, and the `Default` impl. Whenever we want to do
+33// something with transport parameters, we'll handle the bulk of cases by writing a macro that
+34// takes a list of arguments in this form, then passing it to this macro.
+35macro_rules! apply_params {
+36    ($macro:ident) => {
+37        $macro! {
+38            // #[doc] name (id) = default,
+39            /// Milliseconds, disabled if zero
+40            max_idle_timeout(MaxIdleTimeout) = 0,
+41            /// Limits the size of UDP payloads that the endpoint is willing to receive
+42            max_udp_payload_size(MaxUdpPayloadSize) = 65527,
+43
+44            /// Initial value for the maximum amount of data that can be sent on the connection
+45            initial_max_data(InitialMaxData) = 0,
+46            /// Initial flow control limit for locally-initiated bidirectional streams
+47            initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
+48            /// Initial flow control limit for peer-initiated bidirectional streams
+49            initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
+50            /// Initial flow control limit for unidirectional streams
+51            initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
+52
+53            /// Initial maximum number of bidirectional streams the peer may initiate
+54            initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
+55            /// Initial maximum number of unidirectional streams the peer may initiate
+56            initial_max_streams_uni(InitialMaxStreamsUni) = 0,
+57
+58            /// Exponent used to decode the ACK Delay field in the ACK frame
+59            ack_delay_exponent(AckDelayExponent) = 3,
+60            /// Maximum amount of time in milliseconds by which the endpoint will delay sending
+61            /// acknowledgments
+62            max_ack_delay(MaxAckDelay) = 25,
+63            /// Maximum number of connection IDs from the peer that an endpoint is willing to store
+64            active_connection_id_limit(ActiveConnectionIdLimit) = 2,
+65        }
+66    };
+67}
+68
+69macro_rules! make_struct {
+70    {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+71        /// Transport parameters used to negotiate connection-level preferences between peers
+72        #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+73        pub struct TransportParameters {
+74            $($(#[$doc])* pub(crate) $name : VarInt,)*
+75
+76            /// Does the endpoint support active connection migration
+77            pub(crate) disable_active_migration: bool,
+78            /// Maximum size for datagram frames
+79            pub(crate) max_datagram_frame_size: Option<VarInt>,
+80            /// The value that the endpoint included in the Source Connection ID field of the first
+81            /// Initial packet it sends for the connection
+82            pub(crate) initial_src_cid: Option<ConnectionId>,
+83            /// The endpoint is willing to receive QUIC packets containing any value for the fixed
+84            /// bit
+85            pub(crate) grease_quic_bit: bool,
+86
+87            /// Minimum amount of time in microseconds by which the endpoint is able to delay
+88            /// sending acknowledgments
+89            ///
+90            /// If a value is provided, it implies that the endpoint supports QUIC Acknowledgement
+91            /// Frequency
+92            pub(crate) min_ack_delay: Option<VarInt>,
+93
+94            // Server-only
+95            /// The value of the Destination Connection ID field from the first Initial packet sent
+96            /// by the client
+97            pub(crate) original_dst_cid: Option<ConnectionId>,
+98            /// The value that the server included in the Source Connection ID field of a Retry
+99            /// packet
+100            pub(crate) retry_src_cid: Option<ConnectionId>,
+101            /// Token used by the client to verify a stateless reset from the server
+102            pub(crate) stateless_reset_token: Option<ResetToken>,
+103            /// The server's preferred address for communication after handshake completion
+104            pub(crate) preferred_address: Option<PreferredAddress>,
+105
+106            /// The randomly generated reserved transport parameter to sustain future extensibility
+107            /// of transport parameter extensions.
+108            /// When present, it is included during serialization but ignored during deserialization.
+109            pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
+110
+111            /// Defines the order in which transport parameters are serialized.
+112            ///
+113            /// This field is initialized only for outgoing `TransportParameters` instances and
+114            /// is set to `None` for `TransportParameters` received from a peer.
+115            pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
+116
+117            /// The role of this peer in address discovery, if any.
+118            pub(crate) address_discovery_role: address_discovery::Role,
+119
+120            /// Multipath extension
+121            pub(crate) initial_max_path_id: Option<PathId>,
+122
+123            /// Nat traversal draft
+124            pub max_remote_nat_traversal_addresses: Option<NonZeroU8>,
+125        }
+126
+127        // We deliberately don't implement the `Default` trait, since that would be public, and
+128        // downstream crates should never construct `TransportParameters` except by decoding those
+129        // supplied by a peer.
+130        impl TransportParameters {
+131            /// Standard defaults, used if the peer does not supply a given parameter.
+132            pub(crate) fn default() -> Self {
+133                Self {
+134                    $($name: VarInt::from_u32($default),)*
+135
+136                    disable_active_migration: false,
+137                    max_datagram_frame_size: None,
+138                    initial_src_cid: None,
+139                    grease_quic_bit: false,
+140                    min_ack_delay: None,
+141
+142                    original_dst_cid: None,
+143                    retry_src_cid: None,
+144                    stateless_reset_token: None,
+145                    preferred_address: None,
+146                    grease_transport_parameter: None,
+147                    write_order: None,
+148                    address_discovery_role: address_discovery::Role::Disabled,
+149                    initial_max_path_id: None,
+150                    max_remote_nat_traversal_addresses: None,
+151                }
+152            }
+153        }
+154    }
+155}
+156
+157apply_params!(make_struct);
+158
+159impl TransportParameters {
+160    pub(crate) fn new(
+161        config: &TransportConfig,
+162        endpoint_config: &EndpointConfig,
+163        cid_gen: &dyn ConnectionIdGenerator,
+164        initial_src_cid: ConnectionId,
+165        server_config: Option<&ServerConfig>,
+166        rng: &mut impl RngCore,
+167    ) -> Self {
+168        Self {
+169            initial_src_cid: Some(initial_src_cid),
+170            initial_max_streams_bidi: config.max_concurrent_bidi_streams,
+171            initial_max_streams_uni: config.max_concurrent_uni_streams,
+172            initial_max_data: config.receive_window,
+173            initial_max_stream_data_bidi_local: config.stream_receive_window,
+174            initial_max_stream_data_bidi_remote: config.stream_receive_window,
+175            initial_max_stream_data_uni: config.stream_receive_window,
+176            max_udp_payload_size: endpoint_config.max_udp_payload_size,
+177            max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
+178            disable_active_migration: server_config.is_some_and(|c| !c.migration),
+179            active_connection_id_limit: if cid_gen.cid_len() == 0 {
+180                2 // i.e. default, i.e. unsent
+181            } else {
+182                CidQueue::LEN as u32
+183            }
+184            .into(),
+185            max_datagram_frame_size: config
+186                .datagram_receive_buffer_size
+187                .map(|x| (x.min(u16::MAX.into()) as u16).into()),
+188            grease_quic_bit: endpoint_config.grease_quic_bit,
+189            min_ack_delay: Some(
+190                VarInt::from_u64(u64::try_from(TIMER_GRANULARITY.as_micros()).unwrap()).unwrap(),
+191            ),
+192            grease_transport_parameter: Some(ReservedTransportParameter::random(rng)),
+193            write_order: Some({
+194                let mut order = std::array::from_fn(|i| i as u8);
+195                order.shuffle(rng);
+196                order
+197            }),
+198            address_discovery_role: config.address_discovery_role,
+199            initial_max_path_id: config.get_initial_max_path_id(),
+200            max_remote_nat_traversal_addresses: config.max_remote_nat_traversal_addresses,
+201            ..Self::default()
+202        }
+203    }
+204
+205    /// Check that these parameters are legal when resuming from
+206    /// certain cached parameters
+207    pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
+208        if cached.active_connection_id_limit > self.active_connection_id_limit
+209            || cached.initial_max_data > self.initial_max_data
+210            || cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
+211            || cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
+212            || cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
+213            || cached.initial_max_streams_bidi > self.initial_max_streams_bidi
+214            || cached.initial_max_streams_uni > self.initial_max_streams_uni
+215            || cached.max_datagram_frame_size > self.max_datagram_frame_size
+216            || cached.grease_quic_bit && !self.grease_quic_bit
+217            || cached.address_discovery_role != self.address_discovery_role
+218            || cached.max_remote_nat_traversal_addresses != self.max_remote_nat_traversal_addresses
+219        {
+220            return Err(TransportError::PROTOCOL_VIOLATION(
+221                "0-RTT accepted with incompatible transport parameters",
+222            ));
+223        }
+224        Ok(())
+225    }
+226
+227    /// Maximum number of CIDs to issue to this peer
+228    ///
+229    /// Consider both a) the active_connection_id_limit from the other end; and
+230    /// b) LOC_CID_COUNT used locally
+231    pub(crate) fn issue_cids_limit(&self) -> u64 {
+232        self.active_connection_id_limit.0.min(LOC_CID_COUNT)
+233    }
+234}
+235
+236/// A server's preferred address
+237///
+238/// This is communicated as a transport parameter during TLS session establishment.
+239#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+240pub(crate) struct PreferredAddress {
+241    pub(crate) address_v4: Option<SocketAddrV4>,
+242    pub(crate) address_v6: Option<SocketAddrV6>,
+243    pub(crate) connection_id: ConnectionId,
+244    pub(crate) stateless_reset_token: ResetToken,
+245}
+246
+247impl PreferredAddress {
+248    fn wire_size(&self) -> u16 {
+249        4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
+250    }
+251
+252    fn write<W: BufMut>(&self, w: &mut W) {
+253        w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
+254        w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
+255        w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
+256        w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
+257        w.write::<u8>(self.connection_id.len() as u8);
+258        w.put_slice(&self.connection_id);
+259        w.put_slice(&self.stateless_reset_token);
+260    }
+261
+262    fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
+263        let ip_v4 = r.get::<Ipv4Addr>()?;
+264        let port_v4 = r.get::<u16>()?;
+265        let ip_v6 = r.get::<Ipv6Addr>()?;
+266        let port_v6 = r.get::<u16>()?;
+267        let cid_len = r.get::<u8>()?;
+268        if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
+269            return Err(Error::Malformed);
+270        }
+271        let mut stage = [0; MAX_CID_SIZE];
+272        r.copy_to_slice(&mut stage[0..cid_len as usize]);
+273        let cid = ConnectionId::new(&stage[0..cid_len as usize]);
+274        if r.remaining() < 16 {
+275            return Err(Error::Malformed);
+276        }
+277        let mut token = [0; RESET_TOKEN_SIZE];
+278        r.copy_to_slice(&mut token);
+279        let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
+280            None
+281        } else {
+282            Some(SocketAddrV4::new(ip_v4, port_v4))
+283        };
+284        let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
+285            None
+286        } else {
+287            Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
+288        };
+289        if address_v4.is_none() && address_v6.is_none() {
+290            return Err(Error::IllegalValue);
+291        }
+292        Ok(Self {
+293            address_v4,
+294            address_v6,
+295            connection_id: cid,
+296            stateless_reset_token: token.into(),
+297        })
+298    }
+299}
+300
+301/// Errors encountered while decoding `TransportParameters`
+302#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+303pub enum Error {
+304    /// Parameters that are semantically invalid
+305    #[error("parameter had illegal value")]
+306    IllegalValue,
+307    /// Catch-all error for problems while decoding transport parameters
+308    #[error("parameters were malformed")]
+309    Malformed,
+310}
+311
+312impl From<Error> for TransportError {
+313    fn from(e: Error) -> Self {
+314        match e {
+315            Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
+316            Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
+317        }
+318    }
+319}
+320
+321impl From<UnexpectedEnd> for Error {
+322    fn from(_: UnexpectedEnd) -> Self {
+323        Self::Malformed
+324    }
+325}
+326
+327impl TransportParameters {
+328    /// Encode `TransportParameters` into buffer
+329    pub fn write<W: BufMut>(&self, w: &mut W) {
+330        for idx in self
+331            .write_order
+332            .as_ref()
+333            .unwrap_or(&std::array::from_fn(|i| i as u8))
+334        {
+335            let id = TransportParameterId::SUPPORTED[*idx as usize];
+336            match id {
+337                TransportParameterId::ReservedTransportParameter => {
+338                    if let Some(param) = self.grease_transport_parameter {
+339                        param.write(w);
+340                    }
+341                }
+342                TransportParameterId::StatelessResetToken => {
+343                    if let Some(ref x) = self.stateless_reset_token {
+344                        w.write_var(id as u64);
+345                        w.write_var(16);
+346                        w.put_slice(x);
+347                    }
+348                }
+349                TransportParameterId::DisableActiveMigration => {
+350                    if self.disable_active_migration {
+351                        w.write_var(id as u64);
+352                        w.write_var(0);
+353                    }
+354                }
+355                TransportParameterId::MaxDatagramFrameSize => {
+356                    if let Some(x) = self.max_datagram_frame_size {
+357                        w.write_var(id as u64);
+358                        w.write_var(x.size() as u64);
+359                        w.write(x);
+360                    }
+361                }
+362                TransportParameterId::PreferredAddress => {
+363                    if let Some(ref x) = self.preferred_address {
+364                        w.write_var(id as u64);
+365                        w.write_var(x.wire_size() as u64);
+366                        x.write(w);
+367                    }
+368                }
+369                TransportParameterId::OriginalDestinationConnectionId => {
+370                    if let Some(ref cid) = self.original_dst_cid {
+371                        w.write_var(id as u64);
+372                        w.write_var(cid.len() as u64);
+373                        w.put_slice(cid);
+374                    }
+375                }
+376                TransportParameterId::InitialSourceConnectionId => {
+377                    if let Some(ref cid) = self.initial_src_cid {
+378                        w.write_var(id as u64);
+379                        w.write_var(cid.len() as u64);
+380                        w.put_slice(cid);
+381                    }
+382                }
+383                TransportParameterId::RetrySourceConnectionId => {
+384                    if let Some(ref cid) = self.retry_src_cid {
+385                        w.write_var(id as u64);
+386                        w.write_var(cid.len() as u64);
+387                        w.put_slice(cid);
+388                    }
+389                }
+390                TransportParameterId::GreaseQuicBit => {
+391                    if self.grease_quic_bit {
+392                        w.write_var(id as u64);
+393                        w.write_var(0);
+394                    }
+395                }
+396                TransportParameterId::MinAckDelayDraft07 => {
+397                    if let Some(x) = self.min_ack_delay {
+398                        w.write_var(id as u64);
+399                        w.write_var(x.size() as u64);
+400                        w.write(x);
+401                    }
+402                }
+403                TransportParameterId::ObservedAddr => {
+404                    if let Some(varint_role) = self.address_discovery_role.as_transport_parameter()
+405                    {
+406                        w.write_var(id as u64);
+407                        w.write_var(varint_role.size() as u64);
+408                        w.write(varint_role);
+409                    }
+410                }
+411                TransportParameterId::InitialMaxPathId => {
+412                    if let Some(val) = self.initial_max_path_id {
+413                        w.write_var(id as u64);
+414                        w.write_var(val.size() as u64);
+415                        w.write(val);
+416                    }
+417                }
+418                TransportParameterId::IrohNatTraversal => {
+419                    if let Some(val) = self.max_remote_nat_traversal_addresses {
+420                        w.write_var(id as u64);
+421                        w.write(VarInt(1));
+422                        w.write(val.get());
+423                    }
+424                }
+425                id => {
+426                    macro_rules! write_params {
+427                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+428                            match id {
+429                                $(TransportParameterId::$id => {
+430                                    if self.$name.0 != $default {
+431                                        w.write_var(id as u64);
+432                                        w.write(VarInt::try_from(self.$name.size()).unwrap());
+433                                        w.write(self.$name);
+434                                    }
+435                                })*,
+436                                _ => {
+437                                    unimplemented!("Missing implementation of write for transport parameter with code {id:?}");
+438                                }
+439                            }
+440                        }
+441                    }
+442                    apply_params!(write_params);
+443                }
+444            }
+445        }
+446    }
+447
+448    /// Decode `TransportParameters` from buffer
+449    pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
+450        // Initialize to protocol-specified defaults
+451        let mut params = Self::default();
+452
+453        // State to check for duplicate transport parameters.
+454        macro_rules! param_state {
+455            {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {{
+456                struct ParamState {
+457                    $($name: bool,)*
+458                }
+459
+460                ParamState {
+461                    $($name: false,)*
+462                }
+463            }}
+464        }
+465        let mut got = apply_params!(param_state);
+466
+467        while r.has_remaining() {
+468            let id = r.get_var()?;
+469            let len = r.get_var()?;
+470            if (r.remaining() as u64) < len {
+471                return Err(Error::Malformed);
+472            }
+473            let len = len as usize;
+474            let Ok(id) = TransportParameterId::try_from(id) else {
+475                // unknown transport parameters are ignored
+476                r.advance(len);
+477                continue;
+478            };
+479
+480            match id {
+481                TransportParameterId::OriginalDestinationConnectionId => {
+482                    decode_cid(len, &mut params.original_dst_cid, r)?
+483                }
+484                TransportParameterId::StatelessResetToken => {
+485                    if len != 16 || params.stateless_reset_token.is_some() {
+486                        return Err(Error::Malformed);
+487                    }
+488                    let mut tok = [0; RESET_TOKEN_SIZE];
+489                    r.copy_to_slice(&mut tok);
+490                    params.stateless_reset_token = Some(tok.into());
+491                }
+492                TransportParameterId::DisableActiveMigration => {
+493                    if len != 0 || params.disable_active_migration {
+494                        return Err(Error::Malformed);
+495                    }
+496                    params.disable_active_migration = true;
+497                }
+498                TransportParameterId::PreferredAddress => {
+499                    if params.preferred_address.is_some() {
+500                        return Err(Error::Malformed);
+501                    }
+502                    params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
+503                }
+504                TransportParameterId::InitialSourceConnectionId => {
+505                    decode_cid(len, &mut params.initial_src_cid, r)?
+506                }
+507                TransportParameterId::RetrySourceConnectionId => {
+508                    decode_cid(len, &mut params.retry_src_cid, r)?
+509                }
+510                TransportParameterId::MaxDatagramFrameSize => {
+511                    if len > 8 || params.max_datagram_frame_size.is_some() {
+512                        return Err(Error::Malformed);
+513                    }
+514                    params.max_datagram_frame_size = Some(r.get().unwrap());
+515                }
+516                TransportParameterId::GreaseQuicBit => match len {
+517                    0 => params.grease_quic_bit = true,
+518                    _ => return Err(Error::Malformed),
+519                },
+520                TransportParameterId::MinAckDelayDraft07 => {
+521                    params.min_ack_delay = Some(r.get().unwrap())
+522                }
+523                TransportParameterId::ObservedAddr => {
+524                    if !params.address_discovery_role.is_disabled() {
+525                        // duplicate parameter
+526                        return Err(Error::Malformed);
+527                    }
+528                    let value: VarInt = r.get()?;
+529                    if len != value.size() {
+530                        return Err(Error::Malformed);
+531                    }
+532                    params.address_discovery_role = value.try_into()?;
+533                    tracing::debug!(
+534                        role = ?params.address_discovery_role,
+535                        "address discovery enabled for peer"
+536                    );
+537                }
+538                TransportParameterId::InitialMaxPathId => {
+539                    if params.initial_max_path_id.is_some() {
+540                        return Err(Error::Malformed);
+541                    }
+542
+543                    let value: PathId = r.get()?;
+544                    if len != value.size() {
+545                        return Err(Error::Malformed);
+546                    }
+547
+548                    params.initial_max_path_id = Some(value);
+549                }
+550                TransportParameterId::IrohNatTraversal => {
+551                    if params.max_remote_nat_traversal_addresses.is_some() {
+552                        return Err(Error::Malformed);
+553                    }
+554                    if len != 1 {
+555                        return Err(Error::Malformed);
+556                    }
+557
+558                    let value: u8 = r.get()?;
+559                    let value = NonZeroU8::new(value).ok_or(Error::IllegalValue)?;
+560
+561                    params.max_remote_nat_traversal_addresses = Some(value);
+562                }
+563                _ => {
+564                    macro_rules! parse {
+565                        {$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr,)*} => {
+566                            match id {
+567                                $(TransportParameterId::$id => {
+568                                    let value = r.get::<VarInt>()?;
+569                                    if len != value.size() || got.$name { return Err(Error::Malformed); }
+570                                    params.$name = value.into();
+571                                    got.$name = true;
+572                                })*
+573                                _ => r.advance(len),
+574                            }
+575                        }
+576                    }
+577                    apply_params!(parse);
+578                }
+579            }
+580        }
+581
+582        // Semantic validation
+583
+584        // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.26.1
+585        if params.ack_delay_exponent.0 > 20
+586            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.28.1
+587            || params.max_ack_delay.0 >= 1 << 14
+588            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-6.2.1
+589            || params.active_connection_id_limit.0 < 2
+590            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.10.1
+591            || params.max_udp_payload_size.0 < 1200
+592            // https://www.rfc-editor.org/rfc/rfc9000.html#section-4.6-2
+593            || params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT
+594            || params.initial_max_streams_uni.0 > MAX_STREAM_COUNT
+595            // https://www.ietf.org/archive/id/draft-ietf-quic-ack-frequency-08.html#section-3-4
+596            || params.min_ack_delay.is_some_and(|min_ack_delay| {
+597                // min_ack_delay uses microseconds, whereas max_ack_delay uses milliseconds
+598                min_ack_delay.0 > params.max_ack_delay.0 * 1_000
+599            })
+600            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-8
+601            || (side.is_server()
+602                && (params.original_dst_cid.is_some()
+603                    || params.preferred_address.is_some()
+604                    || params.retry_src_cid.is_some()
+605                    || params.stateless_reset_token.is_some()))
+606            // https://www.rfc-editor.org/rfc/rfc9000.html#section-18.2-4.38.1
+607            || params
+608                .preferred_address.is_some_and(|x| x.connection_id.is_empty())
+609        {
+610            return Err(Error::IllegalValue);
+611        }
+612
+613        Ok(params)
+614    }
+615}
+616
+617/// A reserved transport parameter.
+618///
+619/// It has an identifier of the form 31 * N + 27 for the integer value of N.
+620/// Such identifiers are reserved to exercise the requirement that unknown transport parameters be ignored.
+621/// The reserved transport parameter has no semantics and can carry arbitrary values.
+622/// It may be included in transport parameters sent to the peer, and should be ignored when received.
+623///
+624/// See spec: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1>
+625#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+626pub(crate) struct ReservedTransportParameter {
+627    /// The reserved identifier of the transport parameter
+628    id: VarInt,
+629
+630    /// Buffer to store the parameter payload
+631    payload: [u8; Self::MAX_PAYLOAD_LEN],
+632
+633    /// The number of bytes to include in the wire format from the `payload` buffer
+634    payload_len: usize,
+635}
+636
+637impl ReservedTransportParameter {
+638    /// Generates a transport parameter with a random payload and a reserved ID.
+639    ///
+640    /// The implementation is inspired by quic-go and quiche:
+641    /// 1. <https://github.com/quic-go/quic-go/blob/3e0a67b2476e1819752f04d75968de042b197b56/internal/wire/transport_parameters.go#L338-L344>
+642    /// 2. <https://github.com/google/quiche/blob/cb1090b20c40e2f0815107857324e99acf6ec567/quiche/quic/core/crypto/transport_parameters.cc#L843-L860>
+643    fn random(rng: &mut impl RngCore) -> Self {
+644        let id = Self::generate_reserved_id(rng);
+645
+646        let payload_len = rng.random_range(0..Self::MAX_PAYLOAD_LEN);
+647
+648        let payload = {
+649            let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
+650            rng.fill_bytes(&mut slice[..payload_len]);
+651            slice
+652        };
+653
+654        Self {
+655            id,
+656            payload,
+657            payload_len,
+658        }
+659    }
+660
+661    fn write(&self, w: &mut impl BufMut) {
+662        w.write_var(self.id.0);
+663        w.write_var(self.payload_len as u64);
+664        w.put_slice(&self.payload[..self.payload_len]);
+665    }
+666
+667    /// Generates a random reserved identifier of the form `31 * N + 27`, as required by RFC 9000.
+668    /// Reserved transport parameter identifiers are used to test compliance with the requirement
+669    /// that unknown transport parameters must be ignored by peers.
+670    /// See: <https://www.rfc-editor.org/rfc/rfc9000.html#section-18.1> and <https://www.rfc-editor.org/rfc/rfc9000.html#section-22.3>
+671    fn generate_reserved_id(rng: &mut impl RngCore) -> VarInt {
+672        let id = {
+673            let rand = rng.random_range(0u64..(1 << 62) - 27);
+674            let n = rand / 31;
+675            31 * n + 27
+676        };
+677        debug_assert!(
+678            id % 31 == 27,
+679            "generated id does not have the form of 31 * N + 27"
+680        );
+681        VarInt::from_u64(id).expect(
+682            "generated id does fit into range of allowed transport parameter IDs: [0; 2^62)",
+683        )
+684    }
+685
+686    /// The maximum length of the payload to include as the parameter payload.
+687    /// This value is not a specification-imposed limit but is chosen to match
+688    /// the limit used by other implementations of QUIC, e.g., quic-go and quiche.
+689    const MAX_PAYLOAD_LEN: usize = 16;
+690}
+691
+692#[repr(u64)]
+693#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+694pub(crate) enum TransportParameterId {
+695    // https://www.rfc-editor.org/rfc/rfc9000.html#iana-tp-table
+696    OriginalDestinationConnectionId = 0x00,
+697    MaxIdleTimeout = 0x01,
+698    StatelessResetToken = 0x02,
+699    MaxUdpPayloadSize = 0x03,
+700    InitialMaxData = 0x04,
+701    InitialMaxStreamDataBidiLocal = 0x05,
+702    InitialMaxStreamDataBidiRemote = 0x06,
+703    InitialMaxStreamDataUni = 0x07,
+704    InitialMaxStreamsBidi = 0x08,
+705    InitialMaxStreamsUni = 0x09,
+706    AckDelayExponent = 0x0A,
+707    MaxAckDelay = 0x0B,
+708    DisableActiveMigration = 0x0C,
+709    PreferredAddress = 0x0D,
+710    ActiveConnectionIdLimit = 0x0E,
+711    InitialSourceConnectionId = 0x0F,
+712    RetrySourceConnectionId = 0x10,
+713
+714    // Smallest possible ID of reserved transport parameter https://datatracker.ietf.org/doc/html/rfc9000#section-22.3
+715    ReservedTransportParameter = 0x1B,
+716
+717    // https://www.rfc-editor.org/rfc/rfc9221.html#section-3
+718    MaxDatagramFrameSize = 0x20,
+719
+720    // https://www.rfc-editor.org/rfc/rfc9287.html#section-3
+721    GreaseQuicBit = 0x2AB2,
+722
+723    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-ack-frequency#section-10.1
+724    MinAckDelayDraft07 = 0xFF04DE1B,
+725
+726    // <https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/>
+727    ObservedAddr = 0x9f81a176,
+728
+729    // https://datatracker.ietf.org/doc/html/draft-ietf-quic-multipath
+730    InitialMaxPathId = 0x0f739bbc1b666d0c,
+731
+732    // inspired by https://www.ietf.org/archive/id/draft-seemann-quic-nat-traversal-02.html,
+733    // simplified to iroh's needs
+734    IrohNatTraversal = 0x3d7f91120401,
+735}
+736
+737impl TransportParameterId {
+738    /// Array with all supported transport parameter IDs
+739    const SUPPORTED: [Self; 24] = [
+740        Self::MaxIdleTimeout,
+741        Self::MaxUdpPayloadSize,
+742        Self::InitialMaxData,
+743        Self::InitialMaxStreamDataBidiLocal,
+744        Self::InitialMaxStreamDataBidiRemote,
+745        Self::InitialMaxStreamDataUni,
+746        Self::InitialMaxStreamsBidi,
+747        Self::InitialMaxStreamsUni,
+748        Self::AckDelayExponent,
+749        Self::MaxAckDelay,
+750        Self::ActiveConnectionIdLimit,
+751        Self::ReservedTransportParameter,
+752        Self::StatelessResetToken,
+753        Self::DisableActiveMigration,
+754        Self::MaxDatagramFrameSize,
+755        Self::PreferredAddress,
+756        Self::OriginalDestinationConnectionId,
+757        Self::InitialSourceConnectionId,
+758        Self::RetrySourceConnectionId,
+759        Self::GreaseQuicBit,
+760        Self::MinAckDelayDraft07,
+761        Self::ObservedAddr,
+762        Self::InitialMaxPathId,
+763        Self::IrohNatTraversal,
+764    ];
+765}
+766
+767impl std::cmp::PartialEq<u64> for TransportParameterId {
+768    fn eq(&self, other: &u64) -> bool {
+769        *other == (*self as u64)
+770    }
+771}
+772
+773impl TryFrom<u64> for TransportParameterId {
+774    type Error = ();
+775
+776    fn try_from(value: u64) -> Result<Self, Self::Error> {
+777        let param = match value {
+778            id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
+779            id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
+780            id if Self::InitialMaxData == id => Self::InitialMaxData,
+781            id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
+782            id if Self::InitialMaxStreamDataBidiRemote == id => {
+783                Self::InitialMaxStreamDataBidiRemote
+784            }
+785            id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
+786            id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
+787            id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
+788            id if Self::AckDelayExponent == id => Self::AckDelayExponent,
+789            id if Self::MaxAckDelay == id => Self::MaxAckDelay,
+790            id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
+791            id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
+792            id if Self::StatelessResetToken == id => Self::StatelessResetToken,
+793            id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
+794            id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
+795            id if Self::PreferredAddress == id => Self::PreferredAddress,
+796            id if Self::OriginalDestinationConnectionId == id => {
+797                Self::OriginalDestinationConnectionId
+798            }
+799            id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
+800            id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
+801            id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
+802            id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
+803            id if Self::ObservedAddr == id => Self::ObservedAddr,
+804            id if Self::InitialMaxPathId == id => Self::InitialMaxPathId,
+805            id if Self::IrohNatTraversal == id => Self::IrohNatTraversal,
+806            _ => return Err(()),
+807        };
+808        Ok(param)
+809    }
+810}
+811
+812fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
+813    if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
+814        return Err(Error::Malformed);
+815    }
+816
+817    *value = Some(ConnectionId::from_buf(r, len));
+818    Ok(())
+819}
+820
+821#[cfg(test)]
+822mod test {
+823
+824    use super::*;
+825
+826    #[test]
+827    fn coding() {
+828        let mut buf = Vec::new();
+829        let params = TransportParameters {
+830            initial_src_cid: Some(ConnectionId::new(&[])),
+831            original_dst_cid: Some(ConnectionId::new(&[])),
+832            initial_max_streams_bidi: 16u32.into(),
+833            initial_max_streams_uni: 16u32.into(),
+834            ack_delay_exponent: 2u32.into(),
+835            max_udp_payload_size: 1200u32.into(),
+836            preferred_address: Some(PreferredAddress {
+837                address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+838                address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
+839                connection_id: ConnectionId::new(&[0x42]),
+840                stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+841            }),
+842            grease_quic_bit: true,
+843            min_ack_delay: Some(2_000u32.into()),
+844            address_discovery_role: address_discovery::Role::SendOnly,
+845            initial_max_path_id: Some(PathId::MAX),
+846            max_remote_nat_traversal_addresses: Some(5u8.try_into().unwrap()),
+847            ..TransportParameters::default()
+848        };
+849        params.write(&mut buf);
+850        assert_eq!(
+851            TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
+852            params
+853        );
+854    }
+855
+856    #[test]
+857    fn reserved_transport_parameter_generate_reserved_id() {
+858        let mut rngs = [
+859            StepRng(0),
+860            StepRng(1),
+861            StepRng(27),
+862            StepRng(31),
+863            StepRng(u32::MAX as u64),
+864            StepRng(u32::MAX as u64 - 1),
+865            StepRng(u32::MAX as u64 + 1),
+866            StepRng(u32::MAX as u64 - 27),
+867            StepRng(u32::MAX as u64 + 27),
+868            StepRng(u32::MAX as u64 - 31),
+869            StepRng(u32::MAX as u64 + 31),
+870            StepRng(u64::MAX),
+871            StepRng(u64::MAX - 1),
+872            StepRng(u64::MAX - 27),
+873            StepRng(u64::MAX - 31),
+874            StepRng(1 << 62),
+875            StepRng((1 << 62) - 1),
+876            StepRng((1 << 62) + 1),
+877            StepRng((1 << 62) - 27),
+878            StepRng((1 << 62) + 27),
+879            StepRng((1 << 62) - 31),
+880            StepRng((1 << 62) + 31),
+881        ];
+882        for rng in &mut rngs {
+883            let id = ReservedTransportParameter::generate_reserved_id(rng);
+884            assert!(id.0 % 31 == 27)
+885        }
+886    }
+887
+888    struct StepRng(u64);
+889
+890    impl RngCore for StepRng {
+891        #[inline]
+892        fn next_u32(&mut self) -> u32 {
+893            self.next_u64() as u32
+894        }
+895
+896        #[inline]
+897        fn next_u64(&mut self) -> u64 {
+898            let res = self.0;
+899            self.0 = self.0.wrapping_add(1);
+900            res
+901        }
+902
+903        #[inline]
+904        fn fill_bytes(&mut self, dst: &mut [u8]) {
+905            let mut left = dst;
+906            while left.len() >= 8 {
+907                let (l, r) = left.split_at_mut(8);
+908                left = r;
+909                l.copy_from_slice(&self.next_u64().to_le_bytes());
+910            }
+911            let n = left.len();
+912            if n > 0 {
+913                left.copy_from_slice(&self.next_u32().to_le_bytes()[..n]);
+914            }
+915        }
+916    }
+917
+918    #[test]
+919    fn reserved_transport_parameter_ignored_when_read() {
+920        let mut buf = Vec::new();
+921        let reserved_parameter = ReservedTransportParameter::random(&mut rand::rng());
+922        assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
+923        assert!(reserved_parameter.id.0 % 31 == 27);
+924
+925        reserved_parameter.write(&mut buf);
+926        assert!(!buf.is_empty());
+927        let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
+928        assert_eq!(read_params, TransportParameters::default());
+929    }
+930
+931    #[test]
+932    fn read_semantic_validation() {
+933        #[allow(clippy::type_complexity)]
+934        let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
+935            Box::new(|t| {
+936                // This min_ack_delay is bigger than max_ack_delay!
+937                let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
+938                t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
+939            }),
+940            Box::new(|t| {
+941                // Preferred address can only be sent by senders (and we are reading the transport
+942                // params as a client)
+943                t.preferred_address = Some(PreferredAddress {
+944                    address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
+945                    address_v6: None,
+946                    connection_id: ConnectionId::new(&[]),
+947                    stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
+948                })
+949            }),
+950        ];
+951
+952        for mut builder in illegal_params_builders {
+953            let mut buf = Vec::new();
+954            let mut params = TransportParameters::default();
+955            builder(&mut params);
+956            params.write(&mut buf);
+957
+958            assert_eq!(
+959                TransportParameters::read(Side::Server, &mut buf.as_slice()),
+960                Err(Error::IllegalValue)
+961            );
+962        }
+963    }
+964
+965    #[test]
+966    fn resumption_params_validation() {
+967        let high_limit = TransportParameters {
+968            initial_max_streams_uni: 32u32.into(),
+969            ..TransportParameters::default()
+970        };
+971        let low_limit = TransportParameters {
+972            initial_max_streams_uni: 16u32.into(),
+973            ..TransportParameters::default()
+974        };
+975        high_limit.validate_resumption_from(&low_limit).unwrap();
+976        low_limit.validate_resumption_from(&high_limit).unwrap_err();
+977    }
+978}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_proto/varint.rs.html b/pr/234/docs/src/iroh_quinn_proto/varint.rs.html new file mode 100644 index 000000000..8e5894ced --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_proto/varint.rs.html @@ -0,0 +1,217 @@ +varint.rs - source

iroh_quinn_proto/
varint.rs

1use std::{convert::TryInto, fmt};
+2
+3use bytes::{Buf, BufMut};
+4use thiserror::Error;
+5
+6use crate::coding::{self, Codec, UnexpectedEnd};
+7
+8#[cfg(feature = "arbitrary")]
+9use arbitrary::Arbitrary;
+10
+11/// An integer less than 2^62
+12///
+13/// Values of this type are suitable for encoding as QUIC variable-length integer.
+14// It would be neat if we could express to Rust that the top two bits are available for use as enum
+15// discriminants
+16#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+17pub struct VarInt(pub(crate) u64);
+18
+19impl VarInt {
+20    /// The largest representable value
+21    pub const MAX: Self = Self((1 << 62) - 1);
+22    /// The largest encoded value length
+23    pub const MAX_SIZE: usize = 8;
+24
+25    /// Construct a `VarInt` infallibly
+26    pub const fn from_u32(x: u32) -> Self {
+27        Self(x as u64)
+28    }
+29
+30    /// Succeeds iff `x` < 2^62
+31    pub fn from_u64(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+32        if x < 2u64.pow(62) {
+33            Ok(Self(x))
+34        } else {
+35            Err(VarIntBoundsExceeded)
+36        }
+37    }
+38
+39    /// Create a VarInt without ensuring it's in range
+40    ///
+41    /// # Safety
+42    ///
+43    /// `x` must be less than 2^62.
+44    pub const unsafe fn from_u64_unchecked(x: u64) -> Self {
+45        Self(x)
+46    }
+47
+48    /// Extract the integer value
+49    pub const fn into_inner(self) -> u64 {
+50        self.0
+51    }
+52
+53    /// Saturating integer addition. Computes self + rhs, saturating at the numeric bounds instead
+54    /// of overflowing.
+55    pub fn saturating_add(self, rhs: impl Into<Self>) -> Self {
+56        let rhs = rhs.into();
+57        let inner = self.0.saturating_add(rhs.0).min(Self::MAX.0);
+58        Self(inner)
+59    }
+60
+61    /// Compute the number of bytes needed to encode this value
+62    pub(crate) const fn size(self) -> usize {
+63        let x = self.0;
+64        if x < 2u64.pow(6) {
+65            1
+66        } else if x < 2u64.pow(14) {
+67            2
+68        } else if x < 2u64.pow(30) {
+69            4
+70        } else if x < 2u64.pow(62) {
+71            8
+72        } else {
+73            panic!("malformed VarInt");
+74        }
+75    }
+76}
+77
+78impl From<VarInt> for u64 {
+79    fn from(x: VarInt) -> Self {
+80        x.0
+81    }
+82}
+83
+84impl From<u8> for VarInt {
+85    fn from(x: u8) -> Self {
+86        Self(x.into())
+87    }
+88}
+89
+90impl From<u16> for VarInt {
+91    fn from(x: u16) -> Self {
+92        Self(x.into())
+93    }
+94}
+95
+96impl From<u32> for VarInt {
+97    fn from(x: u32) -> Self {
+98        Self(x.into())
+99    }
+100}
+101
+102impl std::convert::TryFrom<u64> for VarInt {
+103    type Error = VarIntBoundsExceeded;
+104    /// Succeeds iff `x` < 2^62
+105    fn try_from(x: u64) -> Result<Self, VarIntBoundsExceeded> {
+106        Self::from_u64(x)
+107    }
+108}
+109
+110impl std::convert::TryFrom<u128> for VarInt {
+111    type Error = VarIntBoundsExceeded;
+112    /// Succeeds iff `x` < 2^62
+113    fn try_from(x: u128) -> Result<Self, VarIntBoundsExceeded> {
+114        Self::from_u64(x.try_into().map_err(|_| VarIntBoundsExceeded)?)
+115    }
+116}
+117
+118impl std::convert::TryFrom<usize> for VarInt {
+119    type Error = VarIntBoundsExceeded;
+120    /// Succeeds iff `x` < 2^62
+121    fn try_from(x: usize) -> Result<Self, VarIntBoundsExceeded> {
+122        Self::try_from(x as u64)
+123    }
+124}
+125
+126impl fmt::Debug for VarInt {
+127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+128        self.0.fmt(f)
+129    }
+130}
+131
+132impl fmt::Display for VarInt {
+133    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+134        self.0.fmt(f)
+135    }
+136}
+137
+138#[cfg(feature = "arbitrary")]
+139impl<'arbitrary> Arbitrary<'arbitrary> for VarInt {
+140    fn arbitrary(u: &mut arbitrary::Unstructured<'arbitrary>) -> arbitrary::Result<Self> {
+141        Ok(Self(u.int_in_range(0..=Self::MAX.0)?))
+142    }
+143}
+144
+145/// Error returned when constructing a `VarInt` from a value >= 2^62
+146#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
+147#[error("value too large for varint encoding")]
+148pub struct VarIntBoundsExceeded;
+149
+150impl Codec for VarInt {
+151    fn decode<B: Buf>(r: &mut B) -> coding::Result<Self> {
+152        if !r.has_remaining() {
+153            return Err(UnexpectedEnd);
+154        }
+155        let mut buf = [0; 8];
+156        buf[0] = r.get_u8();
+157        let tag = buf[0] >> 6;
+158        buf[0] &= 0b0011_1111;
+159        let x = match tag {
+160            0b00 => u64::from(buf[0]),
+161            0b01 => {
+162                if r.remaining() < 1 {
+163                    return Err(UnexpectedEnd);
+164                }
+165                r.copy_to_slice(&mut buf[1..2]);
+166                u64::from(u16::from_be_bytes(buf[..2].try_into().unwrap()))
+167            }
+168            0b10 => {
+169                if r.remaining() < 3 {
+170                    return Err(UnexpectedEnd);
+171                }
+172                r.copy_to_slice(&mut buf[1..4]);
+173                u64::from(u32::from_be_bytes(buf[..4].try_into().unwrap()))
+174            }
+175            0b11 => {
+176                if r.remaining() < 7 {
+177                    return Err(UnexpectedEnd);
+178                }
+179                r.copy_to_slice(&mut buf[1..8]);
+180                u64::from_be_bytes(buf)
+181            }
+182            _ => unreachable!(),
+183        };
+184        Ok(Self(x))
+185    }
+186
+187    fn encode<B: BufMut>(&self, w: &mut B) {
+188        let x = self.0;
+189        if x < 2u64.pow(6) {
+190            w.put_u8(x as u8);
+191        } else if x < 2u64.pow(14) {
+192            w.put_u16((0b01 << 14) | x as u16);
+193        } else if x < 2u64.pow(30) {
+194            w.put_u32((0b10 << 30) | x as u32);
+195        } else if x < 2u64.pow(62) {
+196            w.put_u64((0b11 << 62) | x);
+197        } else {
+198            unreachable!("malformed VarInt")
+199        }
+200    }
+201}
+202
+203#[cfg(test)]
+204mod tests {
+205    use super::*;
+206
+207    #[test]
+208    fn test_saturating_add() {
+209        // add within range behaves normally
+210        let large: VarInt = u32::MAX.into();
+211        let next = u64::from(u32::MAX) + 1;
+212        assert_eq!(large.saturating_add(1u8), VarInt::from_u64(next).unwrap());
+213
+214        // outside range saturates
+215        assert_eq!(VarInt::MAX.saturating_add(1u8), VarInt::MAX)
+216    }
+217}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_udp/cmsg/mod.rs.html b/pr/234/docs/src/iroh_quinn_udp/cmsg/mod.rs.html new file mode 100644 index 000000000..c9f5e54a5 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_udp/cmsg/mod.rs.html @@ -0,0 +1,157 @@ +mod.rs - source

iroh_quinn_udp/cmsg/
mod.rs

1use std::{
+2    ffi::{c_int, c_uchar},
+3    mem, ptr,
+4};
+5
+6#[cfg(unix)]
+7#[path = "unix.rs"]
+8mod imp;
+9
+10#[cfg(windows)]
+11#[path = "windows.rs"]
+12mod imp;
+13
+14pub(crate) use imp::Aligned;
+15
+16/// Helper to encode a series of control messages (native "cmsgs") to a buffer for use in `sendmsg`
+17//  like API.
+18///
+19/// The operation must be "finished" for the native msghdr to be usable, either by calling `finish`
+20/// explicitly or by dropping the `Encoder`.
+21pub(crate) struct Encoder<'a, M: MsgHdr> {
+22    hdr: &'a mut M,
+23    cmsg: Option<&'a mut M::ControlMessage>,
+24    len: usize,
+25}
+26
+27impl<'a, M: MsgHdr> Encoder<'a, M> {
+28    /// # Safety
+29    /// - `hdr` must contain a suitably aligned pointer to a big enough buffer to hold control messages
+30    ///   bytes. All bytes of this buffer can be safely written.
+31    /// - The `Encoder` must be dropped before `hdr` is passed to a system call, and must not be leaked.
+32    pub(crate) unsafe fn new(hdr: &'a mut M) -> Self {
+33        Self {
+34            cmsg: unsafe { hdr.cmsg_first_hdr().as_mut() },
+35            hdr,
+36            len: 0,
+37        }
+38    }
+39
+40    /// Append a control message to the buffer.
+41    ///
+42    /// # Panics
+43    /// - If insufficient buffer space remains.
+44    /// - If `T` has stricter alignment requirements than `M::ControlMessage`
+45    pub(crate) fn push<T: Copy>(&mut self, level: c_int, ty: c_int, value: T) {
+46        assert!(mem::align_of::<T>() <= mem::align_of::<M::ControlMessage>());
+47        let space = M::ControlMessage::cmsg_space(mem::size_of_val(&value));
+48        assert!(
+49            self.hdr.control_len() >= self.len + space,
+50            "control message buffer too small. Required: {}, Available: {}",
+51            self.len + space,
+52            self.hdr.control_len()
+53        );
+54        let cmsg = self.cmsg.take().expect("no control buffer space remaining");
+55        cmsg.set(
+56            level,
+57            ty,
+58            M::ControlMessage::cmsg_len(mem::size_of_val(&value)),
+59        );
+60        unsafe {
+61            ptr::write(cmsg.cmsg_data() as *const T as *mut T, value);
+62        }
+63        self.len += space;
+64        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(cmsg).as_mut() };
+65    }
+66
+67    /// Finishes appending control messages to the buffer
+68    pub(crate) fn finish(self) {
+69        // Delegates to the `Drop` impl
+70    }
+71}
+72
+73// Statically guarantees that the encoding operation is "finished" before the control buffer is read
+74// by `sendmsg` like API.
+75impl<M: MsgHdr> Drop for Encoder<'_, M> {
+76    fn drop(&mut self) {
+77        self.hdr.set_control_len(self.len as _);
+78    }
+79}
+80
+81/// # Safety
+82///
+83/// `cmsg` must refer to a native cmsg containing a payload of type `T`
+84pub(crate) unsafe fn decode<T: Copy, C: CMsgHdr>(cmsg: &impl CMsgHdr) -> T {
+85    assert!(mem::align_of::<T>() <= mem::align_of::<C>());
+86    debug_assert_eq!(cmsg.len(), C::cmsg_len(mem::size_of::<T>()));
+87    unsafe { ptr::read(cmsg.cmsg_data() as *const T) }
+88}
+89
+90pub(crate) struct Iter<'a, M: MsgHdr> {
+91    hdr: &'a M,
+92    cmsg: Option<&'a M::ControlMessage>,
+93}
+94
+95impl<'a, M: MsgHdr> Iter<'a, M> {
+96    /// # Safety
+97    ///
+98    /// `hdr` must hold a pointer to memory outliving `'a` which can be soundly read for the
+99    /// lifetime of the constructed `Iter` and contains a buffer of native cmsgs, i.e. is aligned
+100    //  for native `cmsghdr`, is fully initialized, and has correct internal links.
+101    pub(crate) unsafe fn new(hdr: &'a M) -> Self {
+102        Self {
+103            hdr,
+104            cmsg: unsafe { hdr.cmsg_first_hdr().as_ref() },
+105        }
+106    }
+107}
+108
+109impl<'a, M: MsgHdr> Iterator for Iter<'a, M> {
+110    type Item = &'a M::ControlMessage;
+111
+112    fn next(&mut self) -> Option<Self::Item> {
+113        let current = self.cmsg.take()?;
+114        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(current).as_ref() };
+115
+116        #[cfg(apple_fast)]
+117        {
+118            // On MacOS < 14 CMSG_NXTHDR might continuously return a zeroed cmsg. In
+119            // such case, return `None` instead, thus indicating the end of
+120            // the cmsghdr chain.
+121            if current.len() < mem::size_of::<M::ControlMessage>() {
+122                return None;
+123            }
+124        }
+125
+126        Some(current)
+127    }
+128}
+129
+130// Helper traits for native types for control messages
+131pub(crate) trait MsgHdr {
+132    type ControlMessage: CMsgHdr;
+133
+134    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage;
+135
+136    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;
+137
+138    /// Sets the number of control messages added to this `struct msghdr`.
+139    ///
+140    /// Note that this is a destructive operation and should only be done as a finalisation
+141    /// step.
+142    fn set_control_len(&mut self, len: usize);
+143
+144    fn control_len(&self) -> usize;
+145}
+146
+147pub(crate) trait CMsgHdr {
+148    fn cmsg_len(length: usize) -> usize;
+149
+150    fn cmsg_space(length: usize) -> usize;
+151
+152    fn cmsg_data(&self) -> *mut c_uchar;
+153
+154    fn set(&mut self, level: c_int, ty: c_int, len: usize);
+155
+156    fn len(&self) -> usize;
+157}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_udp/cmsg/unix.rs.html b/pr/234/docs/src/iroh_quinn_udp/cmsg/unix.rs.html new file mode 100644 index 000000000..337ee0895 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_udp/cmsg/unix.rs.html @@ -0,0 +1,81 @@ +unix.rs - source

iroh_quinn_udp/cmsg/
unix.rs

1use std::ffi::{c_int, c_uchar};
+2
+3use super::{CMsgHdr, MsgHdr};
+4
+5#[derive(Copy, Clone)]
+6#[repr(align(8))] // Conservative bound for align_of<libc::cmsghdr>
+7pub(crate) struct Aligned<T>(pub(crate) T);
+8
+9/// Helpers for [`libc::msghdr`]
+10impl MsgHdr for libc::msghdr {
+11    type ControlMessage = libc::cmsghdr;
+12
+13    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage {
+14        unsafe { libc::CMSG_FIRSTHDR(self) }
+15    }
+16
+17    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
+18        unsafe { libc::CMSG_NXTHDR(self, cmsg) }
+19    }
+20
+21    fn set_control_len(&mut self, len: usize) {
+22        self.msg_controllen = len as _;
+23        if len == 0 {
+24            // netbsd is particular about this being a NULL pointer if there are no control
+25            // messages.
+26            self.msg_control = std::ptr::null_mut();
+27        }
+28    }
+29
+30    fn control_len(&self) -> usize {
+31        self.msg_controllen as _
+32    }
+33}
+34
+35#[cfg(apple_fast)]
+36impl MsgHdr for crate::imp::msghdr_x {
+37    type ControlMessage = libc::cmsghdr;
+38
+39    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage {
+40        let selfp = self as *const _ as *mut libc::msghdr;
+41        unsafe { libc::CMSG_FIRSTHDR(selfp) }
+42    }
+43
+44    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage {
+45        let selfp = self as *const _ as *mut libc::msghdr;
+46        unsafe { libc::CMSG_NXTHDR(selfp, cmsg) }
+47    }
+48
+49    fn set_control_len(&mut self, len: usize) {
+50        self.msg_controllen = len as _;
+51    }
+52
+53    fn control_len(&self) -> usize {
+54        self.msg_controllen as _
+55    }
+56}
+57
+58/// Helpers for [`libc::cmsghdr`]
+59impl CMsgHdr for libc::cmsghdr {
+60    fn cmsg_len(length: usize) -> usize {
+61        unsafe { libc::CMSG_LEN(length as _) as usize }
+62    }
+63
+64    fn cmsg_space(length: usize) -> usize {
+65        unsafe { libc::CMSG_SPACE(length as _) as usize }
+66    }
+67
+68    fn cmsg_data(&self) -> *mut c_uchar {
+69        unsafe { libc::CMSG_DATA(self) }
+70    }
+71
+72    fn set(&mut self, level: c_int, ty: c_int, len: usize) {
+73        self.cmsg_level = level as _;
+74        self.cmsg_type = ty as _;
+75        self.cmsg_len = len as _;
+76    }
+77
+78    fn len(&self) -> usize {
+79        self.cmsg_len as _
+80    }
+81}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_udp/lib.rs.html b/pr/234/docs/src/iroh_quinn_udp/lib.rs.html new file mode 100644 index 000000000..d2fe73949 --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_udp/lib.rs.html @@ -0,0 +1,240 @@ +lib.rs - source

iroh_quinn_udp/
lib.rs

1//! Uniform interface to send and receive UDP packets with advanced features useful for QUIC
+2//!
+3//! This crate exposes kernel UDP stack features available on most modern systems which are required
+4//! for an efficient and conformant QUIC implementation. As of this writing, these are not available
+5//! in std or major async runtimes, and their niche character and complexity are a barrier to adding
+6//! them. Hence, a dedicated crate.
+7//!
+8//! Exposed features include:
+9//!
+10//! - Segmentation offload for bulk send and receive operations, reducing CPU load.
+11//! - Reporting the exact destination address of received packets and specifying explicit source
+12//!   addresses for sent packets, allowing responses to be sent from the address that the peer
+13//!   expects when there are multiple possibilities. This is common when bound to a wildcard address
+14//!   in IPv6 due to [RFC 8981] temporary addresses.
+15//! - [Explicit Congestion Notification], which is required by QUIC to prevent packet loss and reduce
+16//!   latency on congested links when supported by the network path.
+17//! - Disabled IP-layer fragmentation, which allows the true physical MTU to be detected and reduces
+18//!   risk of QUIC packet loss.
+19//!
+20//! Some features are unavailable in some environments. This can be due to an outdated operating
+21//! system or drivers. Some operating systems may not implement desired features at all, or may not
+22//! yet be supported by the crate. When support is unavailable, functionality will gracefully
+23//! degrade.
+24//!
+25//! [RFC 8981]: https://www.rfc-editor.org/rfc/rfc8981.html
+26//! [Explicit Congestion Notification]: https://www.rfc-editor.org/rfc/rfc3168.html
+27#![warn(unreachable_pub)]
+28#![warn(clippy::use_self)]
+29
+30use std::net::{IpAddr, Ipv6Addr, SocketAddr};
+31#[cfg(unix)]
+32use std::os::unix::io::AsFd;
+33#[cfg(windows)]
+34use std::os::windows::io::AsSocket;
+35#[cfg(not(wasm_browser))]
+36use std::{
+37    sync::Mutex,
+38    time::{Duration, Instant},
+39};
+40
+41#[cfg(any(unix, windows))]
+42mod cmsg;
+43
+44#[cfg(unix)]
+45#[path = "unix.rs"]
+46mod imp;
+47
+48#[cfg(windows)]
+49#[path = "windows.rs"]
+50mod imp;
+51
+52// No ECN support
+53#[cfg(not(any(wasm_browser, unix, windows)))]
+54#[path = "fallback.rs"]
+55mod imp;
+56
+57#[allow(unused_imports, unused_macros)]
+58mod log {
+59    #[cfg(all(feature = "log", not(feature = "tracing-log")))]
+60    pub(crate) use log::{debug, error, info, trace, warn};
+61
+62    #[cfg(feature = "tracing-log")]
+63    pub(crate) use tracing::{debug, error, info, trace, warn};
+64
+65    #[cfg(not(any(feature = "log", feature = "tracing-log")))]
+66    mod no_op {
+67        macro_rules! trace    ( ($($tt:tt)*) => {{}} );
+68        macro_rules! debug    ( ($($tt:tt)*) => {{}} );
+69        macro_rules! info     ( ($($tt:tt)*) => {{}} );
+70        macro_rules! log_warn ( ($($tt:tt)*) => {{}} );
+71        macro_rules! error    ( ($($tt:tt)*) => {{}} );
+72
+73        pub(crate) use {debug, error, info, log_warn as warn, trace};
+74    }
+75
+76    #[cfg(not(any(feature = "log", feature = "tracing-log")))]
+77    pub(crate) use no_op::*;
+78}
+79
+80#[cfg(not(wasm_browser))]
+81pub use imp::UdpSocketState;
+82
+83/// Number of UDP packets to send/receive at a time
+84#[cfg(not(wasm_browser))]
+85pub const BATCH_SIZE: usize = imp::BATCH_SIZE;
+86/// Number of UDP packets to send/receive at a time
+87#[cfg(wasm_browser)]
+88pub const BATCH_SIZE: usize = 1;
+89
+90/// Metadata for a single buffer filled with bytes received from the network
+91///
+92/// This associated buffer can contain one or more datagrams, see [`stride`].
+93///
+94/// [`stride`]: RecvMeta::stride
+95#[derive(Debug, Copy, Clone)]
+96#[non_exhaustive]
+97pub struct RecvMeta {
+98    /// The source address of the datagram(s) contained in the buffer
+99    pub addr: SocketAddr,
+100    /// The number of bytes the associated buffer has
+101    pub len: usize,
+102    /// The size of a single datagram in the associated buffer
+103    ///
+104    /// When GRO (Generic Receive Offload) is used this indicates the size of a single
+105    /// datagram inside the buffer. If the buffer is larger, that is if [`len`] is greater
+106    /// then this value, then the individual datagrams contained have their boundaries at
+107    /// `stride` increments from the start. The last datagram could be smaller than
+108    /// `stride`.
+109    ///
+110    /// [`len`]: RecvMeta::len
+111    pub stride: usize,
+112    /// The Explicit Congestion Notification bits for the datagram(s) in the buffer
+113    pub ecn: Option<EcnCodepoint>,
+114    /// The destination IP address which was encoded in this datagram
+115    ///
+116    /// Populated on platforms: Windows, Linux, Android (API level > 25),
+117    /// FreeBSD, OpenBSD, NetBSD, macOS, and iOS.
+118    pub dst_ip: Option<IpAddr>,
+119    /// The interface index of the interface on which the datagram was received
+120    pub interface_index: Option<u32>,
+121}
+122
+123impl Default for RecvMeta {
+124    /// Constructs a value with arbitrary fields, intended to be overwritten
+125    fn default() -> Self {
+126        Self {
+127            addr: SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 0),
+128            len: 0,
+129            stride: 0,
+130            ecn: None,
+131            dst_ip: None,
+132            interface_index: None,
+133        }
+134    }
+135}
+136
+137/// An outgoing packet
+138#[derive(Debug, Clone)]
+139pub struct Transmit<'a> {
+140    /// The socket this datagram should be sent to
+141    pub destination: SocketAddr,
+142    /// Explicit congestion notification bits to set on the packet
+143    pub ecn: Option<EcnCodepoint>,
+144    /// Contents of the datagram
+145    pub contents: &'a [u8],
+146    /// The segment size if this transmission contains multiple datagrams.
+147    /// This is `None` if the transmit only contains a single datagram
+148    pub segment_size: Option<usize>,
+149    /// Optional source IP address for the datagram
+150    pub src_ip: Option<IpAddr>,
+151}
+152
+153/// Log at most 1 IO error per minute
+154#[cfg(not(wasm_browser))]
+155const IO_ERROR_LOG_INTERVAL: Duration = std::time::Duration::from_secs(60);
+156
+157/// Logs a warning message when sendmsg fails
+158///
+159/// Logging will only be performed if at least [`IO_ERROR_LOG_INTERVAL`]
+160/// has elapsed since the last error was logged.
+161#[cfg(all(not(wasm_browser), any(feature = "tracing-log", feature = "log")))]
+162fn log_sendmsg_error(
+163    last_send_error: &Mutex<Instant>,
+164    err: impl core::fmt::Debug,
+165    transmit: &Transmit,
+166) {
+167    let now = Instant::now();
+168    let last_send_error = &mut *last_send_error.lock().expect("poisend lock");
+169    if now.saturating_duration_since(*last_send_error) > IO_ERROR_LOG_INTERVAL {
+170        *last_send_error = now;
+171        log::warn!(
+172            "sendmsg error: {:?}, Transmit: {{ destination: {:?}, src_ip: {:?}, ecn: {:?}, len: {:?}, segment_size: {:?} }}",
+173            err,
+174            transmit.destination,
+175            transmit.src_ip,
+176            transmit.ecn,
+177            transmit.contents.len(),
+178            transmit.segment_size
+179        );
+180    }
+181}
+182
+183// No-op
+184#[cfg(not(any(wasm_browser, feature = "tracing-log", feature = "log")))]
+185fn log_sendmsg_error(_: &Mutex<Instant>, _: impl core::fmt::Debug, _: &Transmit) {}
+186
+187/// A borrowed UDP socket
+188///
+189/// On Unix, constructible via `From<T: AsFd>`. On Windows, constructible via `From<T:
+190/// AsSocket>`.
+191// Wrapper around socket2 to avoid making it a public dependency and incurring stability risk
+192#[cfg(not(wasm_browser))]
+193pub struct UdpSockRef<'a>(socket2::SockRef<'a>);
+194
+195#[cfg(unix)]
+196impl<'s, S> From<&'s S> for UdpSockRef<'s>
+197where
+198    S: AsFd,
+199{
+200    fn from(socket: &'s S) -> Self {
+201        Self(socket.into())
+202    }
+203}
+204
+205#[cfg(windows)]
+206impl<'s, S> From<&'s S> for UdpSockRef<'s>
+207where
+208    S: AsSocket,
+209{
+210    fn from(socket: &'s S) -> Self {
+211        Self(socket.into())
+212    }
+213}
+214
+215/// Explicit congestion notification codepoint
+216#[repr(u8)]
+217#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+218pub enum EcnCodepoint {
+219    /// The ECT(0) codepoint, indicating that an endpoint is ECN-capable
+220    Ect0 = 0b10,
+221    /// The ECT(1) codepoint, indicating that an endpoint is ECN-capable
+222    Ect1 = 0b01,
+223    /// The CE codepoint, signalling that congestion was experienced
+224    Ce = 0b11,
+225}
+226
+227impl EcnCodepoint {
+228    /// Create new object from the given bits
+229    pub fn from_bits(x: u8) -> Option<Self> {
+230        use EcnCodepoint::*;
+231        Some(match x & 0b11 {
+232            0b10 => Ect0,
+233            0b01 => Ect1,
+234            0b11 => Ce,
+235            _ => {
+236                return None;
+237            }
+238        })
+239    }
+240}
\ No newline at end of file diff --git a/pr/234/docs/src/iroh_quinn_udp/unix.rs.html b/pr/234/docs/src/iroh_quinn_udp/unix.rs.html new file mode 100644 index 000000000..510d5141c --- /dev/null +++ b/pr/234/docs/src/iroh_quinn_udp/unix.rs.html @@ -0,0 +1,1095 @@ +unix.rs - source

iroh_quinn_udp/
unix.rs

1#[cfg(not(any(apple, target_os = "openbsd", solarish)))]
+2use std::ptr;
+3use std::{
+4    io::{self, IoSliceMut},
+5    mem::{self, MaybeUninit},
+6    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
+7    os::unix::io::AsRawFd,
+8    sync::{
+9        Mutex,
+10        atomic::{AtomicBool, AtomicUsize, Ordering},
+11    },
+12    time::Instant,
+13};
+14
+15use socket2::SockRef;
+16
+17use super::{
+18    EcnCodepoint, IO_ERROR_LOG_INTERVAL, RecvMeta, Transmit, UdpSockRef, cmsg, log_sendmsg_error,
+19};
+20
+21// Adapted from https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/sys/socket_private.h
+22#[cfg(apple_fast)]
+23#[repr(C)]
+24#[allow(non_camel_case_types)]
+25pub(crate) struct msghdr_x {
+26    pub msg_name: *mut libc::c_void,
+27    pub msg_namelen: libc::socklen_t,
+28    pub msg_iov: *mut libc::iovec,
+29    pub msg_iovlen: libc::c_int,
+30    pub msg_control: *mut libc::c_void,
+31    pub msg_controllen: libc::socklen_t,
+32    pub msg_flags: libc::c_int,
+33    pub msg_datalen: usize,
+34}
+35
+36#[cfg(apple_fast)]
+37unsafe extern "C" {
+38    fn recvmsg_x(
+39        s: libc::c_int,
+40        msgp: *const msghdr_x,
+41        cnt: libc::c_uint,
+42        flags: libc::c_int,
+43    ) -> isize;
+44
+45    fn sendmsg_x(
+46        s: libc::c_int,
+47        msgp: *const msghdr_x,
+48        cnt: libc::c_uint,
+49        flags: libc::c_int,
+50    ) -> isize;
+51}
+52
+53// Defined in netinet6/in6.h on OpenBSD, this is not yet exported by the libc crate
+54// directly.  See https://github.com/rust-lang/libc/issues/3704 for when we might be able to
+55// rely on this from the libc crate.
+56#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
+57const IPV6_DONTFRAG: libc::c_int = 62;
+58#[cfg(not(any(target_os = "openbsd", target_os = "netbsd")))]
+59const IPV6_DONTFRAG: libc::c_int = libc::IPV6_DONTFRAG;
+60
+61#[cfg(target_os = "freebsd")]
+62type IpTosTy = libc::c_uchar;
+63#[cfg(not(any(target_os = "freebsd", target_os = "netbsd")))]
+64type IpTosTy = libc::c_int;
+65
+66/// Tokio-compatible UDP socket with some useful specializations.
+67///
+68/// Unlike a standard tokio UDP socket, this allows ECN bits to be read and written on some
+69/// platforms.
+70#[derive(Debug)]
+71pub struct UdpSocketState {
+72    last_send_error: Mutex<Instant>,
+73    max_gso_segments: AtomicUsize,
+74    gro_segments: usize,
+75    may_fragment: bool,
+76
+77    /// True if we have received EINVAL error from `sendmsg` system call at least once.
+78    ///
+79    /// If enabled, we assume that old kernel is used and switch to fallback mode.
+80    /// In particular, we do not use IP_TOS cmsg_type in this case,
+81    /// which is not supported on Linux <3.13 and results in not sending the UDP packet at all.
+82    sendmsg_einval: AtomicBool,
+83}
+84
+85impl UdpSocketState {
+86    pub fn new(sock: UdpSockRef<'_>) -> io::Result<Self> {
+87        let io = sock.0;
+88        let mut cmsg_platform_space = 0;
+89        if cfg!(target_os = "linux")
+90            || cfg!(bsd)
+91            || cfg!(apple)
+92            || cfg!(target_os = "android")
+93            || cfg!(solarish)
+94        {
+95            cmsg_platform_space +=
+96                unsafe { libc::CMSG_SPACE(mem::size_of::<libc::in6_pktinfo>() as _) as usize };
+97        }
+98
+99        assert!(
+100            CMSG_LEN
+101                >= unsafe { libc::CMSG_SPACE(mem::size_of::<libc::c_int>() as _) as usize }
+102                    + cmsg_platform_space
+103        );
+104        assert!(
+105            mem::align_of::<libc::cmsghdr>() <= mem::align_of::<cmsg::Aligned<[u8; 0]>>(),
+106            "control message buffers will be misaligned"
+107        );
+108
+109        io.set_nonblocking(true)?;
+110
+111        let addr = io.local_addr()?;
+112        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+113
+114        // mac and ios do not support IP_RECVTOS on dual-stack sockets :(
+115        // older macos versions also don't have the flag and will error out if we don't ignore it
+116        #[cfg(not(any(
+117            target_os = "openbsd",
+118            target_os = "netbsd",
+119            target_os = "dragonfly",
+120            solarish
+121        )))]
+122        if is_ipv4 || !io.only_v6()? {
+123            if let Err(_err) =
+124                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVTOS, OPTION_ON)
+125            {
+126                crate::log::debug!("Ignoring error setting IP_RECVTOS on socket: {_err:?}");
+127            }
+128        }
+129
+130        let mut may_fragment = false;
+131        #[cfg(any(target_os = "linux", target_os = "android"))]
+132        {
+133            // opportunistically try to enable GRO. See gro::gro_segments().
+134            let _ = set_socket_option(&*io, libc::SOL_UDP, gro::UDP_GRO, OPTION_ON);
+135
+136            // Forbid IPv4 fragmentation. Set even for IPv6 to account for IPv6 mapped IPv4 addresses.
+137            // Set `may_fragment` to `true` if this option is not supported on the platform.
+138            may_fragment |= !set_socket_option_supported(
+139                &*io,
+140                libc::IPPROTO_IP,
+141                libc::IP_MTU_DISCOVER,
+142                libc::IP_PMTUDISC_PROBE,
+143            )?;
+144
+145            if is_ipv4 {
+146                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_PKTINFO, OPTION_ON)?;
+147            } else {
+148                // Set `may_fragment` to `true` if this option is not supported on the platform.
+149                may_fragment |= !set_socket_option_supported(
+150                    &*io,
+151                    libc::IPPROTO_IPV6,
+152                    libc::IPV6_MTU_DISCOVER,
+153                    libc::IPV6_PMTUDISC_PROBE,
+154                )?;
+155            }
+156        }
+157        #[cfg(any(target_os = "freebsd", apple))]
+158        {
+159            if is_ipv4 {
+160                // Set `may_fragment` to `true` if this option is not supported on the platform.
+161                may_fragment |= !set_socket_option_supported(
+162                    &*io,
+163                    libc::IPPROTO_IP,
+164                    libc::IP_DONTFRAG,
+165                    OPTION_ON,
+166                )?;
+167            }
+168        }
+169        #[cfg(any(bsd, apple, solarish))]
+170        // IP_RECVDSTADDR == IP_SENDSRCADDR on FreeBSD
+171        // macOS uses only IP_RECVDSTADDR, no IP_SENDSRCADDR on macOS (the same on Solaris)
+172        // macOS also supports IP_PKTINFO
+173        {
+174            if is_ipv4 {
+175                set_socket_option(&*io, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, OPTION_ON)?;
+176            }
+177        }
+178
+179        // Options standardized in RFC 3542
+180        if !is_ipv4 {
+181            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVPKTINFO, OPTION_ON)?;
+182            set_socket_option(&*io, libc::IPPROTO_IPV6, libc::IPV6_RECVTCLASS, OPTION_ON)?;
+183            // Linux's IP_PMTUDISC_PROBE allows us to operate under interface MTU rather than the
+184            // kernel's path MTU guess, but actually disabling fragmentation requires this too. See
+185            // __ip6_append_data in ip6_output.c.
+186            // Set `may_fragment` to `true` if this option is not supported on the platform.
+187            may_fragment |=
+188                !set_socket_option_supported(&*io, libc::IPPROTO_IPV6, IPV6_DONTFRAG, OPTION_ON)?;
+189        }
+190
+191        let now = Instant::now();
+192        Ok(Self {
+193            last_send_error: Mutex::new(now.checked_sub(2 * IO_ERROR_LOG_INTERVAL).unwrap_or(now)),
+194            max_gso_segments: AtomicUsize::new(gso::max_gso_segments()),
+195            gro_segments: gro::gro_segments(),
+196            may_fragment,
+197            sendmsg_einval: AtomicBool::new(false),
+198        })
+199    }
+200
+201    /// Sends a [`Transmit`] on the given socket.
+202    ///
+203    /// This function will only ever return errors of kind [`io::ErrorKind::WouldBlock`].
+204    /// All other errors will be logged and converted to `Ok`.
+205    ///
+206    /// UDP transmission errors are considered non-fatal because higher-level protocols must
+207    /// employ retransmits and timeouts anyway in order to deal with UDP's unreliable nature.
+208    /// Thus, logging is most likely the only thing you can do with these errors.
+209    ///
+210    /// If you would like to handle these errors yourself, use [`UdpSocketState::try_send`]
+211    /// instead.
+212    pub fn send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+213        match send(self, socket.0, transmit) {
+214            Ok(()) => Ok(()),
+215            Err(e) if e.kind() == io::ErrorKind::WouldBlock => Err(e),
+216            // - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
+217            //   these by automatically clamping the MTUD upper bound to the interface MTU.
+218            Err(e) if e.raw_os_error() == Some(libc::EMSGSIZE) => Ok(()),
+219            Err(e) => {
+220                log_sendmsg_error(&self.last_send_error, e, transmit);
+221
+222                Ok(())
+223            }
+224        }
+225    }
+226
+227    /// Sends a [`Transmit`] on the given socket without any additional error handling.
+228    pub fn try_send(&self, socket: UdpSockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+229        send(self, socket.0, transmit)
+230    }
+231
+232    pub fn recv(
+233        &self,
+234        socket: UdpSockRef<'_>,
+235        bufs: &mut [IoSliceMut<'_>],
+236        meta: &mut [RecvMeta],
+237    ) -> io::Result<usize> {
+238        recv(socket.0, bufs, meta)
+239    }
+240
+241    /// The maximum amount of segments which can be transmitted if a platform
+242    /// supports Generic Send Offload (GSO).
+243    ///
+244    /// This is 1 if the platform doesn't support GSO. Subject to change if errors are detected
+245    /// while using GSO.
+246    #[inline]
+247    pub fn max_gso_segments(&self) -> usize {
+248        self.max_gso_segments.load(Ordering::Relaxed)
+249    }
+250
+251    /// The number of segments to read when GRO is enabled. Used as a factor to
+252    /// compute the receive buffer size.
+253    ///
+254    /// Returns 1 if the platform doesn't support GRO.
+255    #[inline]
+256    pub fn gro_segments(&self) -> usize {
+257        self.gro_segments
+258    }
+259
+260    /// Resize the send buffer of `socket` to `bytes`
+261    #[inline]
+262    pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+263        socket.0.set_send_buffer_size(bytes)
+264    }
+265
+266    /// Resize the receive buffer of `socket` to `bytes`
+267    #[inline]
+268    pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> {
+269        socket.0.set_recv_buffer_size(bytes)
+270    }
+271
+272    /// Get the size of the `socket` send buffer
+273    #[inline]
+274    pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+275        socket.0.send_buffer_size()
+276    }
+277
+278    /// Get the size of the `socket` receive buffer
+279    #[inline]
+280    pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result<usize> {
+281        socket.0.recv_buffer_size()
+282    }
+283
+284    /// Whether transmitted datagrams might get fragmented by the IP layer
+285    ///
+286    /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option.
+287    #[inline]
+288    pub fn may_fragment(&self) -> bool {
+289        self.may_fragment
+290    }
+291
+292    /// Returns true if we previously got an EINVAL error from `sendmsg` syscall.
+293    fn sendmsg_einval(&self) -> bool {
+294        self.sendmsg_einval.load(Ordering::Relaxed)
+295    }
+296
+297    /// Sets the flag indicating we got EINVAL error from `sendmsg` syscall.
+298    #[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+299    fn set_sendmsg_einval(&self) {
+300        self.sendmsg_einval.store(true, Ordering::Relaxed)
+301    }
+302}
+303
+304#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd")))]
+305fn send(
+306    #[allow(unused_variables)] // only used on Linux
+307    state: &UdpSocketState,
+308    io: SockRef<'_>,
+309    transmit: &Transmit<'_>,
+310) -> io::Result<()> {
+311    #[allow(unused_mut)] // only mutable on FreeBSD
+312    let mut encode_src_ip = true;
+313    #[cfg(target_os = "freebsd")]
+314    {
+315        let addr = io.local_addr()?;
+316        let is_ipv4 = addr.family() == libc::AF_INET as libc::sa_family_t;
+317        if is_ipv4 {
+318            if let Some(socket) = addr.as_socket_ipv4() {
+319                encode_src_ip = socket.ip() == &Ipv4Addr::UNSPECIFIED;
+320            }
+321        }
+322    }
+323    let mut msg_hdr: libc::msghdr = unsafe { mem::zeroed() };
+324    let mut iovec: libc::iovec = unsafe { mem::zeroed() };
+325    let mut cmsgs = cmsg::Aligned([0u8; CMSG_LEN]);
+326    let dst_addr = socket2::SockAddr::from(transmit.destination);
+327    prepare_msg(
+328        transmit,
+329        &dst_addr,
+330        &mut msg_hdr,
+331        &mut iovec,
+332        &mut cmsgs,
+333        encode_src_ip,
+334        state.sendmsg_einval(),
+335    );
+336
+337    loop {
+338        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &msg_hdr, 0) };
+339
+340        if n >= 0 {
+341            return Ok(());
+342        }
+343
+344        let e = io::Error::last_os_error();
+345        match e.kind() {
+346            // Retry the transmission
+347            io::ErrorKind::Interrupted => continue,
+348            io::ErrorKind::WouldBlock => return Err(e),
+349            _ => {
+350                // Some network adapters and drivers do not support GSO. Unfortunately, Linux
+351                // offers no easy way for us to detect this short of an EIO or sometimes EINVAL
+352                // when we try to actually send datagrams using it.
+353                #[cfg(any(target_os = "linux", target_os = "android"))]
+354                if let Some(libc::EIO) | Some(libc::EINVAL) = e.raw_os_error() {
+355                    // Prevent new transmits from being scheduled using GSO. Existing GSO transmits
+356                    // may already be in the pipeline, so we need to tolerate additional failures.
+357                    if state.max_gso_segments() > 1 {
+358                        crate::log::info!(
+359                            "`libc::sendmsg` failed with {e}; halting segmentation offload"
+360                        );
+361                        state
+362                            .max_gso_segments
+363                            .store(1, std::sync::atomic::Ordering::Relaxed);
+364                    }
+365                }
+366
+367                // Some arguments to `sendmsg` are not supported. Switch to
+368                // fallback mode and retry if we haven't already.
+369                if e.raw_os_error() == Some(libc::EINVAL) && !state.sendmsg_einval() {
+370                    state.set_sendmsg_einval();
+371                    prepare_msg(
+372                        transmit,
+373                        &dst_addr,
+374                        &mut msg_hdr,
+375                        &mut iovec,
+376                        &mut cmsgs,
+377                        encode_src_ip,
+378                        state.sendmsg_einval(),
+379                    );
+380                    continue;
+381                }
+382
+383                return Err(e);
+384            }
+385        }
+386    }
+387}
+388
+389#[cfg(apple_fast)]
+390fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+391    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+392    let mut iovs = unsafe { mem::zeroed::<[libc::iovec; BATCH_SIZE]>() };
+393    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+394    let addr = socket2::SockAddr::from(transmit.destination);
+395    let segment_size = transmit.segment_size.unwrap_or(transmit.contents.len());
+396    let mut cnt = 0;
+397    debug_assert!(transmit.contents.len().div_ceil(segment_size) <= BATCH_SIZE);
+398    for (i, chunk) in transmit
+399        .contents
+400        .chunks(segment_size)
+401        .enumerate()
+402        .take(BATCH_SIZE)
+403    {
+404        prepare_msg(
+405            &Transmit {
+406                destination: transmit.destination,
+407                ecn: transmit.ecn,
+408                contents: chunk,
+409                segment_size: Some(chunk.len()),
+410                src_ip: transmit.src_ip,
+411            },
+412            &addr,
+413            &mut hdrs[i],
+414            &mut iovs[i],
+415            &mut ctrls[i],
+416            true,
+417            state.sendmsg_einval(),
+418        );
+419        hdrs[i].msg_datalen = chunk.len();
+420        cnt += 1;
+421    }
+422    loop {
+423        let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
+424
+425        if n >= 0 {
+426            return Ok(());
+427        }
+428
+429        let e = io::Error::last_os_error();
+430        match e.kind() {
+431            // Retry the transmission
+432            io::ErrorKind::Interrupted => continue,
+433            _ => return Err(e),
+434        }
+435    }
+436}
+437
+438#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
+439fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io::Result<()> {
+440    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };
+441    let mut iov: libc::iovec = unsafe { mem::zeroed() };
+442    let mut ctrl = cmsg::Aligned([0u8; CMSG_LEN]);
+443    let addr = socket2::SockAddr::from(transmit.destination);
+444    prepare_msg(
+445        transmit,
+446        &addr,
+447        &mut hdr,
+448        &mut iov,
+449        &mut ctrl,
+450        cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
+451        state.sendmsg_einval(),
+452    );
+453    loop {
+454        let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
+455
+456        if n >= 0 {
+457            return Ok(());
+458        }
+459
+460        let e = io::Error::last_os_error();
+461        match e.kind() {
+462            // Retry the transmission
+463            io::ErrorKind::Interrupted => continue,
+464            _ => return Err(e),
+465        }
+466    }
+467}
+468
+469#[cfg(not(any(
+470    apple,
+471    target_os = "openbsd",
+472    target_os = "netbsd",
+473    target_os = "dragonfly",
+474    solarish
+475)))]
+476fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+477    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+478    let mut ctrls = [cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit()); BATCH_SIZE];
+479    let mut hdrs = unsafe { mem::zeroed::<[libc::mmsghdr; BATCH_SIZE]>() };
+480    let max_msg_count = bufs.len().min(BATCH_SIZE);
+481    for i in 0..max_msg_count {
+482        prepare_recv(
+483            &mut bufs[i],
+484            &mut names[i],
+485            &mut ctrls[i],
+486            &mut hdrs[i].msg_hdr,
+487        );
+488    }
+489    let msg_count = loop {
+490        let n = unsafe {
+491            libc::recvmmsg(
+492                io.as_raw_fd(),
+493                hdrs.as_mut_ptr(),
+494                bufs.len().min(BATCH_SIZE) as _,
+495                0,
+496                ptr::null_mut::<libc::timespec>(),
+497            )
+498        };
+499
+500        if n >= 0 {
+501            break n;
+502        }
+503
+504        let e = io::Error::last_os_error();
+505        match e.kind() {
+506            // Retry receiving
+507            io::ErrorKind::Interrupted => continue,
+508            _ => return Err(e),
+509        }
+510    };
+511    for i in 0..(msg_count as usize) {
+512        meta[i] = decode_recv(&names[i], &hdrs[i].msg_hdr, hdrs[i].msg_len as usize)?;
+513    }
+514    Ok(msg_count as usize)
+515}
+516
+517#[cfg(apple_fast)]
+518fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+519    let mut names = [MaybeUninit::<libc::sockaddr_storage>::uninit(); BATCH_SIZE];
+520    // MacOS 10.15 `recvmsg_x` does not override the `msghdr_x`
+521    // `msg_controllen`. Thus, after the call to `recvmsg_x`, one does not know
+522    // which control messages have been written to. To prevent reading
+523    // uninitialized memory, do not use `MaybeUninit` for `ctrls`, instead
+524    // initialize `ctrls` with `0`s. A control message of all `0`s is
+525    // automatically skipped by `libc::CMSG_NXTHDR`.
+526    let mut ctrls = [cmsg::Aligned([0u8; CMSG_LEN]); BATCH_SIZE];
+527    let mut hdrs = unsafe { mem::zeroed::<[msghdr_x; BATCH_SIZE]>() };
+528    let max_msg_count = bufs.len().min(BATCH_SIZE);
+529    for i in 0..max_msg_count {
+530        prepare_recv(&mut bufs[i], &mut names[i], &mut ctrls[i], &mut hdrs[i]);
+531    }
+532    let msg_count = loop {
+533        let n = unsafe { recvmsg_x(io.as_raw_fd(), hdrs.as_mut_ptr(), max_msg_count as _, 0) };
+534
+535        if n >= 0 {
+536            break n;
+537        }
+538
+539        let e = io::Error::last_os_error();
+540        match e.kind() {
+541            // Retry receiving
+542            io::ErrorKind::Interrupted => continue,
+543            _ => return Err(e),
+544        }
+545    };
+546    for i in 0..(msg_count as usize) {
+547        meta[i] = decode_recv(&names[i], &hdrs[i], hdrs[i].msg_datalen as usize)?;
+548    }
+549    Ok(msg_count as usize)
+550}
+551
+552#[cfg(any(
+553    target_os = "openbsd",
+554    target_os = "netbsd",
+555    target_os = "dragonfly",
+556    solarish,
+557    apple_slow
+558))]
+559fn recv(io: SockRef<'_>, bufs: &mut [IoSliceMut<'_>], meta: &mut [RecvMeta]) -> io::Result<usize> {
+560    let mut name = MaybeUninit::<libc::sockaddr_storage>::uninit();
+561    let mut ctrl = cmsg::Aligned(MaybeUninit::<[u8; CMSG_LEN]>::uninit());
+562    let mut hdr = unsafe { mem::zeroed::<libc::msghdr>() };
+563    prepare_recv(&mut bufs[0], &mut name, &mut ctrl, &mut hdr);
+564    let n = loop {
+565        let n = unsafe { libc::recvmsg(io.as_raw_fd(), &mut hdr, 0) };
+566
+567        if hdr.msg_flags & libc::MSG_TRUNC != 0 {
+568            continue;
+569        }
+570
+571        if n >= 0 {
+572            break n;
+573        }
+574
+575        let e = io::Error::last_os_error();
+576        match e.kind() {
+577            // Retry receiving
+578            io::ErrorKind::Interrupted => continue,
+579            _ => return Err(e),
+580        }
+581    };
+582    meta[0] = decode_recv(&name, &hdr, n as usize)?;
+583    Ok(1)
+584}
+585
+586const CMSG_LEN: usize = 88;
+587
+588fn prepare_msg(
+589    transmit: &Transmit<'_>,
+590    dst_addr: &socket2::SockAddr,
+591    #[cfg(not(apple_fast))] hdr: &mut libc::msghdr,
+592    #[cfg(apple_fast)] hdr: &mut msghdr_x,
+593    iov: &mut libc::iovec,
+594    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+595    #[allow(unused_variables)] // only used on FreeBSD & macOS
+596    encode_src_ip: bool,
+597    sendmsg_einval: bool,
+598) {
+599    iov.iov_base = transmit.contents.as_ptr() as *const _ as *mut _;
+600    iov.iov_len = transmit.contents.len();
+601
+602    // SAFETY: Casting the pointer to a mutable one is legal,
+603    // as sendmsg is guaranteed to not alter the mutable pointer
+604    // as per the POSIX spec. See the section on the sys/socket.h
+605    // header for details. The type is only mutable in the first
+606    // place because it is reused by recvmsg as well.
+607    let name = dst_addr.as_ptr() as *mut libc::c_void;
+608    let namelen = dst_addr.len();
+609    hdr.msg_name = name as *mut _;
+610    hdr.msg_namelen = namelen;
+611    hdr.msg_iov = iov;
+612    hdr.msg_iovlen = 1;
+613
+614    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+615    hdr.msg_controllen = CMSG_LEN as _;
+616    let mut encoder = unsafe { cmsg::Encoder::new(hdr) };
+617    let ecn = transmit.ecn.map_or(0, |x| x as libc::c_int);
+618    // True for IPv4 or IPv4-Mapped IPv6
+619    let is_ipv4 = transmit.destination.is_ipv4()
+620        || matches!(transmit.destination.ip(), IpAddr::V6(addr) if addr.to_ipv4_mapped().is_some());
+621    if is_ipv4 {
+622        if !sendmsg_einval {
+623            #[cfg(not(target_os = "netbsd"))]
+624            {
+625                encoder.push(libc::IPPROTO_IP, libc::IP_TOS, ecn as IpTosTy);
+626            }
+627        }
+628    } else {
+629        encoder.push(libc::IPPROTO_IPV6, libc::IPV6_TCLASS, ecn);
+630    }
+631
+632    // Only set the segment size if it is less than the size of the contents.
+633    // Some network drivers don't like being told to do GSO even if there is effectively only a single segment (i.e. `segment_size == transmit.contents.len()`)
+634    // Additionally, a `segment_size` that is greater than the content also means there is effectively only a single segment.
+635    // This case is actually quite common when splitting up a prepared GSO batch again after GSO has been disabled because the last datagram in a GSO batch is allowed to be smaller than the segment size.
+636    if let Some(segment_size) = transmit
+637        .segment_size
+638        .filter(|segment_size| *segment_size < transmit.contents.len())
+639    {
+640        gso::set_segment_size(&mut encoder, segment_size as u16);
+641    }
+642
+643    if let Some(ip) = &transmit.src_ip {
+644        match ip {
+645            IpAddr::V4(v4) => {
+646                #[cfg(any(target_os = "linux", target_os = "android"))]
+647                {
+648                    let pktinfo = libc::in_pktinfo {
+649                        ipi_ifindex: 0,
+650                        ipi_spec_dst: libc::in_addr {
+651                            s_addr: u32::from_ne_bytes(v4.octets()),
+652                        },
+653                        ipi_addr: libc::in_addr { s_addr: 0 },
+654                    };
+655                    encoder.push(libc::IPPROTO_IP, libc::IP_PKTINFO, pktinfo);
+656                }
+657                #[cfg(any(bsd, apple, solarish))]
+658                {
+659                    if encode_src_ip {
+660                        let addr = libc::in_addr {
+661                            s_addr: u32::from_ne_bytes(v4.octets()),
+662                        };
+663                        encoder.push(libc::IPPROTO_IP, libc::IP_RECVDSTADDR, addr);
+664                    }
+665                }
+666            }
+667            IpAddr::V6(v6) => {
+668                let pktinfo = libc::in6_pktinfo {
+669                    ipi6_ifindex: 0,
+670                    ipi6_addr: libc::in6_addr {
+671                        s6_addr: v6.octets(),
+672                    },
+673                };
+674                encoder.push(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO, pktinfo);
+675            }
+676        }
+677    }
+678
+679    encoder.finish();
+680}
+681
+682#[cfg(not(apple_fast))]
+683fn prepare_recv(
+684    buf: &mut IoSliceMut,
+685    name: &mut MaybeUninit<libc::sockaddr_storage>,
+686    ctrl: &mut cmsg::Aligned<MaybeUninit<[u8; CMSG_LEN]>>,
+687    hdr: &mut libc::msghdr,
+688) {
+689    hdr.msg_name = name.as_mut_ptr() as _;
+690    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+691    hdr.msg_iov = buf as *mut IoSliceMut as *mut libc::iovec;
+692    hdr.msg_iovlen = 1;
+693    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+694    hdr.msg_controllen = CMSG_LEN as _;
+695    hdr.msg_flags = 0;
+696}
+697
+698#[cfg(apple_fast)]
+699fn prepare_recv(
+700    buf: &mut IoSliceMut,
+701    name: &mut MaybeUninit<libc::sockaddr_storage>,
+702    ctrl: &mut cmsg::Aligned<[u8; CMSG_LEN]>,
+703    hdr: &mut msghdr_x,
+704) {
+705    hdr.msg_name = name.as_mut_ptr() as _;
+706    hdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
+707    hdr.msg_iov = buf as *mut IoSliceMut as *mut libc::iovec;
+708    hdr.msg_iovlen = 1;
+709    hdr.msg_control = ctrl.0.as_mut_ptr() as _;
+710    hdr.msg_controllen = CMSG_LEN as _;
+711    hdr.msg_flags = 0;
+712    hdr.msg_datalen = buf.len();
+713}
+714
+715fn decode_recv(
+716    name: &MaybeUninit<libc::sockaddr_storage>,
+717    #[cfg(not(apple_fast))] hdr: &libc::msghdr,
+718    #[cfg(apple_fast)] hdr: &msghdr_x,
+719    len: usize,
+720) -> io::Result<RecvMeta> {
+721    let name = unsafe { name.assume_init() };
+722    let mut ecn_bits = 0;
+723    let mut dst_ip = None;
+724    let mut interface_index = None;
+725    #[allow(unused_mut)] // only mutable on Linux
+726    let mut stride = len;
+727
+728    let cmsg_iter = unsafe { cmsg::Iter::new(hdr) };
+729    for cmsg in cmsg_iter {
+730        match (cmsg.cmsg_level, cmsg.cmsg_type) {
+731            (libc::IPPROTO_IP, libc::IP_TOS) => unsafe {
+732                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+733            },
+734            // FreeBSD uses IP_RECVTOS here, and we can be liberal because cmsgs are opt-in.
+735            #[cfg(not(any(
+736                target_os = "openbsd",
+737                target_os = "netbsd",
+738                target_os = "dragonfly",
+739                solarish
+740            )))]
+741            (libc::IPPROTO_IP, libc::IP_RECVTOS) => unsafe {
+742                ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+743            },
+744            (libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => unsafe {
+745                // Temporary hack around broken macos ABI. Remove once upstream fixes it.
+746                // https://bugreport.apple.com/web/?problemID=48761855
+747                #[allow(clippy::unnecessary_cast)] // cmsg.cmsg_len defined as size_t
+748                if cfg!(apple)
+749                    && cmsg.cmsg_len as usize == libc::CMSG_LEN(mem::size_of::<u8>() as _) as usize
+750                {
+751                    ecn_bits = cmsg::decode::<u8, libc::cmsghdr>(cmsg);
+752                } else {
+753                    ecn_bits = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as u8;
+754                }
+755            },
+756            #[cfg(any(target_os = "linux", target_os = "android"))]
+757            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+758                let pktinfo = unsafe { cmsg::decode::<libc::in_pktinfo, libc::cmsghdr>(cmsg) };
+759                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(
+760                    pktinfo.ipi_addr.s_addr.to_ne_bytes(),
+761                )));
+762                interface_index = Some(pktinfo.ipi_ifindex as u32);
+763            }
+764            #[cfg(any(bsd, apple))]
+765            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+766                let in_addr = unsafe { cmsg::decode::<libc::in_addr, libc::cmsghdr>(cmsg) };
+767                dst_ip = Some(IpAddr::V4(Ipv4Addr::from(in_addr.s_addr.to_ne_bytes())));
+768            }
+769            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+770                let pktinfo = unsafe { cmsg::decode::<libc::in6_pktinfo, libc::cmsghdr>(cmsg) };
+771                dst_ip = Some(IpAddr::V6(Ipv6Addr::from(pktinfo.ipi6_addr.s6_addr)));
+772                interface_index = Some(pktinfo.ipi6_ifindex as u32);
+773            }
+774            #[cfg(any(target_os = "linux", target_os = "android"))]
+775            (libc::SOL_UDP, gro::UDP_GRO) => unsafe {
+776                stride = cmsg::decode::<libc::c_int, libc::cmsghdr>(cmsg) as usize;
+777            },
+778            _ => {}
+779        }
+780    }
+781
+782    let addr = match libc::c_int::from(name.ss_family) {
+783        libc::AF_INET => {
+784            // Safety: if the ss_family field is AF_INET then storage must be a sockaddr_in.
+785            let addr: &libc::sockaddr_in =
+786                unsafe { &*(&name as *const _ as *const libc::sockaddr_in) };
+787            SocketAddr::V4(SocketAddrV4::new(
+788                Ipv4Addr::from(addr.sin_addr.s_addr.to_ne_bytes()),
+789                u16::from_be(addr.sin_port),
+790            ))
+791        }
+792        libc::AF_INET6 => {
+793            // Safety: if the ss_family field is AF_INET6 then storage must be a sockaddr_in6.
+794            let addr: &libc::sockaddr_in6 =
+795                unsafe { &*(&name as *const _ as *const libc::sockaddr_in6) };
+796            SocketAddr::V6(SocketAddrV6::new(
+797                Ipv6Addr::from(addr.sin6_addr.s6_addr),
+798                u16::from_be(addr.sin6_port),
+799                addr.sin6_flowinfo,
+800                addr.sin6_scope_id,
+801            ))
+802        }
+803        f => {
+804            return Err(io::Error::other(format!(
+805                "expected AF_INET or AF_INET6, got {f} in decode_recv"
+806            )));
+807        }
+808    };
+809
+810    Ok(RecvMeta {
+811        len,
+812        stride,
+813        addr,
+814        ecn: EcnCodepoint::from_bits(ecn_bits),
+815        dst_ip,
+816        interface_index,
+817    })
+818}
+819
+820#[cfg(not(apple_slow))]
+821// Chosen somewhat arbitrarily; might benefit from additional tuning.
+822pub(crate) const BATCH_SIZE: usize = 32;
+823
+824#[cfg(apple_slow)]
+825pub(crate) const BATCH_SIZE: usize = 1;
+826
+827#[cfg(any(target_os = "linux", target_os = "android"))]
+828mod gso {
+829    use super::*;
+830    use std::{ffi::CStr, mem, str::FromStr, sync::OnceLock};
+831
+832    #[cfg(not(target_os = "android"))]
+833    const UDP_SEGMENT: libc::c_int = libc::UDP_SEGMENT;
+834    #[cfg(target_os = "android")]
+835    // TODO: Add this to libc
+836    const UDP_SEGMENT: libc::c_int = 103;
+837
+838    // Support for UDP GSO has been added to linux kernel in version 4.18
+839    // https://github.com/torvalds/linux/commit/cb586c63e3fc5b227c51fd8c4cb40b34d3750645
+840    const SUPPORTED_SINCE: KernelVersion = KernelVersion {
+841        version: 4,
+842        major_revision: 18,
+843    };
+844
+845    /// Checks whether GSO support is available by checking the kernel version followed by setting
+846    /// the UDP_SEGMENT option on a socket
+847    pub(crate) fn max_gso_segments() -> usize {
+848        const GSO_SIZE: libc::c_int = 1500;
+849
+850        if !SUPPORTED_BY_CURRENT_KERNEL.get_or_init(supported_by_current_kernel) {
+851            return 1;
+852        }
+853
+854        let socket = match std::net::UdpSocket::bind("[::]:0")
+855            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+856        {
+857            Ok(socket) => socket,
+858            Err(_) => return 1,
+859        };
+860
+861        // As defined in linux/udp.h
+862        // #define UDP_MAX_SEGMENTS        (1 << 6UL)
+863        match set_socket_option(&socket, libc::SOL_UDP, UDP_SEGMENT, GSO_SIZE) {
+864            Ok(()) => 64,
+865            Err(_e) => {
+866                crate::log::debug!(
+867                    "failed to set `UDP_SEGMENT` socket option ({_e}); setting `max_gso_segments = 1`"
+868                );
+869
+870                1
+871            }
+872        }
+873    }
+874
+875    pub(crate) fn set_segment_size(encoder: &mut cmsg::Encoder<libc::msghdr>, segment_size: u16) {
+876        encoder.push(libc::SOL_UDP, UDP_SEGMENT, segment_size);
+877    }
+878
+879    // Avoid calling `supported_by_current_kernel` for each socket by using `OnceLock`.
+880    static SUPPORTED_BY_CURRENT_KERNEL: OnceLock<bool> = OnceLock::new();
+881
+882    fn supported_by_current_kernel() -> bool {
+883        let kernel_version_string = match kernel_version_string() {
+884            Ok(kernel_version_string) => kernel_version_string,
+885            Err(_e) => {
+886                crate::log::warn!("GSO disabled: uname returned {_e}");
+887                return false;
+888            }
+889        };
+890
+891        let Some(kernel_version) = KernelVersion::from_str(&kernel_version_string) else {
+892            crate::log::warn!(
+893                "GSO disabled: failed to parse kernel version ({kernel_version_string})"
+894            );
+895            return false;
+896        };
+897
+898        if kernel_version < SUPPORTED_SINCE {
+899            crate::log::info!("GSO disabled: kernel too old ({kernel_version_string}); need 4.18+",);
+900            return false;
+901        }
+902
+903        true
+904    }
+905
+906    fn kernel_version_string() -> io::Result<String> {
+907        let mut n = unsafe { mem::zeroed() };
+908        let r = unsafe { libc::uname(&mut n) };
+909        if r != 0 {
+910            return Err(io::Error::last_os_error());
+911        }
+912        Ok(unsafe {
+913            CStr::from_ptr(n.release[..].as_ptr())
+914                .to_string_lossy()
+915                .into_owned()
+916        })
+917    }
+918
+919    // https://www.linfo.org/kernel_version_numbering.html
+920    #[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
+921    struct KernelVersion {
+922        version: u8,
+923        major_revision: u8,
+924    }
+925
+926    impl KernelVersion {
+927        fn from_str(release: &str) -> Option<Self> {
+928            let mut split = release
+929                .split_once('-')
+930                .map(|pair| pair.0)
+931                .unwrap_or(release)
+932                .split('.');
+933
+934            let version = u8::from_str(split.next()?).ok()?;
+935            let major_revision = u8::from_str(split.next()?).ok()?;
+936
+937            Some(Self {
+938                version,
+939                major_revision,
+940            })
+941        }
+942    }
+943
+944    #[cfg(test)]
+945    mod test {
+946        use super::*;
+947
+948        #[test]
+949        fn parse_current_kernel_version_release_string() {
+950            let release = kernel_version_string().unwrap();
+951            KernelVersion::from_str(&release).unwrap();
+952        }
+953
+954        #[test]
+955        fn parse_kernel_version_release_string() {
+956            // These are made up for the test
+957            assert_eq!(
+958                KernelVersion::from_str("4.14"),
+959                Some(KernelVersion {
+960                    version: 4,
+961                    major_revision: 14
+962                })
+963            );
+964            assert_eq!(
+965                KernelVersion::from_str("4.18"),
+966                Some(KernelVersion {
+967                    version: 4,
+968                    major_revision: 18
+969                })
+970            );
+971            // These were seen in the wild
+972            assert_eq!(
+973                KernelVersion::from_str("4.14.186-27095505"),
+974                Some(KernelVersion {
+975                    version: 4,
+976                    major_revision: 14
+977                })
+978            );
+979            assert_eq!(
+980                KernelVersion::from_str("6.8.0-59-generic"),
+981                Some(KernelVersion {
+982                    version: 6,
+983                    major_revision: 8
+984                })
+985            );
+986        }
+987    }
+988}
+989
+990// On Apple platforms using the `sendmsg_x` call, UDP datagram segmentation is not
+991// offloaded to the NIC or even the kernel, but instead done here in user space in
+992// [`send`]) and then passed to the OS as individual `iovec`s (up to `BATCH_SIZE`).
+993#[cfg(not(any(target_os = "linux", target_os = "android")))]
+994mod gso {
+995    use super::*;
+996
+997    pub(super) fn max_gso_segments() -> usize {
+998        #[cfg(apple_fast)]
+999        {
+1000            BATCH_SIZE
+1001        }
+1002        #[cfg(not(apple_fast))]
+1003        {
+1004            1
+1005        }
+1006    }
+1007
+1008    pub(super) fn set_segment_size(
+1009        #[cfg(not(apple_fast))] _encoder: &mut cmsg::Encoder<libc::msghdr>,
+1010        #[cfg(apple_fast)] _encoder: &mut cmsg::Encoder<msghdr_x>,
+1011        _segment_size: u16,
+1012    ) {
+1013    }
+1014}
+1015
+1016#[cfg(any(target_os = "linux", target_os = "android"))]
+1017mod gro {
+1018    use super::*;
+1019
+1020    #[cfg(not(target_os = "android"))]
+1021    pub(crate) const UDP_GRO: libc::c_int = libc::UDP_GRO;
+1022    #[cfg(target_os = "android")]
+1023    // TODO: Add this to libc
+1024    pub(crate) const UDP_GRO: libc::c_int = 104;
+1025
+1026    pub(crate) fn gro_segments() -> usize {
+1027        let socket = match std::net::UdpSocket::bind("[::]:0")
+1028            .or_else(|_| std::net::UdpSocket::bind((Ipv4Addr::LOCALHOST, 0)))
+1029        {
+1030            Ok(socket) => socket,
+1031            Err(_) => return 1,
+1032        };
+1033
+1034        // As defined in net/ipv4/udp_offload.c
+1035        // #define UDP_GRO_CNT_MAX 64
+1036        //
+1037        // NOTE: this MUST be set to UDP_GRO_CNT_MAX to ensure that the receive buffer size
+1038        // (get_max_udp_payload_size() * gro_segments()) is large enough to hold the largest GRO
+1039        // list the kernel might potentially produce. See
+1040        // https://github.com/quinn-rs/quinn/pull/1354.
+1041        match set_socket_option(&socket, libc::SOL_UDP, UDP_GRO, OPTION_ON) {
+1042            Ok(()) => 64,
+1043            Err(_) => 1,
+1044        }
+1045    }
+1046}
+1047
+1048/// Returns whether the given socket option is supported on the current platform
+1049///
+1050/// Yields `Ok(true)` if the option was set successfully, `Ok(false)` if setting
+1051/// the option raised an `ENOPROTOOPT` or `EOPNOTSUPP` error, and `Err` for any other error.
+1052fn set_socket_option_supported(
+1053    socket: &impl AsRawFd,
+1054    level: libc::c_int,
+1055    name: libc::c_int,
+1056    value: libc::c_int,
+1057) -> io::Result<bool> {
+1058    match set_socket_option(socket, level, name, value) {
+1059        Ok(()) => Ok(true),
+1060        Err(err) if err.raw_os_error() == Some(libc::ENOPROTOOPT) => Ok(false),
+1061        Err(err) if err.raw_os_error() == Some(libc::EOPNOTSUPP) => Ok(false),
+1062        Err(err) => Err(err),
+1063    }
+1064}
+1065
+1066fn set_socket_option(
+1067    socket: &impl AsRawFd,
+1068    level: libc::c_int,
+1069    name: libc::c_int,
+1070    value: libc::c_int,
+1071) -> io::Result<()> {
+1072    let rc = unsafe {
+1073        libc::setsockopt(
+1074            socket.as_raw_fd(),
+1075            level,
+1076            name,
+1077            &value as *const _ as _,
+1078            mem::size_of_val(&value) as _,
+1079        )
+1080    };
+1081
+1082    match rc == 0 {
+1083        true => Ok(()),
+1084        false => Err(io::Error::last_os_error()),
+1085    }
+1086}
+1087
+1088const OPTION_ON: libc::c_int = 1;
+1089
+1090#[cfg(not(any(target_os = "linux", target_os = "android")))]
+1091mod gro {
+1092    pub(super) fn gro_segments() -> usize {
+1093        1
+1094    }
+1095}
\ No newline at end of file diff --git a/pr/234/docs/src/perf/client.rs.html b/pr/234/docs/src/perf/client.rs.html new file mode 100644 index 000000000..37a94ceca --- /dev/null +++ b/pr/234/docs/src/perf/client.rs.html @@ -0,0 +1,419 @@ +client.rs - source

perf/
client.rs

1#[cfg(feature = "json-output")]
+2use std::path::PathBuf;
+3use std::{
+4    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
+5    sync::Arc,
+6    time::{Duration, Instant},
+7};
+8
+9use anyhow::{Context, Result};
+10use bytes::Bytes;
+11use clap::Parser;
+12use quinn::{TokioRuntime, crypto::rustls::QuicClientConfig};
+13use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
+14use tokio::sync::Semaphore;
+15use tracing::{debug, error, info};
+16
+17use crate::{
+18    CommonOpt, PERF_CIPHER_SUITES,
+19    noprotection::NoProtectionClientConfig,
+20    parse_byte_size,
+21    stats::{OpenStreamStats, Stats},
+22};
+23
+24/// Connects to a QUIC perf server and maintains a specified pattern of requests until interrupted
+25#[derive(Parser)]
+26#[clap(name = "client")]
+27pub struct Opt {
+28    /// Host to connect to
+29    #[clap(default_value = "localhost:4433")]
+30    host: String,
+31    /// Override DNS resolution for host
+32    #[clap(long)]
+33    ip: Option<IpAddr>,
+34    /// Specify the local socket address
+35    #[clap(long)]
+36    local_addr: Option<SocketAddr>,
+37    /// Number of unidirectional requests to maintain concurrently
+38    #[clap(long, default_value = "0")]
+39    uni_requests: u64,
+40    /// Number of bidirectional requests to maintain concurrently
+41    #[clap(long, default_value = "1")]
+42    bi_requests: u64,
+43    /// Number of bytes to request
+44    ///
+45    /// This can use SI suffixes for sizes. For example, 1M will transfer
+46    /// 1MiB, 10G will transfer 10GiB.
+47    #[clap(long, default_value = "1M", value_parser = parse_byte_size)]
+48    download_size: u64,
+49    /// Number of bytes to transmit, in addition to the request header
+50    ///
+51    /// This can use SI suffixes for sizes. For example, 1M will transfer
+52    /// 1MiB, 10G will transfer 10GiB.
+53    #[clap(long, default_value = "1M", value_parser = parse_byte_size)]
+54    upload_size: u64,
+55    /// The time to run in seconds
+56    #[clap(long, default_value = "60")]
+57    duration: u64,
+58    /// The interval in seconds at which stats are reported
+59    #[clap(long, default_value = "1")]
+60    interval: u64,
+61    /// File path to output JSON statistics to. If the file is '-', stdout will be used
+62    #[cfg(feature = "json-output")]
+63    #[clap(long)]
+64    json: Option<PathBuf>,
+65    /// Common options
+66    #[command(flatten)]
+67    common: CommonOpt,
+68}
+69
+70pub async fn run(opt: Opt) -> Result<()> {
+71    let mut host_parts = opt.host.split(':');
+72    let host_name = host_parts.next().unwrap();
+73    let host_port = host_parts
+74        .next()
+75        .map_or(Ok(443), |x| x.parse())
+76        .context("parsing port")?;
+77    let addr = match opt.ip {
+78        None => tokio::net::lookup_host(&opt.host)
+79            .await
+80            .context("resolving host")?
+81            .next()
+82            .unwrap(),
+83        Some(ip) => SocketAddr::new(ip, host_port),
+84    };
+85
+86    info!("connecting to {} at {}", host_name, addr);
+87
+88    let bind_addr = opt.local_addr.unwrap_or_else(|| {
+89        let unspec = if addr.is_ipv4() {
+90            Ipv4Addr::UNSPECIFIED.into()
+91        } else {
+92            Ipv6Addr::UNSPECIFIED.into()
+93        };
+94        SocketAddr::new(unspec, 0)
+95    });
+96
+97    info!("local addr {:?}", bind_addr);
+98
+99    let socket = opt.common.bind_socket(bind_addr)?;
+100
+101    let mut endpoint_cfg = quinn::EndpointConfig::default();
+102    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+103
+104    let endpoint = quinn::Endpoint::new(endpoint_cfg, None, socket, Arc::new(TokioRuntime))?;
+105
+106    let default_provider = rustls::crypto::ring::default_provider();
+107    let provider = Arc::new(rustls::crypto::CryptoProvider {
+108        cipher_suites: PERF_CIPHER_SUITES.into(),
+109        ..default_provider
+110    });
+111
+112    let mut crypto = rustls::ClientConfig::builder_with_provider(provider.clone())
+113        .with_protocol_versions(&[&rustls::version::TLS13])
+114        .unwrap()
+115        .dangerous()
+116        .with_custom_certificate_verifier(SkipServerVerification::new(provider))
+117        .with_no_client_auth();
+118    crypto.alpn_protocols = vec![b"perf".to_vec()];
+119
+120    if opt.common.keylog {
+121        crypto.key_log = Arc::new(rustls::KeyLogFile::new());
+122    }
+123
+124    let transport = opt.common.build_transport_config(
+125        #[cfg(feature = "qlog")]
+126        "perf-client",
+127    )?;
+128
+129    let crypto = Arc::new(QuicClientConfig::try_from(crypto)?);
+130    let mut config = quinn::ClientConfig::new(match opt.common.no_protection {
+131        true => Arc::new(NoProtectionClientConfig::new(crypto)),
+132        false => crypto,
+133    });
+134    config.transport_config(Arc::new(transport));
+135
+136    let stream_stats = OpenStreamStats::default();
+137
+138    let connection = endpoint
+139        .connect_with(config, addr, host_name)?
+140        .await
+141        .context("connecting")?;
+142
+143    info!("established");
+144
+145    let drive_fut = async {
+146        tokio::try_join!(
+147            drive_uni(
+148                connection.clone(),
+149                stream_stats.clone(),
+150                opt.uni_requests,
+151                opt.upload_size,
+152                opt.download_size
+153            ),
+154            drive_bi(
+155                connection.clone(),
+156                stream_stats.clone(),
+157                opt.bi_requests,
+158                opt.upload_size,
+159                opt.download_size
+160            )
+161        )
+162    };
+163
+164    let mut stats = Stats::default();
+165
+166    let stats_fut = async {
+167        let interval_duration = Duration::from_secs(opt.interval);
+168
+169        loop {
+170            let start = Instant::now();
+171            tokio::time::sleep(interval_duration).await;
+172            {
+173                stats.on_interval(start, &stream_stats);
+174
+175                stats.print();
+176                if opt.common.conn_stats {
+177                    println!("{:?}\n", connection.stats());
+178                }
+179            }
+180        }
+181    };
+182
+183    tokio::select! {
+184        _ = drive_fut => {}
+185        _ = stats_fut => {}
+186        _ = tokio::signal::ctrl_c() => {
+187            info!("shutting down");
+188            connection.close(0u32.into(), b"interrupted");
+189        }
+190        // Add a small duration so the final interval can be reported
+191        _ = tokio::time::sleep(Duration::from_secs(opt.duration) + Duration::from_millis(200)) => {
+192            info!("shutting down");
+193            connection.close(0u32.into(), b"done");
+194        }
+195    }
+196
+197    endpoint.wait_idle().await;
+198
+199    #[cfg(feature = "json-output")]
+200    if let Some(path) = opt.json {
+201        stats.print_json(path.as_path())?;
+202    }
+203
+204    Ok(())
+205}
+206
+207async fn drain_stream(
+208    mut stream: quinn::RecvStream,
+209    download: u64,
+210    stream_stats: OpenStreamStats,
+211) -> Result<()> {
+212    if download == 0 {
+213        return Ok(());
+214    }
+215
+216    #[rustfmt::skip]
+217    let mut bufs = [
+218        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+219        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+220        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+221        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+222        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+223        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+224        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+225        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+226    ];
+227    let download_start = Instant::now();
+228    let recv_stream_stats = stream_stats.new_receiver(&stream, download);
+229
+230    let mut first_byte = true;
+231
+232    while let Some(size) = stream.read_chunks(&mut bufs[..]).await? {
+233        if first_byte {
+234            recv_stream_stats.on_first_byte(download_start.elapsed());
+235            first_byte = false;
+236        }
+237        let bytes_received = bufs[..size].iter().map(|b| b.len()).sum();
+238        recv_stream_stats.on_bytes(bytes_received);
+239    }
+240
+241    if first_byte {
+242        recv_stream_stats.on_first_byte(download_start.elapsed());
+243    }
+244    recv_stream_stats.finish(download_start.elapsed());
+245
+246    debug!("response finished on {}", stream.id());
+247    Ok(())
+248}
+249
+250async fn drive_uni(
+251    connection: quinn::Connection,
+252    stream_stats: OpenStreamStats,
+253    concurrency: u64,
+254    upload: u64,
+255    download: u64,
+256) -> Result<()> {
+257    if concurrency == 0 {
+258        return Ok(());
+259    }
+260
+261    let sem = Arc::new(Semaphore::new(concurrency as usize));
+262
+263    loop {
+264        let permit = sem.clone().acquire_owned().await.unwrap();
+265        let send = connection.open_uni().await?;
+266        let stream_stats = stream_stats.clone();
+267
+268        debug!("sending request on {}", send.id());
+269        let connection = connection.clone();
+270        tokio::spawn(async move {
+271            if let Err(e) = request_uni(send, connection, upload, download, stream_stats).await {
+272                error!("sending request failed: {:#}", e);
+273            }
+274
+275            drop(permit);
+276        });
+277    }
+278}
+279
+280async fn request_uni(
+281    send: quinn::SendStream,
+282    conn: quinn::Connection,
+283    upload: u64,
+284    download: u64,
+285    stream_stats: OpenStreamStats,
+286) -> Result<()> {
+287    request(send, upload, download, stream_stats.clone()).await?;
+288    let recv = conn.accept_uni().await?;
+289    drain_stream(recv, download, stream_stats).await?;
+290    Ok(())
+291}
+292
+293async fn request(
+294    mut send: quinn::SendStream,
+295    mut upload: u64,
+296    download: u64,
+297    stream_stats: OpenStreamStats,
+298) -> Result<()> {
+299    let upload_start = Instant::now();
+300    send.write_all(&download.to_be_bytes()).await?;
+301    if upload == 0 {
+302        send.finish().unwrap();
+303        return Ok(());
+304    }
+305
+306    let send_stream_stats = stream_stats.new_sender(&send, upload);
+307
+308    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+309    while upload > 0 {
+310        let chunk_len = upload.min(DATA.len() as u64);
+311        send.write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+312            .await
+313            .context("sending response")?;
+314        send_stream_stats.on_bytes(chunk_len as usize);
+315        upload -= chunk_len;
+316    }
+317    send.finish().unwrap();
+318    // Wait for stream to close
+319    _ = send.stopped().await;
+320    send_stream_stats.finish(upload_start.elapsed());
+321
+322    debug!("upload finished on {}", send.id());
+323    Ok(())
+324}
+325
+326async fn drive_bi(
+327    connection: quinn::Connection,
+328    stream_stats: OpenStreamStats,
+329    concurrency: u64,
+330    upload: u64,
+331    download: u64,
+332) -> Result<()> {
+333    if concurrency == 0 {
+334        return Ok(());
+335    }
+336
+337    let sem = Arc::new(Semaphore::new(concurrency as usize));
+338
+339    loop {
+340        let permit = sem.clone().acquire_owned().await.unwrap();
+341        let (send, recv) = connection.open_bi().await?;
+342        let stream_stats = stream_stats.clone();
+343
+344        debug!("sending request on {}", send.id());
+345        tokio::spawn(async move {
+346            if let Err(e) = request_bi(send, recv, upload, download, stream_stats).await {
+347                error!("request failed: {:#}", e);
+348            }
+349
+350            drop(permit);
+351        });
+352    }
+353}
+354
+355async fn request_bi(
+356    send: quinn::SendStream,
+357    recv: quinn::RecvStream,
+358    upload: u64,
+359    download: u64,
+360    stream_stats: OpenStreamStats,
+361) -> Result<()> {
+362    request(send, upload, download, stream_stats.clone()).await?;
+363    drain_stream(recv, download, stream_stats).await?;
+364    Ok(())
+365}
+366
+367#[derive(Debug)]
+368struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
+369
+370impl SkipServerVerification {
+371    fn new(provider: Arc<rustls::crypto::CryptoProvider>) -> Arc<Self> {
+372        Arc::new(Self(provider))
+373    }
+374}
+375
+376impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
+377    fn verify_server_cert(
+378        &self,
+379        _end_entity: &CertificateDer<'_>,
+380        _intermediates: &[CertificateDer<'_>],
+381        _server_name: &ServerName<'_>,
+382        _ocsp: &[u8],
+383        _now: UnixTime,
+384    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
+385        Ok(rustls::client::danger::ServerCertVerified::assertion())
+386    }
+387
+388    fn verify_tls12_signature(
+389        &self,
+390        message: &[u8],
+391        cert: &CertificateDer<'_>,
+392        dss: &rustls::DigitallySignedStruct,
+393    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+394        rustls::crypto::verify_tls12_signature(
+395            message,
+396            cert,
+397            dss,
+398            &self.0.signature_verification_algorithms,
+399        )
+400    }
+401
+402    fn verify_tls13_signature(
+403        &self,
+404        message: &[u8],
+405        cert: &CertificateDer<'_>,
+406        dss: &rustls::DigitallySignedStruct,
+407    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
+408        rustls::crypto::verify_tls13_signature(
+409            message,
+410            cert,
+411            dss,
+412            &self.0.signature_verification_algorithms,
+413        )
+414    }
+415
+416    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
+417        self.0.signature_verification_algorithms.supported_schemes()
+418    }
+419}
\ No newline at end of file diff --git a/pr/234/docs/src/perf/lib.rs.html b/pr/234/docs/src/perf/lib.rs.html new file mode 100644 index 000000000..4c2c22ac8 --- /dev/null +++ b/pr/234/docs/src/perf/lib.rs.html @@ -0,0 +1,219 @@ +lib.rs - source

perf/
lib.rs

1#[cfg(feature = "qlog")]
+2use std::path::PathBuf;
+3use std::{io, net::SocketAddr, num::ParseIntError, str::FromStr, sync::Arc, time::Duration};
+4
+5use anyhow::{Context, Result};
+6use clap::{Parser, ValueEnum};
+7use quinn::{
+8    AckFrequencyConfig, TransportConfig, VarInt,
+9    congestion::{self, ControllerFactory},
+10    udp::UdpSocketState,
+11};
+12use rustls::crypto::ring::cipher_suite;
+13use socket2::{Domain, Protocol, Socket, Type};
+14use tracing::warn;
+15
+16#[cfg_attr(not(feature = "json-output"), allow(dead_code))]
+17pub mod stats;
+18
+19pub mod noprotection;
+20
+21pub mod client;
+22pub mod server;
+23
+24// Common options between client and server binary
+25#[derive(Parser)]
+26pub struct CommonOpt {
+27    /// Send buffer size in bytes
+28    ///
+29    /// This can use SI suffixes for sizes. For example, 1M will request
+30    /// 1MiB, 10G will request 10GiB.
+31    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
+32    pub send_buffer_size: u64,
+33    /// Receive buffer size in bytes
+34    ///
+35    /// This can use SI suffixes for sizes. For example, 1M will request
+36    /// 1MiB, 10G will request 10GiB.
+37    #[clap(long, default_value = "2M", value_parser = parse_byte_size)]
+38    pub recv_buffer_size: u64,
+39    /// Whether to print connection statistics
+40    #[clap(long)]
+41    pub conn_stats: bool,
+42    /// Perform NSS-compatible TLS key logging to the file specified in `SSLKEYLOGFILE`.
+43    #[clap(long = "keylog")]
+44    pub keylog: bool,
+45    /// UDP payload size that the network must be capable of carrying
+46    #[clap(long, default_value = "1200")]
+47    pub initial_mtu: u16,
+48    /// Disable packet encryption/decryption (for debugging purpose)
+49    #[clap(long = "no-protection")]
+50    pub no_protection: bool,
+51    /// The initial round-trip-time (in msecs)
+52    #[clap(long, group = "common")]
+53    pub initial_rtt: Option<u64>,
+54    /// Ack Frequency mode
+55    #[clap(long = "ack-frequency")]
+56    pub ack_frequency: bool,
+57    /// Congestion algorithm to use
+58    #[clap(long = "congestion")]
+59    pub cong_alg: Option<CongestionAlgorithm>,
+60    /// Maximum number of bytes the peer may transmit without acknowledgement on any one stream
+61    /// before becoming blocked.
+62    ///
+63    /// This can use SI suffixes for sizes. For example, 1M will limit to
+64    /// 1MiB, 10G will limit to 10GiB.
+65    #[clap(long, value_parser = parse_byte_size)]
+66    pub stream_receive_window: Option<u64>,
+67    /// Maximum number of bytes the peer may transmit across all streams of a connection before
+68    /// becoming blocked.
+69    ///
+70    /// This can use SI suffixes for sizes. For example, 1M will limit to
+71    /// 1MiB, 10G will limit to 10GiB.
+72    #[clap(long, value_parser = parse_byte_size)]
+73    pub receive_window: Option<u64>,
+74    /// Maximum number of bytes to transmit to a peer without acknowledgment
+75    ///
+76    /// This can use SI suffixes for sizes. For example, 1M will limit to
+77    /// 1MiB, 10G will limit to 10GiB.
+78    #[clap(long, value_parser = parse_byte_size)]
+79    pub send_window: Option<u64>,
+80    /// Max UDP payload size in bytes
+81    #[clap(long, default_value = "1472")]
+82    pub max_udp_payload_size: u16,
+83    /// qlog output directory
+84    ///
+85    /// Alternatively you can set the `QLOGDIR` environment variable.
+86    #[cfg(feature = "qlog")]
+87    #[clap(long = "qlog")]
+88    pub qlog_dir: Option<PathBuf>,
+89}
+90
+91impl CommonOpt {
+92    pub fn build_transport_config(
+93        &self,
+94        #[cfg(feature = "qlog")] name: &str,
+95    ) -> io::Result<TransportConfig> {
+96        let mut transport = TransportConfig::default();
+97        transport.initial_mtu(self.initial_mtu);
+98
+99        if let Some(initial_rtt) = self.initial_rtt {
+100            transport.initial_rtt(Duration::from_millis(initial_rtt));
+101        }
+102
+103        if self.ack_frequency {
+104            transport.ack_frequency_config(Some(AckFrequencyConfig::default()));
+105        }
+106
+107        if let Some(cong_alg) = self.cong_alg {
+108            transport.congestion_controller_factory(cong_alg.build());
+109        }
+110
+111        if let Some(stream_receive_window) = self.stream_receive_window {
+112            transport.stream_receive_window(
+113                VarInt::from_u64(stream_receive_window).unwrap_or(VarInt::MAX),
+114            );
+115        }
+116
+117        if let Some(receive_window) = self.receive_window {
+118            transport.receive_window(VarInt::from_u64(receive_window).unwrap_or(VarInt::MAX));
+119        }
+120
+121        if let Some(send_window) = self.send_window {
+122            transport.send_window(send_window);
+123        }
+124
+125        #[cfg(feature = "qlog")]
+126        if let Some(qlog_dir) = &self.qlog_dir {
+127            transport.qlog_from_path(qlog_dir, name);
+128        } else {
+129            transport.qlog_from_env(name);
+130        }
+131
+132        Ok(transport)
+133    }
+134
+135    pub fn bind_socket(&self, addr: SocketAddr) -> Result<std::net::UdpSocket> {
+136        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
+137            .context("create socket")?;
+138
+139        if addr.is_ipv6() {
+140            socket.set_only_v6(false).context("set_only_v6")?;
+141        }
+142
+143        socket
+144            .bind(&socket2::SockAddr::from(addr))
+145            .context("binding endpoint")?;
+146
+147        let socket_state = UdpSocketState::new((&socket).into())?;
+148        socket_state
+149            .set_send_buffer_size((&socket).into(), self.send_buffer_size as usize)
+150            .context("send buffer size")?;
+151        socket_state
+152            .set_recv_buffer_size((&socket).into(), self.recv_buffer_size as usize)
+153            .context("recv buffer size")?;
+154
+155        let buf_size = socket_state
+156            .send_buffer_size((&socket).into())
+157            .context("send buffer size")?;
+158        if buf_size < self.send_buffer_size as usize {
+159            warn!(
+160                "Unable to set desired send buffer size. Desired: {}, Actual: {}",
+161                self.send_buffer_size, buf_size
+162            );
+163        }
+164
+165        let buf_size = socket_state
+166            .recv_buffer_size((&socket).into())
+167            .context("recv buffer size")?;
+168        if buf_size < self.recv_buffer_size as usize {
+169            warn!(
+170                "Unable to set desired recv buffer size. Desired: {}, Actual: {}",
+171                self.recv_buffer_size, buf_size
+172            );
+173        }
+174
+175        Ok(socket.into())
+176    }
+177}
+178
+179pub fn parse_byte_size(s: &str) -> Result<u64, ParseIntError> {
+180    let s = s.trim();
+181
+182    let multiplier = match s.chars().last() {
+183        Some('T') => 1024 * 1024 * 1024 * 1024,
+184        Some('G') => 1024 * 1024 * 1024,
+185        Some('M') => 1024 * 1024,
+186        Some('k') => 1024,
+187        _ => 1,
+188    };
+189
+190    let s = match multiplier {
+191        1 => s,
+192        _ => &s[..s.len() - 1],
+193    };
+194
+195    Ok(u64::from_str(s)? * multiplier)
+196}
+197
+198#[derive(Clone, Copy, ValueEnum)]
+199pub enum CongestionAlgorithm {
+200    Cubic,
+201    Bbr,
+202    NewReno,
+203}
+204
+205impl CongestionAlgorithm {
+206    pub fn build(self) -> Arc<dyn ControllerFactory + Send + Sync + 'static> {
+207        match self {
+208            CongestionAlgorithm::Cubic => Arc::new(congestion::CubicConfig::default()),
+209            CongestionAlgorithm::Bbr => Arc::new(congestion::BbrConfig::default()),
+210            CongestionAlgorithm::NewReno => Arc::new(congestion::NewRenoConfig::default()),
+211        }
+212    }
+213}
+214
+215pub static PERF_CIPHER_SUITES: &[rustls::SupportedCipherSuite] = &[
+216    cipher_suite::TLS13_AES_128_GCM_SHA256,
+217    cipher_suite::TLS13_AES_256_GCM_SHA384,
+218    cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
+219];
\ No newline at end of file diff --git a/pr/234/docs/src/perf/noprotection.rs.html b/pr/234/docs/src/perf/noprotection.rs.html new file mode 100644 index 000000000..a3028bc01 --- /dev/null +++ b/pr/234/docs/src/perf/noprotection.rs.html @@ -0,0 +1,205 @@ +noprotection.rs - source

perf/
noprotection.rs

1use std::sync::Arc;
+2
+3use bytes::BytesMut;
+4
+5use quinn_proto::{
+6    ConnectionId, PathId, Side, TransportError,
+7    crypto::{
+8        self, CryptoError,
+9        rustls::{QuicClientConfig, QuicServerConfig},
+10    },
+11    transport_parameters,
+12};
+13
+14/// A rustls TLS session which does not perform packet encryption/decryption (for debugging purpose)
+15struct NoProtectionSession {
+16    inner: Box<dyn crypto::Session>,
+17}
+18
+19impl NoProtectionSession {
+20    fn new(tls: Box<dyn crypto::Session>) -> Self {
+21        Self { inner: tls }
+22    }
+23
+24    /// Wraps the provided keys in `NoProtectionPacketKey` to disable packet encryption / decryption
+25    fn wrap_packet_keys(
+26        keys: crypto::KeyPair<Box<dyn crypto::PacketKey>>,
+27    ) -> crypto::KeyPair<Box<dyn crypto::PacketKey>> {
+28        crypto::KeyPair {
+29            local: Box::new(NoProtectionPacketKey::new(keys.local)),
+30            remote: Box::new(NoProtectionPacketKey::new(keys.remote)),
+31        }
+32    }
+33}
+34
+35struct NoProtectionPacketKey {
+36    inner: Box<dyn crypto::PacketKey>,
+37}
+38
+39impl NoProtectionPacketKey {
+40    fn new(key: Box<dyn crypto::PacketKey>) -> Self {
+41        Self { inner: key }
+42    }
+43}
+44
+45pub struct NoProtectionClientConfig {
+46    inner: Arc<QuicClientConfig>,
+47}
+48
+49impl NoProtectionClientConfig {
+50    pub fn new(config: Arc<QuicClientConfig>) -> Self {
+51        Self { inner: config }
+52    }
+53}
+54
+55pub struct NoProtectionServerConfig {
+56    inner: Arc<QuicServerConfig>,
+57}
+58
+59impl NoProtectionServerConfig {
+60    pub fn new(config: Arc<QuicServerConfig>) -> Self {
+61        Self { inner: config }
+62    }
+63}
+64
+65// forward all calls to inner except those related to packet encryption/decryption
+66impl crypto::Session for NoProtectionSession {
+67    fn initial_keys(&self, dst_cid: ConnectionId, side: Side) -> crypto::Keys {
+68        self.inner.initial_keys(dst_cid, side)
+69    }
+70
+71    fn handshake_data(&self) -> Option<Box<dyn std::any::Any>> {
+72        self.inner.handshake_data()
+73    }
+74
+75    fn peer_identity(&self) -> Option<Box<dyn std::any::Any>> {
+76        self.inner.peer_identity()
+77    }
+78
+79    fn early_crypto(&self) -> Option<(Box<dyn crypto::HeaderKey>, Box<dyn crypto::PacketKey>)> {
+80        let (hkey, pkey) = self.inner.early_crypto()?;
+81
+82        // use wrapper type to disable packet encryption/decryption
+83        Some((hkey, Box::new(NoProtectionPacketKey::new(pkey))))
+84    }
+85
+86    fn early_data_accepted(&self) -> Option<bool> {
+87        self.inner.early_data_accepted()
+88    }
+89
+90    fn is_handshaking(&self) -> bool {
+91        self.inner.is_handshaking()
+92    }
+93
+94    fn read_handshake(&mut self, buf: &[u8]) -> Result<bool, TransportError> {
+95        self.inner.read_handshake(buf)
+96    }
+97
+98    fn transport_parameters(
+99        &self,
+100    ) -> Result<Option<transport_parameters::TransportParameters>, TransportError> {
+101        self.inner.transport_parameters()
+102    }
+103
+104    fn write_handshake(&mut self, buf: &mut Vec<u8>) -> Option<crypto::Keys> {
+105        let keys = self.inner.write_handshake(buf)?;
+106
+107        Some(crypto::Keys {
+108            header: keys.header,
+109            packet: Self::wrap_packet_keys(keys.packet),
+110        })
+111    }
+112
+113    fn next_1rtt_keys(&mut self) -> Option<crypto::KeyPair<Box<dyn crypto::PacketKey>>> {
+114        let keys = self.inner.next_1rtt_keys()?;
+115        Some(Self::wrap_packet_keys(keys))
+116    }
+117
+118    fn is_valid_retry(&self, orig_dst_cid: ConnectionId, header: &[u8], payload: &[u8]) -> bool {
+119        self.inner.is_valid_retry(orig_dst_cid, header, payload)
+120    }
+121
+122    fn export_keying_material(
+123        &self,
+124        output: &mut [u8],
+125        label: &[u8],
+126        context: &[u8],
+127    ) -> Result<(), crypto::ExportKeyingMaterialError> {
+128        self.inner.export_keying_material(output, label, context)
+129    }
+130}
+131
+132impl crypto::ClientConfig for NoProtectionClientConfig {
+133    fn start_session(
+134        self: std::sync::Arc<Self>,
+135        version: u32,
+136        server_name: &str,
+137        params: &transport_parameters::TransportParameters,
+138    ) -> Result<Box<dyn crypto::Session>, quinn::ConnectError> {
+139        let tls = self
+140            .inner
+141            .clone()
+142            .start_session(version, server_name, params)?;
+143
+144        Ok(Box::new(NoProtectionSession::new(tls)))
+145    }
+146}
+147
+148impl crypto::ServerConfig for NoProtectionServerConfig {
+149    fn initial_keys(
+150        &self,
+151        version: u32,
+152        dst_cid: ConnectionId,
+153    ) -> Result<crypto::Keys, crypto::UnsupportedVersion> {
+154        self.inner.initial_keys(version, dst_cid)
+155    }
+156
+157    fn retry_tag(&self, version: u32, orig_dst_cid: ConnectionId, packet: &[u8]) -> [u8; 16] {
+158        self.inner.retry_tag(version, orig_dst_cid, packet)
+159    }
+160
+161    fn start_session(
+162        self: Arc<Self>,
+163        version: u32,
+164        params: &transport_parameters::TransportParameters,
+165    ) -> Box<dyn crypto::Session> {
+166        let tls = self.inner.clone().start_session(version, params);
+167
+168        Box::new(NoProtectionSession::new(tls))
+169    }
+170}
+171
+172// forward all calls to inner except those related to packet encryption/decryption
+173impl crypto::PacketKey for NoProtectionPacketKey {
+174    fn encrypt(&self, _path_id: PathId, _packet: u64, buf: &mut [u8], header_len: usize) {
+175        let (_header, payload_tag) = buf.split_at_mut(header_len);
+176        let (_payload, tag_storage) =
+177            payload_tag.split_at_mut(payload_tag.len() - self.inner.tag_len());
+178        // packet = identity(packet)
+179        tag_storage.fill(42);
+180    }
+181
+182    fn decrypt(
+183        &self,
+184        _path_id: PathId,
+185        _packet: u64,
+186        _header: &[u8],
+187        payload: &mut BytesMut,
+188    ) -> Result<(), CryptoError> {
+189        let plain_len = payload.len() - self.inner.tag_len();
+190        payload.truncate(plain_len);
+191        Ok(())
+192    }
+193
+194    fn tag_len(&self) -> usize {
+195        self.inner.tag_len()
+196    }
+197
+198    fn confidentiality_limit(&self) -> u64 {
+199        self.inner.confidentiality_limit()
+200    }
+201
+202    fn integrity_limit(&self) -> u64 {
+203        self.inner.integrity_limit()
+204    }
+205}
\ No newline at end of file diff --git a/pr/234/docs/src/perf/server.rs.html b/pr/234/docs/src/perf/server.rs.html new file mode 100644 index 000000000..e1b75cd59 --- /dev/null +++ b/pr/234/docs/src/perf/server.rs.html @@ -0,0 +1,202 @@ +server.rs - source

perf/
server.rs

1use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
+2
+3use anyhow::{Context, Result};
+4use bytes::Bytes;
+5use clap::Parser;
+6use quinn::{TokioRuntime, crypto::rustls::QuicServerConfig};
+7use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, pem::PemObject};
+8use tracing::{debug, error, info};
+9
+10use crate::{CommonOpt, PERF_CIPHER_SUITES, noprotection::NoProtectionServerConfig};
+11
+12#[derive(Parser)]
+13#[clap(name = "server")]
+14pub struct Opt {
+15    /// Address to listen on
+16    #[clap(long = "listen", default_value = "[::]:4433")]
+17    listen: SocketAddr,
+18    /// TLS private key in DER format
+19    #[clap(short = 'k', long = "key", requires = "cert")]
+20    key: Option<PathBuf>,
+21    /// TLS certificate in PEM format
+22    #[clap(short = 'c', long = "cert", requires = "key")]
+23    cert: Option<PathBuf>,
+24    /// Common options
+25    #[command(flatten)]
+26    common: CommonOpt,
+27}
+28
+29pub async fn run(opt: Opt) -> Result<()> {
+30    let (key, cert) = match (&opt.key, &opt.cert) {
+31        (Some(key), Some(cert)) => (
+32            PrivateKeyDer::from_pem_file(key).context("reading private key")?,
+33            CertificateDer::pem_file_iter(cert)
+34                .context("reading certificate chain file")?
+35                .collect::<Result<_, _>>()
+36                .context("reading certificate chain")?,
+37        ),
+38        _ => {
+39            let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
+40            (
+41                PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()).into(),
+42                vec![CertificateDer::from(cert.cert)],
+43            )
+44        }
+45    };
+46
+47    let default_provider = rustls::crypto::ring::default_provider();
+48    let provider = rustls::crypto::CryptoProvider {
+49        cipher_suites: PERF_CIPHER_SUITES.into(),
+50        ..default_provider
+51    };
+52
+53    let mut crypto = rustls::ServerConfig::builder_with_provider(provider.into())
+54        .with_protocol_versions(&[&rustls::version::TLS13])
+55        .unwrap()
+56        .with_no_client_auth()
+57        .with_single_cert(cert, key)
+58        .unwrap();
+59    crypto.alpn_protocols = vec![b"perf".to_vec()];
+60
+61    if opt.common.keylog {
+62        crypto.key_log = Arc::new(rustls::KeyLogFile::new());
+63    }
+64
+65    let transport = opt.common.build_transport_config(
+66        #[cfg(feature = "qlog")]
+67        "perf-server",
+68    )?;
+69
+70    let crypto = Arc::new(QuicServerConfig::try_from(crypto)?);
+71    let mut config = quinn::ServerConfig::with_crypto(match opt.common.no_protection {
+72        true => Arc::new(NoProtectionServerConfig::new(crypto)),
+73        false => crypto,
+74    });
+75    config.transport_config(Arc::new(transport));
+76
+77    let socket = opt.common.bind_socket(opt.listen)?;
+78
+79    let mut endpoint_cfg = quinn::EndpointConfig::default();
+80    endpoint_cfg.max_udp_payload_size(opt.common.max_udp_payload_size)?;
+81
+82    let endpoint = quinn::Endpoint::new(endpoint_cfg, Some(config), socket, Arc::new(TokioRuntime))
+83        .context("creating endpoint")?;
+84
+85    info!("listening on {}", endpoint.local_addr().unwrap());
+86
+87    let opt = Arc::new(opt);
+88
+89    while let Some(handshake) = endpoint.accept().await {
+90        let opt = opt.clone();
+91        tokio::spawn(async move {
+92            if let Err(e) = handle(handshake, opt).await {
+93                error!("connection lost: {:#}", e);
+94            }
+95        });
+96    }
+97
+98    Ok(())
+99}
+100
+101async fn handle(handshake: quinn::Incoming, opt: Arc<Opt>) -> Result<()> {
+102    let connection = handshake.await.context("handshake failed")?;
+103
+104    debug!("{} connected", connection.remote_address());
+105    tokio::try_join!(
+106        drive_uni(connection.clone()),
+107        drive_bi(connection.clone()),
+108        conn_stats(connection, opt)
+109    )?;
+110    Ok(())
+111}
+112
+113async fn conn_stats(connection: quinn::Connection, opt: Arc<Opt>) -> Result<()> {
+114    if opt.common.conn_stats {
+115        loop {
+116            tokio::time::sleep(Duration::from_secs(2)).await;
+117            println!("{:?}\n", connection.stats());
+118        }
+119    }
+120
+121    Ok(())
+122}
+123
+124async fn drive_uni(connection: quinn::Connection) -> Result<()> {
+125    while let Ok(stream) = connection.accept_uni().await {
+126        let connection = connection.clone();
+127        tokio::spawn(async move {
+128            if let Err(e) = handle_uni(connection, stream).await {
+129                error!("request failed: {:#}", e);
+130            }
+131        });
+132    }
+133    Ok(())
+134}
+135
+136async fn handle_uni(connection: quinn::Connection, stream: quinn::RecvStream) -> Result<()> {
+137    let bytes = read_req(stream).await?;
+138    let response = connection.open_uni().await?;
+139    respond(bytes, response).await?;
+140    Ok(())
+141}
+142
+143async fn drive_bi(connection: quinn::Connection) -> Result<()> {
+144    while let Ok((send, recv)) = connection.accept_bi().await {
+145        tokio::spawn(async move {
+146            if let Err(e) = handle_bi(send, recv).await {
+147                error!("request failed: {:#}", e);
+148            }
+149        });
+150    }
+151    Ok(())
+152}
+153
+154async fn handle_bi(send: quinn::SendStream, recv: quinn::RecvStream) -> Result<()> {
+155    let bytes = read_req(recv).await?;
+156    respond(bytes, send).await?;
+157    Ok(())
+158}
+159
+160async fn read_req(mut stream: quinn::RecvStream) -> Result<u64> {
+161    let mut buf = [0; 8];
+162    stream
+163        .read_exact(&mut buf)
+164        .await
+165        .context("reading request")?;
+166    let n = u64::from_be_bytes(buf);
+167    debug!("got req for {} bytes on {}", n, stream.id());
+168    drain_stream(stream).await?;
+169    Ok(n)
+170}
+171
+172async fn drain_stream(mut stream: quinn::RecvStream) -> Result<()> {
+173    #[rustfmt::skip]
+174    let mut bufs = [
+175        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+176        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+177        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+178        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+179        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+180        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+181        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+182        Bytes::new(), Bytes::new(), Bytes::new(), Bytes::new(),
+183    ];
+184    while stream.read_chunks(&mut bufs[..]).await?.is_some() {}
+185    debug!("finished reading {}", stream.id());
+186    Ok(())
+187}
+188
+189async fn respond(mut bytes: u64, mut stream: quinn::SendStream) -> Result<()> {
+190    static DATA: [u8; 1024 * 1024] = [42; 1024 * 1024];
+191
+192    while bytes > 0 {
+193        let chunk_len = bytes.min(DATA.len() as u64);
+194        stream
+195            .write_chunk(Bytes::from_static(&DATA[..chunk_len as usize]))
+196            .await
+197            .context("sending response")?;
+198        bytes -= chunk_len;
+199    }
+200    debug!("finished responding on {}", stream.id());
+201    Ok(())
+202}
\ No newline at end of file diff --git a/pr/234/docs/src/perf/stats.rs.html b/pr/234/docs/src/perf/stats.rs.html new file mode 100644 index 000000000..8d8d86e96 --- /dev/null +++ b/pr/234/docs/src/perf/stats.rs.html @@ -0,0 +1,413 @@ +stats.rs - source

perf/
stats.rs

1use hdrhistogram::Histogram;
+2use quinn::StreamId;
+3use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
+4use std::sync::{Arc, Mutex};
+5use std::time::{Duration, Instant, SystemTime};
+6#[cfg(feature = "json-output")]
+7use {std::fs::File, std::io, std::path::Path};
+8
+9pub struct Stats {
+10    /// Test start time
+11    start_instant: Instant,
+12    /// Test start system time
+13    start: SystemTime,
+14    /// Durations of uploads
+15    upload_duration: Histogram<u64>,
+16    /// Durations of downloads
+17    download_duration: Histogram<u64>,
+18    /// Time from finishing the upload until receiving the first byte of the response
+19    fbl: Histogram<u64>,
+20    /// Throughput for uploads
+21    upload_throughput: Histogram<u64>,
+22    /// Throughput for downloads
+23    download_throughput: Histogram<u64>,
+24    /// The total amount of requests executed
+25    requests: usize,
+26    /// Stats accumulated over each interval
+27    intervals: Vec<Interval>,
+28}
+29
+30impl Default for Stats {
+31    fn default() -> Self {
+32        Self {
+33            start_instant: Instant::now(),
+34            start: SystemTime::now(),
+35            upload_duration: Histogram::new(3).unwrap(),
+36            download_duration: Histogram::new(3).unwrap(),
+37            fbl: Histogram::new(3).unwrap(),
+38            upload_throughput: Histogram::new(3).unwrap(),
+39            download_throughput: Histogram::new(3).unwrap(),
+40            requests: 0,
+41            intervals: vec![],
+42        }
+43    }
+44}
+45
+46impl Stats {
+47    pub fn on_interval(&mut self, start: Instant, stream_stats: &OpenStreamStats) {
+48        let mut interval = Interval::new(start - self.start_instant, self.start_instant.elapsed());
+49        let mut guard = stream_stats.0.lock().unwrap();
+50
+51        guard.retain(|stream_stats| {
+52            self.record(stream_stats.clone());
+53            interval.record_stream_stats(stream_stats.clone());
+54            // Retain if not finished yet
+55            !stream_stats.finished.load(Ordering::SeqCst)
+56        });
+57
+58        self.intervals.push(interval);
+59    }
+60
+61    fn record(&mut self, stream_stats: Arc<StreamStats>) {
+62        if stream_stats.finished.load(Ordering::SeqCst) {
+63            let duration = stream_stats.duration.load(Ordering::SeqCst);
+64            let bps = throughput_bytes_per_second(duration, stream_stats.request_size);
+65
+66            if stream_stats.sender {
+67                self.upload_throughput.record(bps as u64).unwrap();
+68                self.upload_duration.record(duration).unwrap();
+69            } else {
+70                self.download_throughput.record(bps as u64).unwrap();
+71                self.download_duration.record(duration).unwrap();
+72                self.fbl
+73                    .record(stream_stats.first_byte_latency.load(Ordering::SeqCst))
+74                    .unwrap();
+75                self.requests += 1;
+76            }
+77        }
+78    }
+79
+80    pub fn print(&self) {
+81        let dt = self.start_instant.elapsed();
+82        let rps = self.requests as f64 / dt.as_secs_f64();
+83
+84        println!("Overall stats:");
+85        println!(
+86            "RPS: {:.2} ({} requests in {:4.2?})",
+87            rps, self.requests, dt,
+88        );
+89        println!();
+90
+91        println!("Stream metrics:\n");
+92
+93        println!(
+94            "      │ Upload Duration │ Download Duration | FBL        | Upload Throughput | Download Throughput"
+95        );
+96        println!(
+97            "──────┼─────────────────┼───────────────────┼────────────┼───────────────────┼────────────────────"
+98        );
+99
+100        let print_metric = |label: &'static str, get_metric: fn(&Histogram<u64>) -> u64| {
+101            println!(
+102                " {} │ {:>15.2?} │ {:>17.2?} │  {:>9.2?} │ {:12.2} Mb/s │ {:13.2} Mb/s",
+103                label,
+104                Duration::from_micros(get_metric(&self.upload_duration)),
+105                Duration::from_micros(get_metric(&self.download_duration)),
+106                Duration::from_micros(get_metric(&self.fbl)),
+107                get_metric(&self.upload_throughput) as f64 * 8.0 / 1000.0 / 1000.0,
+108                get_metric(&self.download_throughput) as f64 * 8.0 / 1000.0 / 1000.0,
+109            );
+110        };
+111
+112        print_metric("AVG ", |hist| hist.mean() as u64);
+113        print_metric("P0  ", |hist| hist.value_at_quantile(0.00));
+114        print_metric("P10 ", |hist| hist.value_at_quantile(0.10));
+115        print_metric("P50 ", |hist| hist.value_at_quantile(0.50));
+116        print_metric("P90 ", |hist| hist.value_at_quantile(0.90));
+117        print_metric("P100", |hist| hist.value_at_quantile(1.00));
+118        println!();
+119    }
+120
+121    #[cfg(feature = "json-output")]
+122    pub fn print_json(&self, path: &Path) -> io::Result<()> {
+123        match path {
+124            path if path == Path::new("-") => json::print(self, std::io::stdout()),
+125            _ => {
+126                let file = File::create(path)?;
+127                json::print(self, file)
+128            }
+129        }
+130        Ok(())
+131    }
+132}
+133
+134/// Statistics for the currently open streams
+135#[derive(Clone, Default)]
+136pub struct OpenStreamStats(Arc<Mutex<Vec<Arc<StreamStats>>>>);
+137
+138impl OpenStreamStats {
+139    pub fn new_sender(&self, stream: &quinn::SendStream, upload_size: u64) -> Arc<StreamStats> {
+140        let send_stream_stats = StreamStats {
+141            id: stream.id(),
+142            request_size: upload_size,
+143            bytes: Default::default(),
+144            sender: true,
+145            finished: Default::default(),
+146            duration: Default::default(),
+147            first_byte_latency: Default::default(),
+148        };
+149        let send_stream_stats = Arc::new(send_stream_stats);
+150        self.push(send_stream_stats.clone());
+151        send_stream_stats
+152    }
+153
+154    pub fn new_receiver(&self, stream: &quinn::RecvStream, download_size: u64) -> Arc<StreamStats> {
+155        let recv_stream_stats = StreamStats {
+156            id: stream.id(),
+157            request_size: download_size,
+158            bytes: Default::default(),
+159            sender: false,
+160            finished: Default::default(),
+161            duration: Default::default(),
+162            first_byte_latency: Default::default(),
+163        };
+164        let recv_stream_stats = Arc::new(recv_stream_stats);
+165        self.push(recv_stream_stats.clone());
+166        recv_stream_stats
+167    }
+168
+169    fn push(&self, stream_stats: Arc<StreamStats>) {
+170        self.0.lock().unwrap().push(stream_stats);
+171    }
+172}
+173
+174pub struct StreamStats {
+175    id: StreamId,
+176    request_size: u64,
+177    bytes: AtomicUsize,
+178    sender: bool,
+179    finished: AtomicBool,
+180    duration: AtomicU64,
+181    first_byte_latency: AtomicU64,
+182}
+183
+184impl StreamStats {
+185    pub fn on_first_byte(&self, latency: Duration) {
+186        self.first_byte_latency
+187            .store(latency.as_micros() as u64, Ordering::SeqCst);
+188    }
+189
+190    pub fn on_bytes(&self, bytes: usize) {
+191        self.bytes.fetch_add(bytes, Ordering::SeqCst);
+192    }
+193
+194    pub fn finish(&self, duration: Duration) {
+195        self.duration
+196            .store(duration.as_micros() as u64, Ordering::SeqCst);
+197        self.finished.store(true, Ordering::SeqCst);
+198    }
+199}
+200
+201struct Interval {
+202    streams: Vec<StreamIntervalStats>,
+203    period: IntervalPeriod,
+204}
+205
+206impl Interval {
+207    fn new(start: Duration, end: Duration) -> Self {
+208        let period = IntervalPeriod {
+209            start: start.as_secs_f64(),
+210            end: end.as_secs_f64(),
+211            seconds: (end - start).as_secs_f64(),
+212        };
+213
+214        Self {
+215            streams: vec![],
+216            period,
+217        }
+218    }
+219
+220    fn record_stream_stats(&mut self, stream_stats: Arc<StreamStats>) {
+221        let bytes = stream_stats.bytes.swap(0, Ordering::SeqCst);
+222        self.streams.push(StreamIntervalStats {
+223            id: stream_stats.id,
+224            bytes,
+225            sender: stream_stats.sender,
+226        })
+227    }
+228}
+229
+230struct IntervalPeriod {
+231    start: f64,
+232    end: f64,
+233    seconds: f64,
+234}
+235
+236struct StreamIntervalStats {
+237    id: StreamId,
+238    bytes: usize,
+239    sender: bool,
+240}
+241
+242fn throughput_bytes_per_second(duration_in_micros: u64, size: u64) -> f64 {
+243    (size as f64) / (duration_in_micros as f64 / 1000000.0)
+244}
+245
+246#[cfg(feature = "json-output")]
+247mod json {
+248    use crate::stats;
+249    use crate::stats::{Stats, StreamIntervalStats};
+250    use quinn::StreamId;
+251    use serde::{self, Serialize, Serializer, ser::SerializeStruct};
+252    use std::io::Write;
+253    use std::time::{SystemTime, UNIX_EPOCH};
+254
+255    pub(crate) fn print<W: Write>(stats: &Stats, out: W) {
+256        let report = Report {
+257            start: Start {
+258                timestamp: stats.start,
+259            },
+260            intervals: &stats
+261                .intervals
+262                .iter()
+263                .map(Interval::from_stats_interval)
+264                .collect(),
+265        };
+266
+267        serde_json::to_writer(out, &report).unwrap();
+268    }
+269
+270    #[derive(Serialize)]
+271    struct Report<'a> {
+272        start: Start,
+273        intervals: &'a Vec<Interval>,
+274        // TODO: add end stats
+275    }
+276
+277    #[derive(Serialize)]
+278    struct Start {
+279        #[serde(serialize_with = "serialize_timestamp")]
+280        timestamp: SystemTime,
+281    }
+282
+283    fn serialize_timestamp<S>(time: &SystemTime, s: S) -> Result<S::Ok, S::Error>
+284    where
+285        S: serde::Serializer,
+286    {
+287        use serde::ser::SerializeMap;
+288        let mut state = s.serialize_map(Some(1))?;
+289        state.serialize_entry(
+290            "timesecs",
+291            &time.duration_since(UNIX_EPOCH).unwrap().as_secs(),
+292        )?;
+293        state.end()
+294    }
+295
+296    struct Interval {
+297        streams: Vec<Stream>,
+298        recv_sum: Sum,
+299        send_sum: Sum,
+300    }
+301
+302    impl Interval {
+303        fn from_stats_interval(interval: &stats::Interval) -> Self {
+304            Self {
+305                streams: interval
+306                    .streams
+307                    .iter()
+308                    .map(|stats| Stream::from_stream_interval_stats(stats, &interval.period))
+309                    .collect(),
+310                recv_sum: Sum::from_stream_interval_stats(
+311                    &interval.streams,
+312                    &interval.period,
+313                    false,
+314                ),
+315                send_sum: Sum::from_stream_interval_stats(
+316                    &interval.streams,
+317                    &interval.period,
+318                    true,
+319                ),
+320            }
+321        }
+322    }
+323
+324    impl Serialize for Interval {
+325        fn serialize<S>(
+326            &self,
+327            serializer: S,
+328        ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
+329        where
+330            S: Serializer,
+331        {
+332            let mut state = serializer.serialize_struct("Interval", 2)?;
+333            state.serialize_field("streams", &self.streams)?;
+334            // iperf3 outputs duplicate "sum" entries when run in bidirectional mode
+335            // serde does not support duplicate keys, so only output one of the sums
+336            if self.send_sum.bytes > 0 {
+337                state.serialize_field("sum", &self.send_sum)?;
+338            } else {
+339                state.serialize_field("sum", &self.recv_sum)?;
+340            }
+341            state.end()
+342        }
+343    }
+344
+345    #[derive(Serialize)]
+346    struct Stream {
+347        #[serde(serialize_with = "serialize_stream_id")]
+348        id: StreamId,
+349        start: f64,
+350        end: f64,
+351        seconds: f64,
+352        bytes: usize,
+353        bits_per_second: f64,
+354        sender: bool,
+355    }
+356
+357    impl Stream {
+358        fn from_stream_interval_stats(
+359            stats: &stats::StreamIntervalStats,
+360            period: &stats::IntervalPeriod,
+361        ) -> Self {
+362            let bits_per_second = stats.bytes as f64 * 8.0 / period.seconds;
+363
+364            Self {
+365                id: stats.id,
+366                start: period.start,
+367                end: period.end,
+368                seconds: period.seconds,
+369                bytes: stats.bytes,
+370                bits_per_second,
+371                sender: stats.sender,
+372            }
+373        }
+374    }
+375
+376    fn serialize_stream_id<S: Serializer>(id: &StreamId, serializer: S) -> Result<S::Ok, S::Error> {
+377        serializer.serialize_u64(u64::from(*id))
+378    }
+379
+380    #[derive(Serialize)]
+381    struct Sum {
+382        start: f64,
+383        end: f64,
+384        seconds: f64,
+385        bytes: usize,
+386        bits_per_second: f64,
+387        sender: bool,
+388    }
+389
+390    impl Sum {
+391        fn from_stream_interval_stats(
+392            stats: &[StreamIntervalStats],
+393            period: &stats::IntervalPeriod,
+394            sender: bool,
+395        ) -> Self {
+396            let bytes = stats
+397                .iter()
+398                .filter(|stat| stat.sender == sender)
+399                .map(|stat| stat.bytes)
+400                .sum();
+401            let bits_per_second = bytes as f64 * 8.0 / period.seconds;
+402
+403            Self {
+404                start: period.start,
+405                end: period.end,
+406                seconds: period.seconds,
+407                bytes,
+408                bits_per_second,
+409                sender,
+410            }
+411        }
+412    }
+413}
\ No newline at end of file diff --git a/pr/234/docs/src/quinn_perf/perf.rs.html b/pr/234/docs/src/quinn_perf/perf.rs.html new file mode 100644 index 000000000..34363d0d1 --- /dev/null +++ b/pr/234/docs/src/quinn_perf/perf.rs.html @@ -0,0 +1,46 @@ +perf.rs - source

quinn_perf/
perf.rs

1use clap::{Parser, Subcommand};
+2use tracing::error;
+3use tracing_subscriber::{EnvFilter, Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt};
+4
+5use perf::{client, server};
+6
+7#[tokio::main(flavor = "current_thread")]
+8async fn main() {
+9    let opt = Cli::parse();
+10
+11    let registry = tracing_subscriber::registry();
+12    #[cfg(feature = "tokio-console")]
+13    let registry = registry.with(console_subscriber::spawn());
+14    registry
+15        .with(
+16            fmt::layer().with_filter(
+17                EnvFilter::try_from_default_env()
+18                    .or_else(|_| EnvFilter::try_new("warn"))
+19                    .unwrap(),
+20            ),
+21        )
+22        .init();
+23
+24    let r = match opt.command {
+25        Commands::Server(opt) => server::run(opt).await,
+26        Commands::Client(opt) => client::run(opt).await,
+27    };
+28    if let Err(e) = r {
+29        error!("{:#}", e);
+30    }
+31}
+32
+33#[derive(Parser)]
+34#[clap(long_about = None)]
+35struct Cli {
+36    #[command(subcommand)]
+37    command: Commands,
+38}
+39
+40#[derive(Subcommand)]
+41enum Commands {
+42    /// Run as a perf server
+43    Server(server::Opt),
+44    /// Run as a perf client
+45    Client(client::Opt),
+46}
\ No newline at end of file diff --git a/pr/234/docs/src/set_up_connection/set-up-connection.rs.html b/pr/234/docs/src/set_up_connection/set-up-connection.rs.html new file mode 100644 index 000000000..a601750b4 --- /dev/null +++ b/pr/234/docs/src/set_up_connection/set-up-connection.rs.html @@ -0,0 +1,38 @@ +set-up-connection.rs - source

set_up_connection/
set-up-connection.rs

1use quinn::{Endpoint, ServerConfig};
+2use std::error::Error;
+3use std::net::{IpAddr, Ipv4Addr, SocketAddr};
+4
+5fn main() {}
+6
+7#[allow(dead_code, unused_variables)] // Included in `set-up-connection.md`
+8async fn server(config: ServerConfig) -> Result<(), Box<dyn Error>> {
+9    // Bind this endpoint to a UDP socket on the given server address.
+10    let endpoint = Endpoint::server(config, SERVER_ADDR)?;
+11
+12    // Start iterating over incoming connections.
+13    while let Some(conn) = endpoint.accept().await {
+14        let connection = conn.await?;
+15
+16        // Save connection somewhere, start transferring, receiving data, see DataTransfer tutorial.
+17    }
+18
+19    Ok(())
+20}
+21
+22#[allow(dead_code, unused_variables)] // Included in `set-up-connection.md`
+23async fn client() -> Result<(), Box<dyn Error>> {
+24    // Bind this endpoint to a UDP socket on the given client address.
+25    let endpoint = Endpoint::client(CLIENT_ADDR)?;
+26
+27    // Connect to the server passing in the server name which is supposed to be in the server certificate.
+28    let connection = endpoint.connect(SERVER_ADDR, SERVER_NAME)?.await?;
+29
+30    // Start transferring, receiving data, see data transfer page.
+31
+32    Ok(())
+33}
+34
+35const SERVER_NAME: &str = "localhost";
+36const LOCALHOST_V4: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
+37const CLIENT_ADDR: SocketAddr = SocketAddr::new(LOCALHOST_V4, 5000);
+38const SERVER_ADDR: SocketAddr = SocketAddr::new(LOCALHOST_V4, 5001);
\ No newline at end of file diff --git a/pr/234/docs/static.files/COPYRIGHT-7fb11f4e.txt b/pr/234/docs/static.files/COPYRIGHT-7fb11f4e.txt new file mode 100644 index 000000000..752dab0a3 --- /dev/null +++ b/pr/234/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/234/docs/static.files/FiraMono-Medium-86f75c8c.woff2 b/pr/234/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/234/docs/static.files/FiraMono-Regular-87c26294.woff2 b/pr/234/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 zG^?SfCWVvJqLO7S3w--(2F^HWtD50}TMXPvg_1$GjZ}JrzkRTLQX$KH?rd=dTmBUL zR{PxA|r^2*V)B6C!M_iX$-9eUX?xj}? zCZ{gOm2dmt`e;e}Y&m<%e>SQG-Ifr~Iu;P)_ALv(QijbmhI^2Ki~#S7VslfmcQBUa zr4RBn;DT!FU1F^#to`HJb4#%0{xM z+NWUsx#4rVb0d|QRC~Ck=R3yp%6@i+~X zG9>&dIk~bVu%gc5$Z5zaE4WzcGuyi^p_?iB?`hNB=t`dgzh6<$A4Q1POZj6GZjB<) zU8}KueYMDMz|S0PJ~ME}=yNVSgM6OIT`8sQk5zJ8qa_h|vDJz+yp_K5c3SOFnj|u+ z3|){!Zu1iCWZ-qzpNk}OV%z5T_w_7pp%o?xXME9Y8ExL#e@XvDn6)OU?sW^PD2XPy z9h)<6us?e&*KQy3t^=Q&f=%~8pE^f}l?kO4*0|EjSnLW-MHQilS^Fd?*m7}R-Gzeg zyScbuF{qiez$X;Vc)!a3JN(#VJFl3w``B#R1eWgjn<<$@PGyoGX`1d3zcTHbD zK^BkRFg}&Y6no!1m=6;S;Yk8`EW`3qLKh;-fq+?!dFxi|%Vj<5IdkG>dg<&LzrcR8oz5b&}X%%Gdu zIY=i`l|~_*&iD@a={o|vTzYS(%91a9SJ|)8Wpj-o5#|zKE+;5=-m$LXj5nDHjr*R2 zgksy5UMNrZ59?Qr>6d>FM#U_ButppV;q!u=lFPtlTaIs7X=`PLpVmL2u&xeWn3i^s zdW(Xz+Tb%Q{1nn(SYG6V`48$)a`M|@odyKFeX4FnOV$6=e@rJyh)+Ejp0`qz^zp&t zhIG2rVunA99MBD6;25@qGv)RtxA>E}C9Z19YUf*N+1E_Zu1CjJiMOTF;y_Z(l0n7LhTVfBOwXyXz{^0E>}ThSZOgXV z&m!D1qM|DmX2^Znh`Qm1%sWpXY-0w5ns9imhci8)mMlukvuBF>^PFcvv#GY`!$!8! z|432jy$jc}mtWO*zT&U>&0C>MoD(Q6=) z0h_I=znB?cBIVPpdWQ&$`Oy4_DUn>-HiW1LJm&N1XdzLxcx`Cxida8icf+u=pkh{s zA;UP)$c2rqPs}K|!F38ik zw7!ndb6H(=DHpR=#5T+ynBKXt+SM|%BRRq4oh>uTGJwc`I^`LP%85qmV2cEp zak0CsNFpe%V#!o4QVof&N@UBmTBTekG}^^C|DnPQrT4$kWr5}vL6F(Y6y$A`T0|qy zeX=FHev#Db;}pI-GdS^Z^+=u0Ne2GLFrZ{0DUgA&oTb4Dl-l%KNYoH^+Wh+q9u!VP zX=7K!!sPPN7vnDqxBt`0Z(DSuwEa@orJ^M_I{59f{AGYY(>b6egPFl!?Z@iOW8H@I zUX`4I`Imzg=g+UxGGrMUPOug(mJ5C#FGEU?{cmGb)OO6BX_R)= zNu&}?2HVDK&dXw{3i3?&Zx!F7D+Dg z8%DLpbse3XC7lz|(o6A8>k0b_W7h}bpYCT~N@5MfC*a}*f7<;Xsq0Ec2y;+>l-#wo zeHk9KJbH0uU1~lt0a1oB?u;EX(KSM+zO>oDh*omJVLu%Pkg1C}#d1J+?EbdNLh=O~yk}kJ%nGUYlnV&yk&0^WP zhv>|@BL8m%dW=!^Y_0!jRZNo-;jBA{{GG8rUp->TY78s<`i;68i^kgJXq+|I2HC_G z@pM11Rj8x+!DN16kLDa83i+PUKBj8dvmIxW?0i2aq9}7iA9$H_-r(7bVfm}{s!pF^ zBjDnd3)7{PCLhtm)Ipl;rb44QzbkIy!x^RkyUbr~)n!}kzB{z^m+ zLJFF-s0YzL(9_>D-0o$x7~8yhhx@xb2D=e@%Qp)(jw+Wr&*HtnIDbh|=a*M&9A=N1 zUyi;Z+xyDAoS*eFlLfKK{*4|RB$Mv5)2p%=|3*N!|ve05fW8 zg=-p&T5W;jTWz&u27qao|0sD4%xoSr4jQRvE4`CaqiU*`)R9Z)cV!V?$T zi8fE0F`ZDie=7Tx8JBB8P2fu=8rjiJfpzf3yI3v7_|*Lcxc=tfAc^^u=AekPujkYK z?5Hc73O8iCr_hqMw4~YM!obrP#r+4D?%Ud+T|2{Movu-x^;0D>3mRsOEiE4?wkzq& zrWGVhXlJ>TCgDS3<3rH#)8b-1v}Gl>>WBTYK)sJ>C|0iucE&B&(qm#~Fz}89c_hs~ zgSNOKw|K@#Z|SlE3dpT4uV59i$x265d!Pem`@Joytj*8L zA+QW=wZ$y)O?4X8kTJ)}^1B@hU~wP- zHl5nl!rWSx2iG>A-!;*4t`?Gk1D>N%p7SKrWpm74W71&=k_>r`3q_}qivLCx_KXL) z8S-E(Y|$(Dl%Qw#E{v_5_UiWpsAae3scRm8TG=bJSn^`>OxW`xcR|MTM`SjKErI?bjuKW43%{9o2sM%b7HMyDrM!SA1&cO`jkgpd1>NJ_c27K zNgxKD_Jq^vr*4z#G+bt`I#nG#1!02P?!QH!ica%P4f`PJw#U_E0Xd2+ng&kCl}dyx z22WF(zMB<_3D?atK$>As0yO$ss62YsdN$<5u()#x^&16!eQd?-85(kTt`fAX7r2lQ zR?TqBrQFJ@A^kg?1gt1^i^?SY@mNF5nx&Cr86=pLVHY!&0qN41MG_1R&m}@{#PU5! zn8>t`LnYXp%0jcKy-uXSWuECo&djXT>zQq35?NK-cyIUMf|kA-@1s|m#5P|p0IVrg zhp7U*w1j_y2fB>H{GCNW#f8rPFm$SYE#5Yf%G4(|y|ibb^KUX&haid}3C zkO%asn(Ky}P$%`M%Qe0=HAbC9E)f7K-=7R|!r`TD+5t85dBvs|)TRG?Me9|^>Vo@> z_LRH~^|oHU-)wWTgFt+-W$l`<>9Iy_n%{HE3rjIOP=8wEcRwsgokT%roqgajGNV?_ zN42|;_>dL;u*=m_!;7=%b12VwWX;1@)F|D zABK+@Vwmb`klSOm2s-M-DrU&&oir5{8XG?ijh+@C8yXLn*GZreJFlQ+g$wOL$IYmV zj=+NX4I~F^NHBTzC31_GkzXX>-|^T)K$WuUI|(|AMt`Qj!;uZrq@r}DQe(gnaQ`Mp zb@r_+2g2>NET_U=T9y+b4K6-~i~9R)Tjtztm~)jH!4tZ#tIhtB4d1-*hi-XPRvg1? zl5DUt5KPL(NNsCa_9$7w4>)zQ-2v}LS`J2{AE()4Aq0Eq9P^bndUKEll{ot=r5{xG zR=Xq@fB)7=`)8yvZ-)9dI+mx>Y9Wz#WXAJoKg09EYR{pp)6V_6w zL-ZFU;q!^UGVSmlReQdGQB^8|TFZA$^sDx^<$_9?nkldpYvQMZvp#|>S_h)=WjV=d z2a>)d#5Twru$f|(CWw;1OiSU7Diu-VSkKkZ$(poGE&GslC+GVN8MJ1-8DLM!OOtJ@H2Afe6CG*kNqKhWiz3>}%WE?g&|rv1Hz20h zOK>~1Nj;(qzK%e>#_BUvIpVIQlP6s{r6X4*&CQhQiO%7(1|hGq+L8JcFOj)(9pXHp zF{C-9rl5wR!Ro<=j39Hk4_E&8KU<%Dc_=`&@&e(ewsYoE%C^I9Ub*1ItF`sujtn?k3VB8-0wp)7=6h1G$Rg# zhP#uSM%ORFlhL%Q9LlIKQAa5)dPE8G73sOsKcwZ160gNX-X;0R_y;#%&rYvM*|+L* zRE#*AnCMs0-@lSV@i``{3yKX)x?#6Y-LC6&91**zAztnrXKe40%6DDMEI-(| z_q%OXrnZ*a_8k&56HH2;H7hx3R{R}nJlcvz%h2)pn~l&Lghrvwp{w#$7yDE-1ogpu z8qJmk@Y<%&YpsS*iWSgE|G+|;i^r2lcr+k6deuNF1){1C7GyGX6oJ;!33zAQHGFXq@P)P>ob5T+PTP@&c!2&K(nn3-|IfcTJYCcjsxu zapego=~i{f%^&6_+sYftA&$$!iY56xU`e1VSYEQ8LU#umInIgg7RgOu=d!$ zZ}XMwWN2-kki&IW(G;+o&bDVY%?4&OVJ=^2%*WGMc{rYqrzDbAtS6r(F#!Re5Ad@B zv3uo7vY1L*C0{9(%e692`oaTsb9xu5wyr3ii4%|E7tiq6zQ{yja~&*NF$nX@N||Cz zr;>7ETAQR;1%gI{Mg;R&ErL2wIGB?=cB+F^D!p(pH^+Ags$^hJRZ~+=ja&|f>aCK; zu4Tl>GtpTlKJLF5_#yMBS*C4~Z5dxvsJ_&vdOaVTC&(u;40I;W*jF=XVEQSRLi3A< zYtt2E8pva3!dPDQco{>QX%b|U$tp4zHgrM028))*$#FB~{ji<-P@Y6mXp}(jLMBRz z<0(a1l#jd&NjzoOH+eX&G$p-sTeJSn1o6rlZb5fk{x)KSu ztb!vG7Z7IB-a?5KNMqEf6d)gc;5MYK1sdx1oqGS5OB3@BhUCjIf8XROkJvkPv0%I(DSq-bR8u0ib7IOw(S8Y?iE?^`tuy7@c z_??Ho$cH4!yXHo+@;ETXCo1|GI;#)=vl^Qrl5?r)b#fVnfc{ecVmda{$cBl!_)FLJ z0eE{2CXM~Zx`pe>7^MLNVZ@Fb4b>&;vRmnjN@ zf6tnjs{C4kD2C+Y-#D08uhb0x09~qTGEt&ci6?<3eLxlDtkuj>>bWE9#+y0 zZFF#!Ut5%g6{2nB)2zvDfTUE5D%7Q+m2dPNGkrr;0%{R-xqGQ7M!ya-1C~%_%?b}3%JJARx#BHcY{L-0%!-pns7#@eg$NYI)CSx)oh{bYnrQ zkBn|ZpN&stkJmp`qb}u#ux#iA>li1o#>wBi(CD%iHui?PugL!_to-f|BajoZSrNH; zVU+7i57-m$7^TjrZxn+Z10I9aS$Ze&&N%vx@|OmT4wxp`;F*gHFFWWYQswFRVd;}A zNqrcMC5>>f<*!~1CPPAD;#8?uzB3p1AOQX+5EM9(BM65^{;bZUMet=#9y0dwokR*d zWQcReJ_^Rl-tf>J?4?@jejIE} zIBr*?G^yIfe!cg74<8-_|6dhLtNNh&5QEcw(0{n@&&#v$16$1%eGA<;UI$DE0d9?#+J1d70d8%So-_+y%emKgF>Mh(}sQZ;TMsi)L;HW z{y+;wJ^954u|hDrL8uBuL>xv0k|YE>WS69q0uT{Lf7YjJ5N7j&rXXA%N4@>Xa-s<` z%ZQnNKBPEsgF+C?55E}hOHXQZVrU`ACsDroVG!^ir2GqS4d1Hx7qhnUUxkOj+*kf2 zFUjsygWK%#+F88Kb#(=fYrA}#2F>EpZ_RPBxgW*WeI!ZJp#L`kg;@_}#-$mTS0zn6m6$jcX`G^@W{zA|T%uUuaVMmky$xhq z;_zh~5C=abhG0@pP^a~FV)FTdw`+li2p;6*PYwd2{X#N_=+L0y{+-$~Q3OyNI{T$v22Ovxj7TrhVCHMFVIvD@%DDx<6OE8XoRa*P2#Cd` z9RQsj>8kAqRgw5rMSRF?4$S^|>vis}+h_grbT%asjvux^G*tFtsk%larvOo z);Vt_j#Vw_xXioUO^KB-OenKbsZ!5%AW<8`O9@{~KB}>V9 zw?Oo7>7y_f*>dzcEaPJW{L3OD5?|U6-!$}0g{bw2NF^j37u~d?_hr<>^(!~F`l^+s z;8nvAJcMv;F(}llpN5nM4zl=pcw5)YONBQJZ_G!$oSt2{JOu=%12a=(g_s6aLqgaW z<%QD;S87;F5n6g!4zl&C2&&vmAxenSmP@!7zUBRRn8Y7Jnjl_6yCbn_0rz=67ljbFdOK>C<>Nm7px1>K_dFm zuy!66u1|clNf+nT`By&*FVkkGr|;z>f#~HA*u-1X1^!Suo+Kjb1lC({(uf4HLnqdk;=yD+< zMkytd$x;#qCPMmL@I?uYQl6A7WiWwcBdE&-Hx?%;l|*ote~omiD<)H)eDLW(SSb|M zK{#BcQX~|^rEkMK#Hl;d@!L}8O@#flEaImN6@2qvFqaF%2&~qI%W-%ZIpo-W+Qqn0 z0)73Ea#9Q!RZ$yzlreV8QuMD2&-{pap*X3T>6>3`c#|+LH&>I_TB*(Ela_9 zD`#W)J?c2#(gsn@Ai6FWe4-chUIMl;9yj$r@_Bt6-`fB9Q^cPUSB`LpS@2q5b~N{2 z`51vHRi>>QlWgBe**HPHCDJ5_iX?nZQ3!|NBkYlz-$reXULSQZ>g{GC;mChg*l)2_ zM-CzTI0WK^CVFesqUbHrnhDbFzIA{E_I4maEuriWjLH;2oz9;5-1 zA8LPNQ@BA=Hdi&JP8e1r7EdiPcP&BOpL($C0fIP1+C@UNPPOf7LmZnrzUw$5U@A}z zEQsI?dtOAVy$)gbUgF=}fzJ{DA-#!V98M(?Xj5j3V>-hA59t*t?9^|44Jkc_*8~BY z=xTb2YW+WrQ};2S#vV+`8oGwAa(;IVHBJ3nr=h8~tLeW=6{GK%DwL$FuoE*>Dw_H) z4=|R3aXN+2FbKWp$&QfuZ1YrNaKdu+B9DCak)!)#sJuU_Jj z99){@U!(QVebf2U>J6@@tCx6ZJR44B{15Qh;)H*g6*b3q!El#S1$T8Rl&UU;qD!Sz zbalZhMC$*CVd!mix}CwK+iY|u!%nB$m<)!^#$X`H=e$Vrn!!*!QPfGM*!nR3T@EuAg%}a%6O)`~sbR&)As$!NWknWr6xH4z#-d_k;DN`U*=R@bDoJ zXsH0p@M+rrWUo$NIp(Lt-vl9x3kZDp;GWA0rac+U*6H_>k5&|0Twn!Qpo`m|h95l8 zFBsdS*X8&%?r|hN0FW~Qaeb5il1UZ=nc1fW=%)n)di2cS{$4cn+RsDlICYv^D0GWN z6*-X>{Ud35C_O!+gxU|K%?N7R&0+);d6pkHFrBoR9)y0 zJji>nsRZ1sVpBrGQ%FNtWa1D|q6t^i28BgOL6(zHX^JA2q{%T}<~qRYj1B{%`so2zR8{EdjTIR05wB^RT z50!A{(^O{6Q~{C%Q$33-(Y6Dmn;L%i3)`oNo_uI640hCvW!vccww^#09&o!Lwf z9-o~`j}H!xPfu5b$Q1pUoAE$;>`!HWb-1Nt>*Bkx6k??2?Irdd06@EKukP|*nJ(H&y_ z12&mX2975bwy7=T-$?Kyh8F#_J^VG0CPW;V+?p~@QMgx-$u2E^M9_($Mn9cTmD#M!D(dZMb|&H2x+RnjFpt}XoueT($~RD zLH|CuY9HkgdEp`&C|T3v;K@+%l?(zRvE+@AfITwx2^gi(1yYU_3s9F0?nA;~Kvo=o zap(bY^O)+?;9t}&6oARx(Z0vqCdrSbw9$**#>Qj{dWvM8Rwul?LXWOHwROd5S;pZ` z2^7Dykgx?UKYZyca!))D=Mpz2KRq=VWPy%N=V%N=BgIe<6~tI%7KAmGR)Lso@a-MI zc*JvAp{qMg#PGI8je|LX?aj|TneeDEqI(}ApwG$*)`)?OeS$J$VmBtTn|!{8_uUe{ z#F9IKz~hHj-aL|j!g0zpf{e&L#9Wx6n!Z|}%nLjsL8eU0ytg%2c{D54PvP(XkAFbO zgY)e{s3U$aj-Z0t&;J|};P~?!4Q1gp8cF+L%@%6(-~BB*7!A(twLIZq@bZN$T)qm`Y??t+s44q9pDM zCH2rE&QKHu&B@$CP8HMWWD#S@TQWIlO4JZ%(V$S|C>v!wDiae{PxJ&-iW{FU};SlJj`Gwch96EqWfZl zWzU5wv$_zHjV_c<}l^$03)wlkF^n#=O%CVzJ%2&4(O696Sh^Q zGVPg(-t#a~V0Orr61G-V%BAG(pc#r)!CtR9S_X-%wvxDBPMUj`IN~ zp4Iq?>zOMUAN6&NEGLJ@&2D5Gy^{N%cbLSZcDs0PhtXo`GycXKl&>&?kx7I3)Cyc{-%GtC%zTrMO3?PnFIN^DKcdS-2rs;U!eIC0hs zwqzlhAii0DP#4c_|G0}lWF^N4c{Ed;4CJN}0oGOktkl|yVOIN-4+P>!aoXD0Egvh5pVMxzRtb~L03Q9={Mn2;O zYAbajT?xm{h{=d4Km@*2rIO?{D(+ON8AN3&SwW?e&3e>S3Wbit(K_5TobJ0rGy6ka zBNIOJilAcpYTEy;aBkP!&am>&A+DMIeT8`EpTtj%+GsLzA@Aq;J6Flg8pvgmF#IY^ zk1EVH8lhalwU@4}F>O@XnawFm!zu{i)OW_ zC`UY5Tc!8*YwvzA>mWyIt+p~xNQl(ft&vAU=fFq#$AQ40oEa}iUS=QSsAA2&TNGQ4 z8IHNe)drc_1K=-a)vpX+d9F4ZhQ^+6D=*vbasejj#(jQ&Y@bP%t5fZ78oy85_r4En zlWPFvvPcIj(q~$VVaI(iQA58X)45{TU-K#+@RUV@(l(!HX%yt)KIm1#GyKrx2`qO% zl!iTuSm0g}tb4KEtC(v#;31HBDsY}s{sXCJWqRdLvT&`38_vTc3Y@l?XY?vrKlf*h z1=g+O_=EU^LXR>3yx=VvALV2bx1|_`T7iV%}abcONBg$bk3wW+**Y! zc|c~%AD&X_W){Xq+t`gtqO)IZiN2(k#o4|y?=7vFvE7A;J`lBb1Ce`-js7nHb?%!; z%20lzmn3xZ%1@S6C+iRuN)upRwfzJJJGFh4)d~o3Gbe_txFc20ZhenF3^lV~)nn*3 zWRFyGQ&6FDV9_5=oGkNobku!XB?H~EWK&@D>*&K!BAt5zTwTzy7QGx#ce{bybW- zg8gmZTxuSWPE5W$Q~K3~nHv|9eCv721;tmR&Rjjx${lWXbY=UQgnc(d&oB!`l$Zkr z2+{8hd*6AoVqfbvL|_x7!WM4m_;;HUU$tJ1+_(X+=3_HS-;kB9FF-RQPjFrkiO;Fj z=Y7=|)OnZU;xEO<9gdHSXGSrNDL;OQYCqKpPM#P0nujuklRUqDeG6S-?F;WSJ zBti+jTnDztXZJ~*gCwAN-5ua12{}4(4P)F~^#odjKJ<=9OV)c;> zKb?v>535QyOCw&P|KUF1UQ^+W5VQY!|B{)mZxJzA*YtTaE51T_C*N65lM$X+DpHs2OSNJeihSZ!;*Yo2r2LOi#gt`q1*n=YPiY0 zYpZQcP0Vpoo=N>D03|gm2(RD$d<81Pf0fG@|%> z7_KWxC)}1TtR-=hoe7r_uTt?|x=>X7v@mJM){Di9x=Yd{5CJ}+MLxcjyPHgbp(gQH zAtCbjxHU1s7pf)n)R$|MZ?m~FpIunyAo^qZ?;I9CG^*m{A8~uKBo&8L3^u>GnV)tP z6nM6C(?D3#9ltdEptRYnvo9#ygw74jCi;`zEzl7TOHcbU+}XnIgsLu0h?nv-T)50O z{8lhh9aF+24sm-%O6+8!nNF9FPA-l{fnd3S`8GIx!*twJWXz# zKfYj>-Q{C<6=ra-JIvU#`N{nM%-w?wjXLEBC)nx+hA3>1EBS2pkd#klJ!1Mw1AL4k zyq&k!E_Tu}nz_*&)!j5D9^%nPLg~>vJkn^N^?(R^Y-3nE3i?(pdo`&;`Y zL#{^4U&u^~4wD>;H3&Cc60w+xAF{f`WUCAP1wm>&S-jB4807D!j+=F6hL}dv(mZxV z)73_3T3kw%XuDN|qq!40hSTD@|Gb?ejC=y5bmk-0R8R6D)_8I<8w}%yu>=0zIEq}M zlM>`^zF+!e!iE`lAK0nmf$7t(Y<}j)u#r5yK+Djzf3HSaV=eROJA1b_7q30sl7$S1 zy-6vxjzx?;CRdi$HZH&MnV9Os()8+tE{b>Hnu-Q6qQ#bSpmr6yYIO|S*mHokM z)A(&kO;=rE7p^8%Y|)^$KxuTud&lLK5eQmVKO0eVeE8u?>lOAEEBO{`CFq2aLut#( zOBlMFGLP8Isd=6y*GG0fao7PTTI>k!JZrxM?EZy;xRzKwH>7SA}3}1b)hIJ+~E3dp_n>oNRX68R%igPG?Ob(ZxTp8y7-!%hSM8kA5 zBkuYzAcxDh${r|5Y4F~?(VO1%_TK&2k07{w??`KX0pl#j*7)@CeYWmhQMb0n1}RZD7`y||-E z%8!(4Q438EU2xKLdT2UxgrVuYvb^bPNe;PxDf1|1A&Ys8C!|EO^W6Q<<$*bJkujfQ zDOxQ4YBxld4i5GhaiYq@0|4eRpJEoWh{bYDYLmRbDVCzmCTE+KUbOY$fH03@7P5%N zJSHV_Iv3wOI=itXc&~n75ShE(g`3}xakBK$SyEe=_kPiklYfl3;PoLO3;C7Z2PUP& zU;oaOc@(pd#mUigH;;YSf4`o!(P!>dUC6JT30Gc(+`TY=L0p+_Lp1LP)7@{bHFCm# zabdzdido2F>x^HFFKV0ki8g*hIDv^UUe@JxfaX9e4BY&s=V<|yWphpOjCW~ zE+`u}UW{9yo&c9o1(r?1##Pdva`O(VG+`L^<~x{-U5)|D0-$c()=Ckz>9xSJVY18q z0A(jo+pFe_d$*~3^cQLgP&oitHa3ufOpJ~)_$IZIkW^30&zdieP&NJB8(x5>3-?c% zM0R^_$Vd5+bKd#>BGsJF!4ONM-t=bqQUWzP)?4`fFe znjAJmy6d-(0?K13d2?1-rz<#snvx%AvEiPUQ%p7kRU>bCz6}$=vbhJBV!k)~&u*KK zLx0{l%RS5*S*Uzs#Wvr z&Ds1%DSq2isXv(g2J|V_0=4D0@W33apvicN-4giZ`*iLNW9#O;W{1&e_Z!wXSJz^P zjl$bLV^?#0Qo6oR>r_2NNMrU;MYf-T=`1?R{VErrtn&Z=vXuprF%a3-!XA~fqnWGD z``m)E>)w~L=w)}TmHZ|eyzB{%#xm^BWJuowIr%t#`W+owQn9zBWD0zF`NVDSB;-0q z?qk$NjJkV_IJBg?3;{PAl*$d6e&zC=( z(PRJTjgGsUh`WloyNJ7txZ8-kj=1}XhY&}`w*3*M-*?GJLy}Il+2I5TwdiX-+80sb{(<%h`Wfm zn~1xL>~ZjrjSlU4SYPFrRegW{D^CtP(D5=lzd_e~kR5~WP37qM9mPLsuJ&nOIaY3> z?KE_JjLv`1^(AEIp!-l1Q*z5~w4Kv+e6qn>OTDM}^q$_+dref1o`qsGSNk-tJBEZU z+I;DVj_8Pv=!lN!h>qxpj#zG%l#KYxOQvW|$Xmw$55?tq%3GZ8uBxa1wCa=X3cgMBXdd3>(y#TEJ`S9NtpCR$7&2m zdHKf@asX#|U|-^yL8u0pEx|OIO-N|TYk=)@R*RA9V%0+fjWp4WgqEV!U{0-4R2Qor z8fc`6W+dY+EQX`|5uu-z^^1zU`PodgzrK9tiQe}8%{$)nfsf6n!9KC2`L>g->;&aV z0vN;I0Y?gPMF(3@LU~MO6*#(aV9nDXfDrm&fFVX0V~QE(ScK&qy793X7(@j2)rVK1 z=Zsh#PpqC0A$sLWxMpD--&Ax))H~tC^x%ynGns`mF1X@`J05tl2p<-+grzKFIllN4 zz^dq-BO{0ZW7KR|7yoVk7pF3z!FD4H{;%oln&d$^XkLgc{68t=sp#aSeR5iz%3-vb z7)g{+iH(c{ujTfXvhQGtthw=SqVulF>T~{$#c7{wjY9BN^p}d;mH*Q|`6Un?ZN0GR zw`#ql7_UFTz?tQ${VA*1fA+e4c+}LJ-Tm60mD2$G#H1PTVcM89AM#Yb4)m6y+y!-8+J^6IvWw2mVLw6yL z3}Q$}MAP5m8Vf=;mAD@{eUL)Nz4814EK^0vN_!H$0qh{ekwOMB#FKunh;2X8CeX+j zdknkeacI>gQ;)g=^Tkvpu7()mNnaOPbFYrUy6)OP_8AEvKxcL*R39Ji_>10=znoLr@q_u#1lY&$*A|qo?#BOZyfz#){cmi@3_85c46A9 zW3d)k8wFUkb;zLu-+j5N%!+hk0D#52|1My#oz3^}V%0n*7B_}5G2LETMa^P%Upj`> z4mV;v8=}DCY&S*;^n!q$54+zrTM;Ju0Pq{hs!tVWSv)XTm8~EjyBa(LB&2ystc7Q4 z%mxP+UZEkp>JtfY3Jh>qBEl8ikB`}+Hq)~t@WPDeo!3$q9Iz}&49-fNEX=|nVBJ@d zkBqF}eTiM9>@4Dyte(mOEV&tDo|vR}Jkt=GjIAPqcyAd);xoIQ?gF$ou@h3s*1x6yuEeA z{aqz-*@}pzeorV#yNOU_`k4)Oa*p|OoQEcz7 zr5+`6)vMLcr0K@Ctkh1++5>eFWi^&mS8{=0-ACM)tFKvMwLK-CDz?)+CF%-M*43-k zO>A%`ZFOwRMXh_*9zW}f7iHZksb=JTPrNAV;moKrSgo$yV}KbrtIDE&`j>WTi;mA+ zpHJ3yimd%EMOlTpYBsA`RGfMbVODqR5-XgQr-LJNF+g8cC4)XLm|BSbLwwB&2~*Mm zN9JPS^k3kp%_wlrU2&pk$UF~g?X0$hzdT}0el@(#^Xr&1EkutKk?C`>8F>#bo#sc4-_DaH(& zYRfuy_j~leXeG=>DgmYxV}?z&rLO6!d^(zIDgmYxr~CxHm(8g3l++q#_r1WBV$86q zwg6YV_gRTl6sDw@VNhmdb5dlk)Y@!4#$y`a#D7Cs#L}7*hrAumQ3b< zkBr`V>&l`tH?!i{>P!21GjnEFc2^YQutrYGN!jhGn@BM`Hg=cWmc(JcM@H|wHD-9* z@n=UM#yiQQ9lo_UjE0$AOI$m*PkJo2xvjPnW|!wtvvX3lRnsbzG~=sm(WOSWo1>$I z?K*Ui#WoE`xLGp{a2&-cKF{sQ#M!?YPsFk&FJIx~gU|a&@{8Z93vV*fe;WRI(-Wpv* z?4$OkOerAkjZT-&+-z^ZocDx|IaA+cu56ex`CpEHWwW|pSuSWSIFYHd*^$s-hWmfS>&I?WPWV;wj)RlU@Swp{ z=@|2~=#9l|?E>68<_xeW-i{L8(=V-XUL?QRDc_~35N7T8*8Sy+A1o5G)O+IBI>78k z0iZSFuUVys%*y}*-*Fuy%YD9$Xl>NP50Nl7n&afJfkZY6Ud@M^+Ob3}0~S{jjsqQ7 z**K3GV}!Uex1&hkyIm)lljO07TN_doMl5+qO=k@*e{a=6sr7*0F^2)$-7zE70DLW& z-5p5OdUKWgZ~|1yENx|q?+_dWVc$G;i}XDzzDL=Sssz(8@^%vgmQ4Kz0tiZ{ZB2_E z9hsZFH{tr=Y$rtV> zXOHZdlr;jA+kmLK_EdP~SQ`=ddIAi)AEDN{u;J<)I6#I$kYe;=@&=W^dp>YlF>zrU z82BJgHDofcwZ{qeOi;MZk#s(hxkLcS)rRma&}z)I)lacv7}i20#E)KmiZ)-(IHSz6 zVvC&B0IdY_#$EDS`t_eArRTw5vUi+~*coGjHc~C9B&g=F)x?fL1GKY*cvqxq9Q7Fk5wZG91c`3L=zH~jsi%W$j7Z;Sd5O86;W zRd6Y``+hkGmMV+GFd9##!HA8-H!WR*b0T>plrhc-l#8?lw8E4Dm-<$`Tx2c}QclpC z5`>TUkZ#nJ9*=H`pcW*Qv}?PQo;|R4?$(=}Ve#y~u9mJz5zN|VFiRN%i)MJN>=b=d zaX;=3M^MT9{>))xb(qVBCKriyC*yHbeln4JRnt)wjv>)3sieLAQ%rB6rC;B0aUm!)~rXES9IjogiMk-1V2U`j(WnOVx)*EYylysw0B2WYp zR3^FY^bS%Ys+=y>u>cR#=Yk3U6537~lo+4|(oZS2p^V$Uh8rNqV=M3~bI|P#fWDpn zN|LMxedZM|BD_VgK=252gzuq;W6g3!dKP_F)zrD2F?JW|fA{VTMSO|--wwF*`wpuV zLeVH>u^d-i8^oH9hXZX{%~+qSE~1CT!Q=k^JUQcURM7^R7dJTKc1WDqqUf!Vq^a&T ze#mBs9Zukrkpoc0(B+EAbsLrWa3qjgM~3@QN+0va5I%gMZ(8d+;(ok*n`eak+uM)d ze>}g=FHetXYm;^19jAFdC#e|jgtFM)nRTSAb(OFzKetJDPO|`o)n6Pd+_?^r_pujX zh(pM_p>Z&Zzi6w+7YebhlsnB>CkW1icaGL%>OcgLY^F+_1cX;l-F;Cuj(M| z^oY~HeV$;cSWmu7(P)UFafw*+Ag8_EEebJ2)#_~fIy$JMZK0D~Z3NR&XRHbYf&Ney zX*wXCJw#Z#EiBtadsVj3?3@AgcLi60i|u9DMx9~mxI~aqBY*PyW_nEhu`kA-XN_rQ zv9GqJ*P_YMNTlnNrLmz^cOu^_yRbA0C?wrc6jTae-j?R7L|MVpG_Ww`q(7F$C(irK=yKp(;|Mm1ntN%3Vwe7p1?!(e#hBsM85&pm<$#>WzXevy=R_AKYvCQ{-;i zX}aO^%$ZL853X;jzcfKjuXo{shIspIp8?oc2m+^$pZ5&ut6S zzJb{r%b|&4n-gqH3v9liCYhzBIHh8?76S7aA$#L{g_*A)a5)gmIwN!wYX%t(CapMA zerYSXCje?yTw>@Ic=^V^NZHBKT}uI=b%OlSwUt*F-D)3l`6or`kp%%ojG0CE9B{94 zBtO~iaUPSevv-?Aw+qB?TQfi_95=A=<1T{+*P<2NWsa;Hnb`f>5$@fJ+uiDj2CbVA za_N*4(cSAQFx`FcAAs%R|14j7a@J@yrnlA2K-wi&N3VI{0zBCV7tp9ZpV+dx;?!H* zJ({VNx*blLPLO`Bm#%)=j^J?fch&IbdJP?L$qJ4Y>dI$$yR-gT_4a;4UzdVyk8JD` z=aRr|3163)qm!ZCzU&Tn0s@>E<3`+X4IE+MBm0y5UbwJeUbf#FyTnB9Em;eQEm~=b zb&Jh(1FME<3oz?2Ir7@KrUuw)!#bTcP%11rKl;E`E!^C7mv@61vo9i-&y zNk;Iq4o9e!r)@^zQmm8U&`W~Orrzoh_nXDqpmN|eX0~Gwxu}wV6pR=$xrbX#hh0-h zPV*CNW9#6@&}{B7P)~4y`1%2~!vG*b-}77u@ByChFEYU9ao&)aE$4;@R|OP6+Hbd^lDsKM$qsOs8cln*#^w;DnE!raUi&tCQC7@9J$nwNiXt zd$|u@x!!g-{W&Owa5z1_y*#T2HSZjn7$aUS11E33P9bsgBup(wK^%`N74BT?(~!4I z)Q0$qvn0g;faF~6o<*kswsjYdM4p=6hTZkAhApwgAz|gZHlc zA>|ufx!gZLoa&O(^~~bYZWbgDu((!JNK1j#N)HIM87X|kjv0UH25?IAjJ^iPkPqI)4em(6|j4J zhUHl;x6hC}A?;?kk(lKw#`T0I?s4fDq)ovO0}K3doAH+I%nE*4UL7z(WR6HkA zzA)`9iBQ>t^i{e}CyxQxs?OE(ucZiy^vDq->z>-qvea1DmGQ|8`|F!EHKruRFBtn0 ztKP~1Ip~}#Xb|-z{&ES*Xl3NT;8fcpY5aVqOquu%E}Mz3wG0bX;fPb=|MltpEiZGd z(zL(%WF5$kK&PZnQV4Ge@Kx2LJ5Wc6YqS9?q(*R3X)h(2#%$sy5BqglI{d+%m$LS%YY6b@JdH!T>)(-^)rXHWeU@<3SgzuyXN-QcUIF#=lV< z1jx;f=x+6NQkoT@+j-&Cb;_=&vFNOHL$<+x&2-3_+@pR{Ci7Owwn5^6|-_sEbSlo+bXLjS1J3tsFjJ8SOLaym$kDd&)P=! zT~k+OAwdAj5m-Mn=AsXMIxwdw4hXze&ExypoKk?MJNWLp4rPq@_b^N-6D*%fLG3sS zfVqS3`~~H&lF1^q1|YihGzQs)j>ZTmpIWj4GdIC5t4LUd0{{-dE&?^UV^J^ORgDl| z2`2k+dKG|;y4TZHChiS&;UAbJl9GKLWnoX|3UiY|M+#(^Ccq19YV>!nw(Ymga7Vsd zLFg)C6Ex{mpfae4+}CmFdD?mhDBanR^!Cc-!eRHMY_G$gla1NK+(EHpamt(gRcouP zteuDB5aXwKY(d|9^xDXp2(lcP@5F7F;SRPsH#wGIlL3wuO|$j(+!35U?EneQ`n=I7 z@sz-(I?R~%q6(%%F#hsnKry5^7i$w<;U~D9D&7HP%h0&VkpMiSjF#e)Yddaj@FfSx z5lr;B&N{TmVS_Wf>XdE);bVZ@XU$3V^8M#WCby#%o2yZ7A2i6}9|9mgApYl^4m~YB zA^}8LUMWrjm+{kr28|0B40CvUnh3GmD(KY~jxHN|%~Z0q#rAr^fMaj=eWHtu$->Pt zbgRV~$4MxZBKmmbI*A8?9~Jr-#7}{;+P$jm8ECz?SFeJW~y=5xU(;;+nr{!9W;^y+7=-24Wu`0JDZ#Wu%j}00T|d zKrJdss}j3c)=!DvdN){&dgIjP8*~`vIItRNm1v^^2sU!1(=)p`zN*{B)bHC+5MVlo zic)Nyy?Uc!66-)6{iQcfLr-BWDM(W?)(n%gUfaFY3_*{XYN`bW3vw-P+G1DvNE1dH z-j~dpLo*w;*X{KK03rQH`ygT#L_GKa_rwJWLST2$q5`l7HPLprEX>&|WpdjXQDE#0 zo=$Bk-&$r1YdXJ*F9ZrQbw{E}?2*TD1hwL6UO;QUy2I+Kh{(1{JsU4Z%4S4<$wq+q zO_haLvU3hh+In7KEr1Or>Ghz)qWuk$V3T9abL@>Q(DPKk!05<-{r2ht&5>e?>X?Ub zjRVrz5-Ea-_k9MlKRsjp2kFpv=spxxXGPHY3@aMYYQ-h6;C*KL6eiv*PxP#4=>fr6 zXbOnuk?5eB^}y7h6dX8}@TiDKv_dk6HoIMl6z|LNHyXaGsljSQ<eDq}3TTSm8EsK)SqAT_{TvBe_Q%%tRwykisvD)pvkVy5^vKYy=U8`^N=*@fl(gw&5GwSl6CHIPyvYkf zmw0p%%mZgW`5??=RH`bzFRYu6OnZCMtgVa3b`kb%6g`_fi5ShT8Rnl9cK2A8l9{q* zbz7wYAn>ia;}r?M4ZNJ{gJ7E@M9L(XlqrEo*Z_z78Xm^hdRusC=ngjUF(|V-(m_~I z?hwAV(lsU$P`76t zflP3UxM)u*6p=E;r_Vd_0NF-@uXI9Xik1Mh^o27uOp9nJH(@Hdq@j%V_)UR{nQRP9 zqj8O4IMUBuB3jGw_Ob*W=tD~df`z)+7xz)iz&V} zh7D5_)&%|img!_n&W1f*xk)5F`F}w8s1@RA{U8W26xgl*KMqGlz=!KimNgKY_K8Rl z;~QCX=d`44mxkcnPE04?$oa|$RuSg4uk!nqks9Zw1z_-yzfc=LKlZP(thcb5>^M8{ zs*u@beK9)~>1Bq(I}%DLu=sGuLAN(fB@Y=MTlWa9X9g9U1d@y3*aGm3YWpZQvw8<# zM74o+f#}u`<)xrGy3JBSVw$hbO`n2VC#=-@eTG@hnWlS~i;&V2tWYq$FJg2IFCtty zA;|S@0Ni0C;=U&&^mLOmyP6X4o|os%0TXn4cVQh|mOBY`cWR@Rirj=xz@T7S*4-m` zuyK6RI{SWSxdCSknXOa@h>bB>M@BIiK1?unV*4;Uz80e^WffDxw?_quvcFVm8u$$G z3vuqar7{mA&k-D*2+x-fjcyyGURYuV*S29|;#41&(+fAo4CT#ChcTb9Re0|(Gj#8- zLDCt%35+CqE<3Y2%fzktdPgOfh%bY9xSD;v8!=#lQZjXS(^FVEy^+!l`vjnt`43RT z%6cWUR6uMk_ZX8bPjvJv<{_nIMNf~8Gi8uSGe`9(-qx{w9XaW-+YwfxRVh&-sVcad zsFuQI2q!`Zl6Nq0!P0Tay4w_jQm|S=tWmcx*peKWrn+>ZxiJ>&WQXaby3IgD_7Ou* za|Sj5V&hv&%1=h@n&*;W1l5#=j@Cwng?UGQ0d&P@McvH^HK?PeYxi#a!maqA+tgHr z(yiG>WzR5cpFm^rTX)T3T<#Q8jYNj$#^rZvB0(ho#eL0q5OIKxyB7*HWvqj3i9IZ5 zh@~*W6o0YQtnf5I7);wq4Zo!7$yztq?IOguH*C9fC4^75jeE@{Q+lrnsdWwRR+5U8 zdu@>kBecy7(AyLs5qtfz4$_4v2}X`jDFi0jzrkg(-gs(qu!frb;}SmIyow}EWyxk2 z?Y=*;tOG$2cKgNd42}Zy+UDJy;9M6uQAi7rnKGy5r6*Ch$<0_ui6n0>opmGgBhHjl z_z~^Nk+A7+%K8|l8P}OZSx+Qh1J8x-S=6N z;bLI(K-ur>hGgBq2%{gC*P0age(CHoVs}e)YXuC;z7`i%tQO%i=YvI`guY5A*ZIC~ z+r!7<{rR!ow>!6sV>Lkc5^55-1UKvd(sA;jE{Y-P83Aa1%>85S_f_9b-;1P6A8mDD zR$bC7*QIB$^jSfpZu{TpMSByDdmOwqAl~B+MW6iY?by41hZ);z3h9p^xSkW{_dU$p z%F?Y_}_JT;UqY<5V-fzzWbq}@RWxH47 zLwH==n~-r^-!dl|>0`oEIY*}(f@-2(_U%#DoRINeeRNe++Vh6S(^&PUZ%svOvB`R` z9?4!FpcpC|7gvFdK+9hT=G`32Z%fPqI6hz{mU=W8&J*yuccR#Uon(?i58TC?2~*T9 zw}t?Z2(gRBcV-P4ScwSpa$_>N4%S|>>@ahPjtj{kSbHN{1))r(?psD9m0uh>)UT3W z^^=MUunTOE%n#`8z90KcfLp-f8P{cIbql^ioBM@|*aCdL}j7vsBN7$K#X&HIT${+x|IUh0ei4L~}$q_FzJnqW? z{!lG0h%##x9!w+M=zLwm3|}ERSmvw7d6sX=N6*M^ho4N~Z=vp0zx}rVAW9F4_uQi} zS%mIk_6D^M#2KLrJSx;p{QiEZ%lr{P=Ek^#Ts`{h0}i{mkG$7Kp8uY-M{9f2-^$MJ zGz1nWfXs1Dux;m%=7aEALkD#XpjA*X@Kqy{6c1zNvq?FVJax$FMW|g+j%`sl%i#Q$d*E+gcsIEYtSiG zA8$u!178v-TqE%aB!ilzm1{^oq$U>@?Jo1^F7@yiTb~wFvify9xBGS;o<}cc^t%() zEUYexhk?S=NA1i^y{v^aXOEiy37oy}`hDE@w(mCY@-KV!zhR;HU4~h@(Mjo3*Thm; z0Vy9(sM0{T2MhlR9`pfA>1>?C%Uf?5Le>>cSs+7?xqUaOr~~)juD!H1NV{3jYBB@O zpOV0dlwYngTrq?kHq7D2;})GB0NwHx3Xxt^=k19@*x$1K%dP+A-S^!8S~1#l|9!mT zl=_;8ucFMYoinT@2@AIkfDf<^#b#~4Pv87tPu%@aB-hh`th@AGmfq(+Y)(WG+#rnm zXn6RuY#PHhl+*nGY!)KG{FNOv%)Fux%iM~?*Uftk%8T?m9?3QAKAblr3_MXsq;DZ5 z$Q>H2==7^$|L)qiwVz>mM*1gIbX9~kOtKw`3}AbPYP=kiN@BXp_CaBjJ~{JQXQMnU zTUEuk_fA+dUfZ3zrfc-zvBr1Tx9DGe!Q1-QhL{9@jrn8{G$S)Ak!p1=EXuxZ9&kP4 zkz7S9at!O_Wa_0vP9hS!PUMKkq+U-H1wxSu-8F7#M_7P=JCR%Q$vumDsoKLvu!gN) zPP`(2%E-^8C+_MwKeN)_CHdxLf?^feFISB9GXZb1#&3LCVRzg)YBX0a1&$MUwSPe_ zOAa{gv6rXz&*t8K+GAf>S$1SWf@J*|`=0v;I_+byJ@j^)`D%R7t&p>=JiglHqPzs+u0G85PN%V+7OG*YacQYZ-M5pZ2hjI|~(1OW9Ke z2SkRHJ?o;4fwi5soh{YJ4EONqqBG8JfFY7$9s_;V_JeeY-V|-EadqwU!ajn_+Us=D zne{D0G8PXZQFK>YHWkAd_00%44AGZdhks3gjmNh6gU6Hk(jmCWtBx>W_UNS2bM zPm*UZi}P@Uhpqq{$b$B3&coYoH~64P(fALknZZi3i)sb#ms65VzXLF*K{gP*1QeLi zDr*Sjo`Vt6SO>$0T+29R#xf=YCAQ4IE5fh`G|cTrVQJD=yHjZ@hB$?WV3v94V47TtU4S6C>4aAdx;rU zGqAfOy7Baj&hRm$ZtWWP8lf(0JN6-lqVVPOk?N`kj6U;*evMm&Q02-DT_o`S6}}jM zF&?G=Mbcv}=qC8R@Vboz#8BPn32>nV_(X>M-t^;%$k5r?ea?yRUqHXOY_?FCv(+qf zZ(D7<+)R;haMye2D&9ort?#xj{88|l^W*>9~GY(>J)oHR# z^8Rzm)vl6v8PK1dEks%gnVlz{2frGur)XxncB@cFt{V!Y6V>McXv`a(94*RPXt=8Q z?#Z0)ud7x?wl9IR@Dp2KKXZxAzs5sb`O!@JO6|Nan79G~^rq^U5MKMu-Pmc^-M7t4 z0#jd#P@751XvIYv*?L79t5mUDMz|&yrtMrmXct;Qe@9&sV>+Fuc{|vm3=WHG9aghd zL;A1?yIVo?OIxg?b|oP z;HoDACKBHV#iaH;z*@kkveuoS`!ICjeAK3kN)ByTJshy-?QY^ca~XP)!i73n_Y$~x zadY`)F|Rt%(liZZl|3@ittTKKLlXQHttT=-wsW>{Fc^CsoUjR%T`nw)8f3B_7z$$G z!jKw2a3IIE(1`b4BCqX3y(r~Wi{wkg1#ycXi8Fe%mL@sG5b!IkEwjC5esVsAl#`)4 zo7y_sup>x)*e3ag?D&Mt6nQm7;iOwirApt}y;ZbRT%5H4J3w=p|I%j1T|3808dyjy zg2zS2Z77F;BXU)So@86WKh|IVBp-?k$E+E(-hi@OT4I2NA$N@1hx&o*iTz2~LOTrm(V^-je##XY?be9TO-TtQGXWD=lbuUCP61RcV}l1AmtZkp~zz z@6B5b^6xji(xD(5rI2_$@&m$+@P#zu9zzH|QE0Jilm?-hG)@gsf!$}2WSCrwh^UW) z2!5*aQ9N$%ACIlV;MOplJ(Yg}mX@_|Ez5#n>+5RG%3%WE0dd@QA!UZn&Llc@4y_%n zrZP_R<0MhxDK+A(4WXyW5|HijgFjVO$r(*+S|9))1UvTGn_O2KhrT6U&Cv1NmX@FE zxG^91K9v%|7|d#Nlmp6`(rvuRWneHg6E?`%uB1l9yzP4QRV(j?NmKV~xF&I%GqiCp z#nKe>a%m|dWL{Di^i^TW7}S&7@l9a<%@f;1LQO>g^GCnnFhFfa?9tX*@{e6-NyOMW z$C*?$yIQe~t}3Z<5g=RAdEKF z{7q(H8%Ya;tImmL9q5J3n$W4h8@2^iAtTjN5En5SBEh%pF8xYD0~2QTOTS<`XeyM; z40YIa)7)&aCBU!9@--C}Ykdg=u$pB6w63HYae?*5R~g8Rf7_RVLkMR-6(XD%EnLlg zKWGVA#yr{GPx^=ZG37Kv^5v=TT$C-+=K>tQMY~*Jv8ys%hi!Vpu5;dH-v`Wyfjr~3 zZ0zHM=fD-TnUjb(-oL;r!osTtEEo z=lk3AGCj{nO65LhNqPz!OCi0w8FC{cigqT81HXimU>PNc|D}}sI*oR_dV4xdPP_>f zpIw0kW&~lJx`TC@@t>cQ3v%agIpf}D{%)`S`{_>}{-D-PD8F)U z6L5u_?`XI)*k!Ng_F87Z`?^dotBX#KCeJw4sxS^CE`P0k5eX?i>S8kMP4cRC`7djl zl9&P`9s-mgp_|&h8n%MF0*5#}kDD#7B_RmqpzKLOWZ}VR+?kVCX->pk4AS@` zmiOoMxO`vRvg!}$A|f!58o66Q)FoWiX-bYu2sV`#s784#>`P#~6}HnL;&a?%lG)vn zp?Mt9x!OY6j5ybM8KPdlbF9in)J{jo%GzT?wo^bk=CybT949e?nOtFx-ryzrlEs8l zn~`ArmKt0P`wxOhk3q-LAMZ*7F3J{y)wTwaKRp{H7Iu7NaKSOEs=W0k3(O7x&fpOd znMq|Efe~Jaf(rHBhkbpQ?Hh~gc8-y~uEjmOvvwN;9W}OdT9peYUpcwoGu}fbG>1MsiXF0iN8d6#&>qy{ z_I6%2CD@yKr188L9(tO$DW$ZNkl98%+yhpys!^W>`qW||M?;$+T_}A=0+}zcp;*OV zb{Jjv*{I>7x#L6qqL*m4_BBv5d)rfM(b%=zrp2u!jy!aHrY@nK;UvEYh(~ z(;^*YPT7HL2f86y1k=Gb*4`;41&nc;TiKO(LhLS*q6MWm1)#@3OHd97u+@8h3!W4b zKx-9|_ovhThCngK#GLY)6}81t0O*8+cZALm%{fz9CT!dkq>c?q_U265^oLTc7~>B4 zwQosOhA>g!W-;0|a2z&7ooWl+QuyKMlcIx}px?W8QV15T0M<;iH=7Nb1?nB-3~#0= z(%k_~gz61|?p9q42WQa8aM6bD_6X_<%Zq{FfUQ|T09Wfz(DD}i!d?SSA)tQ=$@ENU zDhK|6G4U;AfW(^sK&uGBKllyST_N_(q|t*XXN3dJ1^kq{0EtiLssr$5Y<~kTG05~5 zbKCZ#j&&Ry!u#2 zgY=Xdzkf-cJ@XItfVO?0d+X;3R(*thFP~yz*R|jM>K22pzR;`A+?j)JyWk-&RLNRm z&m!?yt1{a{wt-opj5*i%@vb*un(}$8WV4o;r)qFT``y&7&#(jx%mm@e@Be z+q9A{@aUvYpvsqEnFR@}b(b5-pZ@!?T`pb0i+QjfIU z0r53zdXz>t+3BZQO@u9T{{=0rU#2xR>vY2|oy}1@gT^}llJpCUo7}AtKc!-amL~LD z*A^HM!u!PF{Gl?EXp&O%-0nOJF%|6F2qK{AYSIqkK!vv&c9c;= z#tJ>m2Y|P3nNHVHECbAS>l|ocv_=J&XRC%V!+$WwW$PWpN(I|-FSUoXT^_7>pdL~z zz-b&`$!IP=GK?CAfU>xeTUD^(L~L(!I~7HKTI}2Y^X(Xsa2VEz|k2fGY_m$k&QZU>#cqCCD}2 z8w#4sl*nOqHB|;jhOS zfmh_ShDZSQdOo8Y)|S^8c&>hTYr0@p>8wNmT+vjU>P5c8XkVkC*S^jLkHQ02!M9f_ zoP^_D9XpHAI*X*;4$n3I8ys8D6{F#RcvK?*>u7(x*|s%hiO{aieB771RBeq4c{PNR zty~7|nMl|rWtn#v%)t6d{u&V;S(C~>hxo_OPIC+&btT#&F`6?E2BT-{ko}{?LlXb6 z0oydQUQ684K-Erv)D4Shl3d~1#Jhqy)O_EEvi?LE5kQ7JX9uTSPUBp$G%qfUs6~$O zA@Uk~votCL{y`s9FXpyEQ7^+2ie(aL4adlI;AaIRbT5uRb&)zvQ>q-jGI~z2%>z92 zjca1cl2OJAGp_U4hBDlf_d-d|hGqF?wmin`0lkgg-1n3&W{H1?Py!d z<+(e1POtcPDDFucPN?m+8sd({UU?>4jc)8b54VFcwQ~2-bj5#<-`;k+rb!RLAB4W- z)zwem9=&}?GH&;}dwDoXTr1L>>blSCPiVNj1PR5PrpxYl4hQ9GD#WC)X`)TXQu=Ii z3j^N3`bB0OiI@7_C(Y5aXy!#E?HNmvf{55zKl#OzW<-M}8oFgch%VjeYq53CqAugV zM1n21!E5zS=0^l!dscJ+rA>ow;B|V9NL|HzR?<$HST`GAW}d_3RXWGvMVjx+)j+ z`wRRipQJlqznPAQF*u_HBmHi>S+ACh`D{8V-?MASwk?}BtXs3Pv@l0ZPmYfc5BB3d zT^(3!b7Q^WXJ>nBb7OsNb!A0)X>nnG?%&kj2^Nz9@bC9>=4_@$hI+bM8l^%i5em5PZfi2A6e0nOK_Z|KUw!dW2>^SoNXX~0 zVde0jKfd|moj0C&;+{LMx#FC&e`5Np9V>%Qa-jbA=jV^FUp{_#`|}UqfAjShpMUnr z2k*c0_8YIi^70GMKlAhxk3aJ8Ll4|{&mFhla`O$>Uvu?US6p)O1?Qi0Hma_7nzHQI z9DmHwM;w00!3XTW=Zdh$Q^Af169A zJ>0{+Ylf;(b$=DNv>uW@?$$DBrsdVO)$YdckiZ6cdaW@Gi&CtMo>(SAq`N#xNp{39 zte~syLOWfE-4*&-(x|fNHj=u#)tHx6rZVY9CGSoi?f#=vHN0+vRxKwt$PU}x6Nv>? z12)(tZ0e0!KVDIqBFf&Ai_m+7FReT#(s~Z^4XkU4`N-*xc5YGG1-9IvO9dB{WCtK- zJu06ALzgA$%*SV6Lxtvk4K~>IxR=isUM#zR>}LGtpTAxDpMU=Gw?F;S`MzM0)nWha z_xHnx8o>hb{%Jhy;LiSqyZuBn*I{)eK`Z`O`Os2ySDgk;Dv&g@%Pndj(m}s4{NGkG z{u zZ!LZ;y;)chC-dRU9+wIlry5cMz)ThEg!>)Hk95DvqeIv&?2~(vY!(tY$8cDOl|eWc z=zolftW#bz$1j>rZ`NAg5aowkoPMcRMYou&bSdyC8`kVhMOwxx@NO(`y%aNN*)aN1 z6xwLpc$|=EjNV(lpNxbcOs?bS%hP0elnkUVMb>gTa_xewlN7lqyeNEq=?yk5Wt&|s z9R}gXthd1dbsS7{l4#HQk-;M^s3`-w zgcdMX{_#oc0A&+EB;=r8zaY-7LUh)-S9_vjmYz+FNw)9bS^-XCY$$~*brb77C*4k~ zEbBv=nhmnuD%gY6Y;yO68a&=Kn;Q{&%dRVS(u`z>4)v?***q+SyAS%H{dEJv9Qhb4 z{y+(2$rsrU3)xUXaFtk6g86w)P_qDK@Z=nhn;emHI9! zt1X@PU%xfJF3;`CEVnl`)K!+1mgG9DCY?qhgCrszhs7}33d2TQ2(q@l-RbJ2Ge6J| z*N%{EOEqBe3}~2SoPOqG;X1qa`Nvmzt=!ewFRxw_$8_*>SJqAMD`@1?b|w2jmJ$qNoXu<=>@~@qKKa1P;Zg=&I`yTKyT_9MT z<>qf90+(6W<^KL$wT+otNbwFCy2ooe*J7cMC6*-4!1XfE%OJ=X`WqE^LcjvACC_1d z=yWcecJfHoHPbUhkk)+dCBa#FHCIhFruU)qp}8lqKo*wR4DJGO|9rN|ww|{o;4Te2 z3;jGYP*HeP*cSj@7dTPp00zb;ClXtOmDl#zVYU<1eM|N>p z2Gb*(5%aYb+{qZ}oJY#$6o+_@7x_DNROkOKd`!PC613W56xz=2HPY9*JBx?8T;JVk zR5y}(L6JLjGy~~(8a-jnNGY#tnXFQfGY9rT$)op0v0FUyjSBhJ380%^l2hoGf|c$L|*d^w>xk z9D*!dhQ9U(9lF^xx+4ky&lw7vWnPc-HY<9+EU0d4_a|i4tKs7tVC%p~45W@xuf_*L zY&WK5PRVmUmCl)&nDOU+!8|W8LS0V_QU67eJ9~likZw+ypa?%6QyPS3=7dpVl6geS z0iM|0e6M*|t6~Mln6^|RccMD|_@TLa9?j4w^ytLeuI(XwBD(vvboQUBq^tN|=iwiE zEcV&CU(koIHnDoU(0_LSl9VxmpztDSya8s%(lJAz?m0ajhT`z!a4OZ06>r|~mzdLb zra4t9iZ;^ll~{m4daKw~nMQ|AzI)@58K@=zoUSKXci7H4fWakfuNjyz`wh( zcA>p-HT+D5o{T+)oZ6EuHVoX5*R?WE%V_m#jh2%QH((%J7Gwj za13;R$g{@qSE%3K_91|IVT8{~CWlX-?k!&3AMfzA7zFQvM^Rr8dd$a2xXqG z(bYTwdSF^2Tq+DPAUQ91^RdK3;8Eb15$fps#V95y(^1srX1Ug)7s{*z^p24znqz4U z%q3%G9UZ(DW+$9v^8prv5t%EOkCB8(+=b~p{K7IGC3$b)!r!hC@dyIuWSJnF1t)%w zG6xpV9ou%0?y#~{v_YZ46<61FKgO**%N7o(zDruyPEDYEz?H=R<}a_2{{14YhjqO6 zf+jz?VhK<2UN$TkviIzKRL@a$qPmFcE~-RScW4dImien6-W*tHnWJ#t5jwJ$bz&?Q z^-v75GqCYHWunSJRTXIHM-r!wu4C;)=E8lCTc0P{o`gria_z zs6L{)hHBJy<&_E=hOhX)`Gb;$G9;x4B?l!Nr5Gg_r357d>RFtZHxD7yx&QqQDJ>`u zq+GH-uaj!5mhN~t$`71!0gk~a_6u+82L0}llu;?$Q1VbZQ7Ta4Q5u(VAJ^0>Of*D+ z9pm6Lk17quHx=??!{YTQ_odv;Vz?O=AoueND#O3LPd;V+bndmdq4+y1$1%sX#e@*v z9B)X8)tsQmC{Ng=xJ{q)}F<4 zoLEi?k8xvr``Dh=k8d(m8hgH^{aC>npDc<=*|+%0vq23jcU0e9ynUEJFO@A*)(Z-0 zD+{cw(9GcxNgkAh1nQ`M1JV|YN*&`8SUggEcC29;{QH%%y*nPGBTN?G5w<51?AD!A z5@mK4**-m4IvP{*x8+#&8MQ#KA=osoH#Jd@7RD)Sh7TBBO|iIPnRz<` zS2*eT(_nQvN#e&P*8o*>Q0zWvySA6@Me94)!rt|?ICxxZTr@2gyQwinDlUzq5ffyV z<`IF1BeFQ|6#2PYRnY#v8zmnl1x2=-^qo(hOG?3+b2~kLT&77c{E)b1^~arZXNyVd z-ARjsM>}o6nAhgW>94_*d@C;76`a!Q44mfUHqPSVHxz!!R)D2@;sHcVgdDqRSc=_f`!)RFtw~~Rj2tifAJ;f?ss~kPl{Qs6V!%qP5|CM#x=LvzskA9gXrp!a z38P)II}MyBUXR;4%Z+hL{m`s9C%hHv9dBs3G%KcB#?#`zC&U?(Hn4y9X#D@Q_&mOX z6yOjO=Yn8Jq=pdRJ!dq^3{B@8_yi+t#1~Tnb8n)Isepxu_+v~Mb}B5mU~jjzWaVd6 z;{!pg!|KqCYvSC9 zY4EaQu1PZoAI#yH^bnF^Mu@mt`^SzQ(H+Sx*pSkgj^rZTxA=fA}3 zOWNJJrR$zGBPpAG^Bof3I})NLNtPIcmny}Ii?U(sUr0-$vh%8wC+wwXuw}~1m60dp zz|qehByvs_YR<09u9YjVUQUC_V&~G~=Gx=uHV}%$J?741B9+M%j$x2OKh(652tfuWIckK6!HDp#TG)Xdz%vH*cs);6~HkITcG6?WY( zj_dhBuQf@toCBS2-L%@BZm+Ll z4tzA8OlR}Oa<$%U0T6-_6LUd0K~glsayI5wBFT!X>4s_9j_dhB7{yR#Pg!1+RSjcy z?)qVzsLYG9Zu@aw_w#y>%Uv#CD3;2VYOUUAw%VO;ua603Tu7yjbv{5G3`gV1bT(fs zS8G6GQS(T=^s4JaX%&RoVzt>FPM6!`_4(IoXSgfC<^M=hGy_=<@q#GHimK^`X<^%O zJ^vJhQJkb%UX)ecw7&Z0I|K@YBakRG28(Om4}nM`Q>Zl5^Tsz>Yz~*l7YIdSiBu+6 zC{=2WmbQ+rp1y&hk+F%XnP2?+URqh(*aF`4Y>E8O&#I;yre!;>=LcaFCux@R@Uunw zXDj;8mh!)<};EkR?+YZ%IOjjBH1Hd2&mF(~wj$3shyD4cIOwX!AY zuUaM+b}VqEk0iQqpbh~i(LfC&%8;M}dT>`(^FJIwdf|Km!h(fz!3_hL68bC&DG);s z6?ILORJCeppHxZoq!Hcie9CD9NVTE3Gy0b=GmjqHO6|xOKovSb0SY>(2b~C2RAnjv zL+p_JTmW*dO4z5a&fb&(cz+{hNS9~q~ zy(RQ3gGGJe+1LUg1S2Sh6C_15EXNC?BrB?>8>VGDuIC3~q?PuZJ~#G>Yr)%uvQo1;(>`t{=kNsBhx>7bJ?y6L&TxxKr8cuc0!nJ}A+@`Yll zT&dRTjbrSMsbp6c~Mq%(`t9Rz5ZZ08ixQtESTQ@t~c92P{>CtT)0Y;D$!vF+8K_R z*U=jd#QhylBva{3HkU6HOW1tUtu_R43V12qg9mqS%<~Xjxpn`YdvuBbfwsH-;dnY< zuDAQ+`FekF+T=&3Pv)Wpz#;b$U9MDX^+vPR?sR*7OnwOx1!fan1fQ{qYVeX`6Y-oP zJ~dGWz9@EyBJpLN7+-BtBn9s=s!0{>#8w+gpe8#Gh}yU;4{c@~$^9l{s9ER+jHcZ{ zrFDh=Lpz|KwI5zUx*^ecHyWFfgxN?(TbYq34%m&Y=q3zXGgW`MYn7s#Cg0spxpiia z(>do8RYtQ}2;8?{R3*+JWo0PZEW}TD3&)U`%%kd80dnYU+D?-RXq( z=}2xVtE6o~Lv*CO6|`!@9iYg{vEvRnvdAhWPp3WC#!ejwm*Fu+VqAKMbRExf>w@)Sv zHC-8xe$ZN9uqv#u#HvYQ^TXgaLuc0=sUGvL*`NfwS&qJEThj0{{R@DP@#W zN-3k1GR7EXj4?_N!@z{AJd1$|H@NO`mDdY?E8QkiN-5P85U#3JL|q*S<&M-nib1%_ zvlv*!7-Oup)>=EeU|_;kUceY*j4>v4N6&duIWKxis%C*M+og%U^9Oska68 zF(}jnN#P=pB(8Y%gPKg7;{M#Xs%#VbI5^Voe#EcC6S-}0Lv{Q*2H4HQZl2Z=Z-zqj z&DkxnT34yw;JiY$m8IDgQ&KG}suCx&bi^Z_Us=%;G{uyiFbJqO054iQA~?#L1cE!l zsQoa3GXW;WQrDM#Fe)648yrGpc}{Qu0F52v#uKsvOOF+<^vtLI%1g(UN_%GQu2hbj z(m4lLsHwY651A~`mp4i)uFo9S{NihLhBX??Rwn`faw*a)Pyn2zf zU4t>k7|uE8eB8BK>$)XEZ)tC>-Wrn>FoCkL=+215@kb1_8zU0l_6?Gbn(}nRgfDUb zh>cob3HHa@D;Mz+_)PPF!`D#<<y}fb*i`2M8l!kJqt55Cq3Hf#`ZU)sdeOP{xJh z{+EBYOvv>vPTu^JJ2?lIQ*Xve^FmCk#=h`MFqo$qJP8!%@hCadi)EFIcM1&VX$DUM z#d$nRkc-N`|EqLBggqlnfHE$eb#qDGbbs0O_SI0a5<&G{hLI2O z^bBiAFY)Lhw&;RA(rB#zU(MG=!+!Xnf%_rzOohKmJ{jFV=CAeYL*sKMC-Ht&TuY*0rFCtBl%qibr3i#>wsnt zt6tlJ(QtGy4jl4BwOr|bF2sy$Dp~9yDolao_mU~j42A$Fw~~?~BVC0VQOyYoWE`oD zb9j1LXTales!YR@9sc)!a<8=I;y-+UV-#U5f7oYXx7W{iIS93WH~Li@Lp*j=_bP~B z^lzccu3%=6p5Y3CvDPvYlNQz2MwjoxUsd@kL)cxbDGHm$D}>dM3MBZB~;5V z4t#j|sv2X-38OuVgF^!W000c*Z(-Xcy;9)vjUJ@)^uZUc&@4WaAFw1GBEA} z!X`mN8Y0>Q6k82Bd!a$(D`*HnrNyI@`Z}=E#;leRYnI(^HxQ*lplPxXI|I@^)Z0Ln zE~#XQVNS}8C)(f%>{V9@gQ0j0q^t~T5TZdB*7fV~7*1%UmGn}!(@Ofr@31CQI^S`= z_&`NREf?=q6k2?6OuBel-(?GwLp&46ai9Eik$AHAINkre+5z$rpAbh*{wdrBeBuZ5 z>qhZ*vG@$O2mSs&JWbPS+dAuC?A(tii#EiC<4UDH^N;@{4p)X=cfB*{wOmf)m(cfZl_(+?c=r;%&nf~8W4he(%f=P>3h8wbq@Pb*_(T904&BDTGSQ>AUM9+;*Et)q_`@fgNwbVW^3+}P$?E~1CxqLT3#wCy#i7_>+&pG zMRQgoN@`?Cd;qU|X`qWvoDr8!Oj4IFw!u5bpunwKwlYX+Yox3x<%EQB+dqFe0*tz+K+lR=->aVJ5Zv^2R8^lOWd<{Re=LJ2K}I*|-0%LPz; z<6*>7TZ?6G$pn1s*jGuziQ&oznGjy$Qn-eA|22`DtJ#v0;t^PF3jmYbI3MzzoWiKe zDG0<2w}C^@4s6Wc+6hmwA#evCx$OXaZ@7ycf*rk!AA%oRyCV3o0@T=t?*#6f=!xhV z;^+*OZQOn=tMJ*UVTwg{=(6?0w&lCK_j{v%HJ;Ou=)#ww%=q``voA7Qxm9k2ES%FVXJ5u3EaOMB{!M~c{WXA?|J&LQ z0;|^(|IDw(Cu+w?Y4|V;n(dMBFol}`_diR{K_Y?IMqw$y0ayY=*45nqj$!gQsy9#e x|09Km#@T1D|Gz_#*H)B&-v##{i<{XNR?`#SF=ATXDh~aT{_FWt-NU~E006ch4G91M literal 0 HcmV?d00001 diff --git a/pr/234/docs/static.files/FiraSans-Italic-81dc35de.woff2 b/pr/234/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/234/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt b/pr/234/docs/static.files/FiraSans-LICENSE-05ab6dbd.txt new file mode 100644 index 000000000..d7e9c149b --- /dev/null +++ b/pr/234/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/234/docs/static.files/FiraSans-Medium-e1aa3f0a.woff2 b/pr/234/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/234/docs/static.files/FiraSans-MediumItalic-ccf7e434.woff2 b/pr/234/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/234/docs/static.files/FiraSans-Regular-0fe48ade.woff2 b/pr/234/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`> z0~ME!G#q{LWV-3hr+>Exljx+IeN@slf|a@R@cCk0iKIc4zn?(vO0383WncW8->SPT6E16E!KO z?-NXec9g@DW0aien&-;CoTX%!^LP-n&l8N-K5;A>Ri!G7QI)4M+t*;qz`L zH4LdWNvQOMQL!b)jDbKo)s^`qet^0sjr=X$qC<+8!^kYoZzRn|3mB?i?*@66hpyd4 zHa|`lulkw7o>^+#Sx3MXOKn}}y?mgQF-bO!<#w31^f(p!Mzzr4)8x^r$@XZGYSgJ> z_w)>+Bo2^M-fjBTg#M(>GtCE8Pr7&5#j7MkmER4Y;5(XFsG}$A*dMc>dX+6s)iBu6qx!m_5LIQANh1Q5P()xl2zr`%^wMEIL*g=pkfhKwF7G-9AfY8v+Fty9>$S z?u^+(TZg#AJpK!E1B5wk{-}ZGRav_@_}f=8mEwmA^tvNLbPLW+v$LmkQSC=veA%Bw z9o6#1l9Q{PEJR&z%a?4TuE%8&RRNS=E}9)a;aJK^^8>vsYP;N!xlW`T<}BIl=D#O7 zTE<6doK($;_oFUh$x=o2wW!g=WM<(gACi@6bw9{N4u=PZm$>XAmfhPO%jo~({JM;I z|9Iu9AIg1=ZlYt*6P@%aUni9}`jDtgUQTm6p4DIX2@M6l%d)*L4EC0la&s$I#Y(-t z8)(~)=(OShOEK$r8J9N_9viZwU=)go=(3c*;-I(t({0++rInp zWe>gHa;j{FSAn2oLU7mROOCR`G$Fb!k2~XPYgh-b;}Bh!dG?S96VLMOmzAnGvd!r_ zziWGK9i8n8wp5Qhyi84xG1>LJggm>@m*9?AzF%|e!bR`IzQZ{SntI$NEMKC*9p$o< zZoc)`Nf_+a`;gC*UB&!ky=5ir_LM@CW@bNHk1x#TYy-vA&nw|+cGj3M#*G&J1PA#% z?h=@J{VpsG>-^OMJ&$Vf5wLA}UZkNxh3k(g%fb!ZMRI?`Ni;koDaCE*bzPeS7ey)N-8 zIA_B(7RZ)&ycj%8f(3$1T@S>}YRJzzylyvwVAm|LUYWSkt(@hwD0tgjM;*oTfSlx` zZ9(pd*H!1{Nm3{OA7%(4zeqW8Y@bBX0R9$q7MJuho#ogVteG2ovBRJdSan;<@P{C~jW?7Yj|)(K1^%Q85~?;?vm{CFG|cT3p0xdaj1xkrx!+O2Nnv#A>l@ST>p=b+=u-J<<2r z6;flcnAsb(i(f!Hq6=jG#s~KYKPV{L)S|`UqRk^$v`nmsj^Oljyuux3e4bqjvTus0 zmL|tl*NY>5*F)4L99J*%LsQX3oz8Lfu{l<86e*zu0k4n{J*Yg(Q=)PTrZw70jq}W& zcxSs%id%{mWW0m17VTgM3pe(s=@so(7|ND*6rYbNSNM1sF-i{$p+|ajir~AuQTiQT z)a7Q)Iu|1^kw=|9uItN^EuN+J8LYJ?2xld9wtK!A{QJ$a$YiO?s19E}@=>7y*jYC- zGpF$CSW)jeXGS>g%#I6!dN#p8|cUsM`|C)~0(|lS3QG_B1ILE=zthrVr=j>tI9!PVrrs*QTeoE zpT^U6LD`*_c&^Gx0i2YpLm9#o ztBqAg`^9-rY3D#rkY(L^ceJ{`!B}na*DEm(wtY2MX;fv1qTse!c>}RL!&N)XCB}uj z?vg$EvPz&z3Y{5sfoytMb0DOtPAQhRaYru0@C!e@&n__0a4zLH{9X#LO+Yy$L&+BK zpjTN@Xw75wzve|QFI&?PbejG+gJ8e!!44ovhC)t8w3g>(!sawbu1G? ztx?e{pc7yVg3|6vgJ_Xl6BORMnJtctv=93V90<#k8);$&U~|8{qtvCW9})nFnd;R0 zGGH7G=u2?Ypbz`YFaY5eZZJp?Il9a}s`%p?_V-6`S6^dpL$pgl{{{O%a;p`)5)j7u zU<$prlXxA583l7?e-wJfryI}1B<#J#*onefrY(Bt>xu}fgf*Ow8c{IO37W!xS!KBz zv@$%>0MrKupvtp?B9~f5RTWA<<<-i1!doE@2B?rW(A}mPN>@z!OyqR$KBBURS@2uS z8~M7%AhO7o^dUl*KFfI~x7nNLDCIrJ>Xo?Ed!fV{tv@vdeKsutpQQm@Pow^k2h{uj zbkV9DVeQHR znaUfW&oSBm<+Ydlp;OM!p3$LF!bJ=MQhpreB=1SJ(uik981d^6=kWzeW|Kl-{W?BY zf{AC(4?(LDIXa6$0=GMaLCTPc1fnz`ksSbOhgbS@DIg$sqJ~<0&()a?Ke=!B>_jQ^ zUXe$h2GDNhcT*SzpN)NX4C1fCydP#^!6f^KH>y~?s_gS#ieek(D9CvLT8vTrA3BXT zl1lAt!^8)~5Fi@Uc-F;mJK<`~VDFGT^L&5hUqJ{a)+!l^#Hlqpal}mQRei&ua>P0| zuG|+Xqul{Aso%31gkp2M_r>!Y&g3szbVJo+#8Kdp287V>S(=`i>C6To=^EbO<*Wy5 z?@TRhMn*rS$xznEE0SuBF?+81A@Mu*cpjt~FanGk>sHIh$>?L#vzAmB&+ICGOM&&b zXx6Y9;;oFv>Da}_7hRk~P`g<$rb;0g`Z@_Q%DmJhG0ThYGep}VW`9r>ETg|qtS~3b zbMKTyFku#^-sOCKHrAHQgciR$B8i6wd`_kEuC7u(dtaoifJh2*h67Qc_4usIZe`_V z&$v7!PZ6*sGK$%F9S^rAuDiD|av*e+O}vRXM3zUH0tS9bfahGX<02kd=JO0R+dU%+ z{>$1~JWdNK8#2xseYFd6c(k@FUM;ad~ay88xdphQOh7M&Qkq%^v*K!6ZCd^Q4T2G_B+ksK30v zg0#q11h95{7m1e}OX@g7u0rgQ$cPN~i{S!EN97@gk`TUNj^&CXbq&gk1R^~$D|6Ud z(XT+Bx}nJ*iNVz-Cpx6}lu_G}irdJJZaUDR{_DXIto7AsCfj6vCTy+hT zQ1>o8(TLa7%_L+9Hx*S`RUcdzSrR+N=^=9z_^8o^7qW>eRyY=&=0p5kS=qQ;(H_^? zlIR-KnH6*)Yb1yQOy_)E|jSes}ol%V)Xfl5Lsw%m#n$N|=bkX=Sm< zee#z)d%l%q=TUJ|)a}iFb@=uQpToU9qumFtnv#iu?UFHU5-H-t9CUu_D6Av@mjbO-Q6*!6T}N_7t6I~kuMs&Qq-G{ zvqEdz#)vD6U0W(6wIZh$S#dB0aPhIXNSJkyFB(~(5f0pA4p2Dw= ze4hIqV~$I>D2kk;u-tTlo01Yt3(yT-MYk$6-RLB<6d?HMZQwUgIla><)^~fRC=$#& zC__e=ZapEp^4w7Ju}AEWb4;ZI*18ajuDNc9^$UrgioFs% z2|uGmC`hlIRo3`3=5q?Ki9JJTPaVvM&(OAeM8PRz!Gr~faOGwE(!hZbodnkIwP8*r zi%@;Yx^c`=XlU3QY3COyn;$z>=HcYL&9o)>0<7}6dpgHR*mAoA7zFC7wF>83MP^9B zexa8$^5=O?Z*0d;Ruk+HH^#n2N}e>p(TerB-I8*~jxt27Ohl(>Uh^&c03b94ySeOD zj=G?wWMJ1yoR$OI6^fQ;J^UFnh0Pqq@W*~E4+2vq{qonAi*+|SEAr$@ zS6SI7s6%~}7g}##N-G6}IF{D#g?TQmo|>fvnCYNDn&YhQG|dvgN^jJ~_3$2=qiD}R zin(=4f|M8_%}O9OlH0nbDQ#OORy=>cVoz06;(YPy}J9mS8RXJ)99x>avHBH9BHd%AdM_}-&s-6uQ2A6}yc2#|O ziCxf)5+0ta49x#xj!4CnNqBbK&f&CeIM3*I)hiO~ijifmG*ONu$j%4OnS!7w_}6BMQ*&kW)4 zffa6*&GQdjjQPz-yw)^mz}#@EbKxhp_0N?^=)CG;PPM<)Hu0H@GQlZR%Cp;X z#n8kButP%oA$aoM)}B1YdB!h{=^u<=qsKlbo=)AEcD}i5+Po?&qcm3il6{c7)SN4Ok>j&t17dPs}*h^?YdI>@T!*`0)`4%iQQW_SQqAafINlKD%62+3TIh# z_7Y@V@)mGHKW~GdxM7WRVFfHdt#H%&`hBoE-kIK=EQN4@b zvu9wcrCX`f)j9cJG}I@_d%hg zBm4SQs3VBa@HD3E_cP(apM^6yr@Sk3W_|xzO_?s9Lo=HOOivw^({8PCv0g6vK{|I{ z_XR7}y%>HRQE_o8=^m3?F6m1a=x7q{i1o;0UUwjRUiZ50*Shz0-`9O!_iKIEkO0y6 zHSu=_jL?Mj11CvPoXW(fTyg0@QG4G{tq4|3^wi zV<$L?4UK5L%AQ?n2xwhQRhG%@P29z68B5|sg;H`@wj{cq#7YTkwZH3^(3)o5txc|K zg-fj2L`svP%6;-NOr@$&jzf%_OiHTtKFdxC3f`u!-%M4y-q!!f^HY}mHI>#lPLZET zhxV-ZGz7C4uaM@C6HO8SIA{zO2fZ#F}ay7J1G#6W`kQMdi)`k+Xu; zwP>kK;Ki)$&zf=#^BAet`)pn8!#vIObzWB(*T@dvzlrRyzC*FcsC-4WiM<@WCms6> z&M4b(`_Z(L8@_zy)`@0){n?QQzZM$@IONAkvG)0m5#Vosa~kzzk#sXa7hK8V6l0?P z_uyaBtrJyjN38K?B8t`zMR{GN%n@H0OS0omY&XqdYNyn*-d5SxtCP`)v%rjGQcF-5 zsfpZup=fi=Tc&j+8sO0p`ce_=+=gA+hu;F(K(J zF&LJtT-XUOml-DkYxHs$`I&wCICdhB`GQ!djC-Sqw`*}A`?z)lF$2)96LoAyth1HZ zR$L*IVdA=>d-WDb1Jf^gFo4Vc~YA0F&wYlWLi<7oU9Yre60R(8t3U)jGZ=_w*X5 zrc69I5>$ST_6WpdOke--kia#!?3}~ob(8Nj>n|NDwbU2d^~HtW##VSuR!hT{TyTY5 zrFeprA~6u5hO3r+8=S4=I?q0Yp&VczhLY6IO=xC6T!`H>xIZ^FgzZ}^ABLcug{GFH z3#>?b?emfutyITV@*g4*&Xpz~#RwKm69y@-Bzq+&P~P*4)Vy8e>LgYU(=&8jcsQ|& zUnUfKw{guY^H~a5=j=D)3IciQZQo_b;x-F*Xjkj=x2-p`uU%M|>sTKj{K8sYuVZay zk1p1MVnu&H*kMPHdt*9_Hz8v(yF%fGKiZ2gOlxDUQYVk@FW89ldcmT?_@~t`y`_xO z4J(BPZZg`bsCb#5Db~wE+O+HSFQEtcMKsLl-?#Ow>tr8^Q({ds4h@9d0f-W`ue0qW z99~k_8zC+@TT7UZ;%|!%8NreQJ@>SBNXUAar!o+Wp4$zcjBGx4?gKYi( z#V6!bA-KyrR`ZaZnqfi%2S3QjPwSq_pD-UfJ&nr*2xLdgC{dca0sOV+xW1_?fGe^q zx#1j56%3qWgmUo;OFyz?rKwDSV~~NSwM-o!3s5-WRII-30@;NkCfhGvJ$=FzY01r9 zC>(C0Te3Q>GRE$SN>$OgE?GG&Zv89{5nhzAran@QL&0K^GAqA?pbv}0odU#?RCBwT!og+{>l$<*qq@$7`U z+*CjhglJ$u03=O(Y0#Zxf32J79~WCEg+4%=J0t$3cq z&v1ml<2lXSEqGbBmN6d-1uMFCS+Qd#nEBWVrT`4e7?(o#@>*Y`$YQ=;{Vh8_HVm)1 zH(s@9`)d0rzmBZrsq2Vo-TR!u4)@5>y(rV~J&2_Hnb=T_hG-AKit&)b$hktCQX&;b zld(k3y7gjlLqlbCP?zkdptmx+o(4KyfPcj{qE_UE1+_A}o*l?~k|gZj{$@_OW=@$z zp=e^eUQN!i2V-NvEl+^UfcgWmT_@AklRK{SNo(Sm}; zS7*We7Gs11qbUIF_KfsFDFFq}_tTtZ(LDJgm%kMgy)Q(snnJ9K;1`ckl1Ag0>a;m# zwVTo?`E$U7dYLD$MnD~hF^_|>y zH%iu^&?2w<3z}@rU1EvSZ)VKXabuKXz&X4zd(>&)b&DTel+=R(@Q(Plf|q#SFln_^ zatGd#s3`7aC>9SBkH#EE#kJGS9$GaJav5$DpTs#v>SI{KTJyLFEFKb*Rz@hY|BH*1 z{9hQmJc+_m>1?ojIoya*I`QKmX-M9SCFQ3U9@a^dD_Go5buWpQs_lsRW^9b!v`Lpu zyZ_VDS$$avKeoE2)=hI0&Y%9pg)A}vu*sHuvo4`78bA=sozv$3z5gqk9dM>e;{Qz- z;feWY^4Vlbfd7qG%jS|~mH+pqSMK1c0zy0QGvPI-D*eS>L5qOcAHOY~26kE0L21rr z{iJGqxU4snU^Ff1R5~^#fCTl%Sh>n)CgRZgUxIl0NE|eRBe4>%&c6-~oL21nk`aOC zP!v+Xf@g~+5HWtZuY6gA;?W#wiC-rGxS9o}qY3b{`>y3V=E2v@v5vq ztm^yJL97({!qTM|I6qm-z4Vq6BEQW5T5O>Qb&tOhwWJh9Kd4jgKX(+HhPTm2SMJY) z%7oYkk$74=KmzBSk?0^SHoD%r$DnM(7PMiQMjaDj%oW1N4%5AIdJr78BFz=ZwPmi? z5=3^9^@;`G^xg-v9n0QD2IZ_L39@KaYPG7$T)F!l7-#Pu?>p+@)@O*U1}s_8pE8 zVnNrfd}4)3m$Bi-;Q|bx_t1pWKB}vkV)Hm;PZYH2xS)hU$rv988WwoWcL~H)K!grM zPf}T{NP>N^S2F;T->aFth_Y%-zGK8Y$7VXl9M)vW27_eBZEUop z7L|V+fNLa{ZCPhehoD!`&-+lgb_@lzSr`9?Wmb6D@u6bP$DpDdg79&c^IK)~(wV&# zM8bNvcZz*FJd6_aXtgPm)zV!js1bxU z%(zpW*AE?m01~EDkmXm}B)^QcMj8GlBDS$D_jx{emgrNxTg+{;{(FAc0YliBC};@8@5G0nR$1f z^xY+Rs8t^m`z?W47V|`Rzf14N8y-N`d?=`*d!E@(Ex5vy-rT2E&mHcT8MB^JuA22} z+faOxgUNS8niAByt}YRT+JjZv{K^WmAuxxgCONe!F#@2fO~Osb)*k`sUmZo)_`L1X zAETtrzkXzi6I2r}kU*N-(VtzmXAwllgm5uUd@up zNbOxu3?LHxNZCL1t_`mLO)Ba!Y&z7)nSi#AAYqK+?C!gsxp&BV$^bw^j$u9rE~v(< z)`=fRML2VfWY6V|6z7p2oyVcolbrUu)7B6^amFF4e>||Uu3mR?G|1dz7@F2XPJYX_ zQknLJ!J}^CWSA5pYt;2GyUfMY@G?AKrdMFD0Y2p;mL;6r=mAphb-8Jvv$r&AwKvyT zQ>5zaJ(f1Ae@XoKl|8%%z%74ioviBU%5)vN^0uly@7=m-_6I{vW4-&d0^0VxsVZ%F z^`p*9PxK~y-eipyPcJd(~17d(ZW zOztFBM}gfC8H6_nEVWZSTvSB zqh(9)9TBUS=yU>H!fWWd{i>5?rxBYt80b!d+F#MC%F=URLJbDMZsT6za(dv9^lPv& zZo7apoC~hxZI?ktC@6OX%Nq394m|IEN-bO4gqhGbAUYv+sfkgoWT--&f_9j&lI&Xv zmR7=Br;(FinKKsO(eVlBGW;$mbdtTZmaYSXV;0l_){r%r00wC=@K%3eURjFl%ixp& z0GSSMoux-eUibxzBhsTi(qoBPyD8QBdfAusB@NcH$9_&{q_j(ivjZ@CkrQdhj#0WS z?N5HF^>bDKtaP4>FxCh3Ho?GKBZZk{m@}JNY$?iL8!Mfe)p8+`iqBy_XgJ`H=MZg% zTJ)h>|AuQRlMN_NWM*`7q&RH%Xwf_nFH*jqw@~%j*p5`&55`>4y1tQ&cEJ8wd9vbM z!yfi0iZTdayeO>X2!rnwin+=6ia**zRxN80?oD~Z<>v@J50?QJ-!B4U~ip%NxGuY=ifQcsRWgo~Lg{YB=<1lgv zbPldkM3r68I*G%^Ub|e|It=Z=sG26UWDtEu!8DD?O^k)|W48Zp$I5TLcCXMz$G%@k zY^3l^9pmA^thg!&5-oC|MJ0v$9%RD4+Vpt*o+-5zo%NA^^tHVg^<6L9eILq=#Z-Zf zT`)On*VE8vf`M&=t!S&VPo#N&beZu*Mg;e$_5wTybO*Jvr3@& zF$`!QEY`HxqRSSY>?&}to=%brPJ3mymscD77j%5iIWD8Qr;k(fr(-WSrbHkr9w_JbX<{KbVcGLG3gUt z_hmV}4X>=%i|O9SQW(8_94B8A57W=zSnBgEladcyk@aAh0pL?Y|JMM5H2Ffh_reR- zSLPDhW;Wmj@b&V)@HW1_eBo|82=15@rHyRP<`R_myxC#UOHAiHRx7>P0e1^Y>s$2GmUrw~@ok5?TYwGsA z2py&Q+z0o102Bg+;s!n zZSK^(V+kO%d_`$Ko2X=#AgB-a;o-AT@}(I;Q0jCC5NBmcLr>q3 z8Bh{cU4vRQKjF*1u;A%uzKgkK@XMbpGNC@j8i2`ZaL>S=NYx2;5Nt5y4I99|Q` z1S#mJ-T5s@AVlRZvtaq8-1pL1U3Zu-wy=<4k2%~!DS(Lgd$Zu{3%Ph;!sX_?qHlT{ zFiu<8$PJ&yLgd1@F3Cawd~ z3{l>-EE$Ik?KR)mQx8+GNA-Dad9a;t+QW|GC0k#dJ|~~{-Ce^U|AIY9S{}i9`W`aS zjX=Q3{vKRZ=Kb$xEyW47aMA*BV)`D(8*Gci8J}IsN2gGpW^*ISUY)fo-ns_PM_aP+ zkOMZJAQ{EC-m+&~t(^jJr3CwTQu=AxBW1A_p*gw>;a8F!JR6Mw{Vh6GHl2RZJY-2B z71WwJ2HuR@JDPWJ)(IR8{4yBb9E$DmqpGF+zA<`Gcx@jlu4+`S>RILU3HZV)wO!`YmDd)85`pwh<~{BFZ+8asj|m~G zKVP%9VP0pK2c~8g{YWeRE){ONObQo~WiZW$rfS91$sBqul9$$tO2U|Azgoz-PA){0 zzT($g5z(7EOg)8V2yvFbyaP(XEE&_)yCTeY@JG!2?5KV8bsPzfzpBrNXXBUg{pmvX ztT&kA#)sboQPQ3|DhHuN!b^oEM-S0(g6oN$Ehs3PM~jpK--x;h1Byb31;YzaStH4h zjv;6%y52teh3V*?^4qkKuF#?+480DN&7g6#Bmg#U##IjR?p2sFexr&?^w5o*5H!*( zOK~Lg344eO+}l_U^m6g@kB8ZZx(W@r4f)Wth4a$6Ser(FB(1t%Jo*Ton`ds;#tMheiMI(=lM@TouRwNylySoCOy~ad`#J(ty({mAu;>KxD+3>%Z+*hbSNe<)} zpkShcn#|A*3|iOrW9#m;LPO;Q4i|wuAar+_yg~O`rJ}82t&n?+mMUm2qsGP`wnY6Q zAKt4aC%Z%1@AfRCNTWI50Sr{WhtY?p7bB| ze`(6tHlq_Vt5Qg|EM*niiYj#%*I`5PUe2U)^(ge(;ZV_@{^x$IOEK4vr)`P##Mjiv zZNSREXxR>QwHR%KgGOPzI^=7Ivr_CQZ$|X@W|aKb9%Fes)HOlxYJ5@WKp}C`%5zI0 z;BRVV0Cr7LI3#6NJ~KMB#5KR8Wz7Ibn8HYOF6mAZheLcVQ}-NKf7va!WpQSx`JS=O znc7T)lH~}W0)ymlvUa?s4Qq4qhqY>A^{w&?pS(fG-~WSr#qXK}e_)>m@%s=kBlwkM z5(Pw=4~aNQQWLiscDCME!Wtrho>&8JovgbQDZF6=Fod~_1#^l#H)b3v`Hx3{{QZ^( zHo(_}kBkjK_z7EJ?7&GrK_GQTl&Y2hoziL9aCxh3b6{~)99FYLGq0V~v^pYCiVxYV z8fetm2R`P!uoI1LUjFs^>a_TO{{N4Vbgs{DU%Zm1iq4Ew)lx!Exv8x3Pgi92k<#D) zl#n>#GtQ!8&KL>F9GInS!oy_k18T!BNbVGOkud5Ci~*qA%+-3T~|D}Twv*Uw%wxXGbdQYoM_k;Z#dd9>{ zkbQ2XtRs|VgZD8j8aPSHVGa11G$u3kABGxLavQJ$Rng5!csy~ANzE0>o=X4u`_^U4 zjHClDE1#J1gJS_`y(bQ{Frdbw7t3eR$bTTK(@|Xfk;j-uq4+A$hB;~c**gx5pk959by=2;;rbXKlAqDt9kjz>Z!`!)j&^&B*ELLVjjW zEeKC(K9mU$4LU`nUT|S%m#D+%Slwi64VlNZ+^^W2u>puScgdFVzn}j5?|=OLZ-4#s z@BWt$8~o*mZ@#L06V(plmt*%LC`6oI^T?ei?}n6bX2`uh(fNceSCxbRxS{y|FR8!V z>^`?R&nb4ywhZ?0FAh6kyB~e*ZO?nk{kFQ%HC9{Z3iHf1!vx)$s2~3G%M~jFIUM^k ziM0>*)duT?0Y=M`R7${)3?e5?%z$SbZk@Pvt*TDlq~lQrcix#gA1%p0H3~;6{kK1E zTppJKMULD;Je`rzm!)k`0)){Iq2G0};}<^qVl|Z`<0W=KKYUC^Rrwb7`td&^Gz3)Q zgSOSh9!_2KL8M5~$tGkp;j}ePXJ?eod_5DO4=r#Si-LjOY{#$?d6f{x<#OU+h{!U+ z=MG+H3>sH>2gsA&@1hKBHs#u}ds6X7y3veefw~#5%9-GyMR>6sDN{tS+tm64a78R_ zJq>rG_t8S`q>J$1-vuz0sNdOY-xAmWBl1 zLl=-Ky#W>MXgPKxCQzT|4;tHk9_d2X0lNT$XM_N3f~k|TiZ7cT6{>1!q9r>p-89d- zlX}VEMDnI#-nZn7vmjYw*J@c}@EK~pT2fywH*=VM1XNES^kyTM{3FSR-opYSQR+kv zXs{#IO6}<-#D>{pU}0prnemXvkhUZzg61L)$l^q-Osp+8O+ zGDQEhqwAT4s0w_*H_3SPGgW3vFmc?!C09!Zy@@X!qh7rcdg;4PRzZ)kC2yPEfFp+c zIY+Ue6dC|Y7?#jumdDg-7nD&k`RJnDp2k8ypNsnpWrv`bIx_zZZj$`X>obYZyZ}J> zqN&?WDH$LWmB%yaGuh+DA)ZejC2QM{9<;_Haa(y*R)1g3+<57IVn5rYp+dqQ22BC~ z<@@HYrJB7WkgCa)f!{t+G)+ml(0giPess3IpU>u~=N=_tLZ*)=#Sfj`W4|xr2&9rq z`h^kDw&zQO(BaR9-j)TjAxpe?NgGbs+h@o@$NHQma7JdfiAXY*iRgk^_R&+kl$+g| z8&Uh-J;xyAqk(mr_;Mge#)p3fgG2agGx3R3(Ev;h5P&i$xDAr_{_n>dd&2lS8p|2i zaZxc#jy(I(B>?xfbpb;lW$hV4$7m*laV6ECiXloJ#-$9%{}D?a%Jbgsg9qey9i2a} z84xvzVyqC#Z!FDuJk;;0~LxNKchMhKDDi#W%E6oJ;ISO-7y?2L{vI!Tq0*SU) z*c?^yN-r*5_9689k_N(N3C66dp?=?TZeV4zP`EwHIK+L5pNz&vhND7WD;C=+j6k!p z^4(U@g1a+d8Xve-PIM{(beSmy+-YLkae`P@ zx8vjhqT??YWhROA_hnOd)N$HNcR8N1x;x94dv9%?KyHQy#bmwK-_lawzD1+mb!$Ji z@rQg1v4L)HlkV}+bN7pC)GEeUtR24WLEEcc-yEBDF8W-zJ3v@Q0Kh-Pkc~ut<$(g? zxDTcy`B3;7H+P#mBA~!^JIRa+&ID6W68B{z7Pp#bgEP9Hf?BY`=`#de=to~1=ELod=-s!xNiIXTS?lz{eRrL46H&*_CV(=xeT zWtT51XZv9tCe<5&<@3wwem{TgHEe`(CNwv3nXR?1I*o~`1V!N?<1?Rayc2w@?K(>5 ztjLNE?6`}hbFX~|EHs>1jR%U;D2VfRT_B4-z3Kh@_k?~z^z^n;PGZoAP^4W@Qkw|^ zOQpqR5*Sr`&j`SF$c$$N=dVaROlgmRDKvQJU6tv`cGvGW4}H7ZrxT(%aSMP-j&7i$ z+`S;RH-G3)6+N+#lc!Lp|9TyxVnKq|89s!I@|j6JxCasP!wb^VHM2xF=*aLJbbESR zs${RX-J{SbJHi39vE}%~eNrA>Mz#MuG)|fVI0Wte5hJmBk4+689HdK?(>~?Ew6v#l zDnFI5)(-ZYRpQM2&bEAB#E|=JM5{Jw)9P4@SL1?G^IPKDf=u(UN7$uy;jD zn$?A%4!nBCwW_t#1+uKfYdEWx;!={25$P~gvvBLCjAJ|9j_du-DOanSjW=xXt5;=! zz)u)cin>}ffTH3|wLB<|!^(JX)jLQMd@&Aw&6&T(Rc=H0EKLg4STSGHn91SGac1?7 zRrFK2zvoNGMAhjzjymn zA*uI6f5*n_q1hELprk1xnuKSZw+Q_HL(L3Ilgq4Jn8JyNwG}f6o&(>g5P;BC?mo<> zq`R%&IoT?o;aN`NhMKdJ5$8P8?eNS@dyH8IJRz35|NGGiCWhH)g>Dt{q_dTVI8ER} z+>Mxsguub?_|<&xDuYqqx2~>kQqMZ7bXZPSk|u>mfd`F%qf1oI|8N_S5l?y89jzMG zsZyatk(;t)NR=o?gv~bmhUB(y2@o*ZYhVQp6C>i!iY{6zvhdFTkHh-kS2f3UTFAd9N$du7BqqfD%xbe_Q4_a z+k^dNKWux2um)r})PrO-u2%$GPH7!mD_`L9v3Ch(Gr20=I6JAhT!>|9+elVqS#C7z z?=eC1yDeHxoA@w#I20Kps-aLV zsxdB4A8#XCIY;fU%J+{ATSB3OB~8CvI&R&$PdM3?<(!g1M_X$HM(in5Km@f-GW$i4s6hR!&nDf?MIbB4sF2%uLv=~H~w>-N^l!11>c~0m~ zm9J-I2g^Pr$HCUZez5u|Zk0~q$X*PQ%>0Df<~;pJjE~1pU%=2tQUog#!w|l$Z6}r$ zcO7CJZiNkD7$y8+*aYwjK7H$YQ_jLH)M9gJPMlwhz1S^tWDL*hHG>=X>#1ji2`};9 z6>ZybQbRqyv%z!{*)u$Ee@g@5^)Ol=XL30zReXO)3Uj?w9sah@Ntvd zKU3Qu(%S#(0l%!k|Ie#R?b#WfYBb564KcM!=f96BaM$%~_5yvCLSWRpZYb{Ku9s|K z#uRw=nTxRv0n^Utt{F^J2Y{3wUxyY>Mw&%T1M*NUWM+Em~>0KB`WVBQ86YD#jm`j+l~?JhI7qT zj7Zem_sVXj%=>XP&Q3$Nx$yc-O@*f?Bi$3#9S6dRhWCbc=4bBeoG;b%{3tvkk*&?51D>>bPUw!SuV;mcu*L0K z+9cX8F|YnPeKMyhwi4?r*%K_`<5Jj7qkrJK@_BB;Big=Q${RQhL-{`#-@7*`(QJ@xkcJYMVo!wpG2*PG#|duXath%Cbj&2$no+X83-! z!IB4AuuT&|2NDQd%*%LGw*G?k!P{x% zB&V12?5)$|ssS=7!^fU7mPAm`iF!s1=`AKwYodM8*?s3G>BJh+AC0_1$?PM-dcW|1 z!ssK@=Gin+892>1&k5b}tJHr;KRDQ!u=>y3B2WLp;br%UOb5fi%-``oK?A4%J%cT4 z>h;R?Trk*8AUnMG2&3&jRbNqAR{EnYC4J>dNNz>R?ytv%p^~}2b(M0lhxU)^z-Xum z>e|z|(`k{xKje;(nF_&@;ZBB4=Wy{TGBk9vlF?KK{T$H#cCP%CF}WjcGe5^yIz3d& zRw!$$u^c&)y*MuEDOS^up!=vhDK>A9S0uB~X_~P)X3>w1CN+x)fyrbu;gG^?+hpky zcH0bHd@H5BI5f_;awHsrO=r*6mN~`7Bkkz2z%%hpq29)PU5kKl4uTajbD52ln4+F- z1r*jqzp~@lJ`+eOm_v_vZDS5$Ofo6{hPlTc2JWYFdx)C0nR^1Sa6VH?_Vw;+xM#=h z9}^VjYPh6VZ>1qD^|U$#cD1dHi~TcYI&^I;HU#VOJ&KoZXEUSk4;bl417Rv+-Bz`c zusjz)%RHxZ#L8YVX+a*TD<4~g9g3RPe0AQ8aKjjJ7gDL3zLJ^A;vhDnN|J5NWca8W z*m>5MKY1K8=8=+OzOy32-+(RmaAKtR9G@p+P>+|}1GYgR*!$bbp<4om$@`}pO#yw# z6z75Y$X;$@Vm{lFGi8C9JaE$lQ|z_P$@Jtt(MAd5kXa}uqa>qN$8#ddiY!3JrRpuu zM!qMi7ce(4B*LeRyHJ_GL~KY6Q>^wN7~BK90V({{CjACm>pv}VLc6xBwQcW8ljXg-NB{NQf zI7G2$ncY1-%b|5qL$l)Q-w_#Vs`QK+R}S-L_~#R%5@ZDbyU5gEh|*;|EX1;BFiFmB zF6o?iG=`(>!Qhfcvooo%%W5$ocD$+8WRTf~G-)^+i7p^&9wLLEB#g_B*HKZjArHy^ zl3#0Epm9EK*86tRXOGjCT&;^(Db<|>tD`St?}~4>WrOF$6Av7o}9mRG;RGu=dq$!^Tq?HdCj$0Lk(y*i?44w)tq z*^hydEm+H{RuB(H>BSn^9e{sw2kT-M)p;qv7mGXt);`G)!5BA;(u2f%ziG%9n0J}_ ztgS_`Om4&pyN7h<95`2MPG)R8!c zKxS8Mk+EX?*{A_Ke^*LADMqBT9LpZ`B27-zVqhyA${UqM2J|_ck`{@)9^hvh{%&5QQ>QC6^)7#)+!>bTDH$O0u@ypY= zmWgLhl8@|e4jjS4_pvy$(!q$`LMoAX{2#ON>Lb2}RRzfOevBHU$EhY_i}}7CO--R{ z&deEEG03xO)+?emy1~gpv(yZ0sS$603j9Ybc6#@0DQqvmYvLn1|1phGr&r)1h$Q9iP`T6uJ{Pvd4@@rgA?$Tg{(=ybl5z&Jqu{ zbf!rCj-8Y#!H@^H12xZ1Pca5-qsw%QKDQi{>uZNNo{S+A?&bK!4uXP}B6Wp8$dw}k zx9=MUw?|O6>99ui{v3T}xO^r*r{DM(&SL<>zcNC97H@2+9BuT^E?gbH7KWVR=YDPx zrOVxM%8@=W4l!Jl1%8p?3M=tsn_-yXe~7{Nc+pi~%ndBX zgyuhfpRwwKPnATgad(`?nF_S2zA30o2x(|*7 z%dvI(Q9OH^XIIb6h^~yO6Qf}SHE$r_aDt=TrnzkT6ZjQ`5wm>(NW0pS(vzNd?3Ylc z6zrD{eYG?r7xK8z-K2VI=VZ5k&7hBAyww_{)%y*5g$ic&OifR3qi)6=OwK`Pi>r&P zFC&HsFL!6a|9_|!wX*$L^%OqAP3j+cZVsnHt>LTg45w24240lAy9M{PszG-%CC(CA z<6;hJ{=Z#E?$qEt)&754UTDj<7*>|f|Np7{>z{E6PeYy0x`M6=o#c80tm*oGQ( zS5yM(wmyOXboVg0A=J@tq~fBu@tp{Ue-YyR&lrt~y!Jyt;Uq)E6LaQzVOsa8&(9^J zq{SDZhbSiZT0A7a|NBuS~W=t0}c)%G3>V*0ShnpwjLSqimFuW~u>g{<#7ArZyIVo*hJ6Z;BTRL{o zZ_K{Lx4#R`ZifQouLJ6kE`e$7ikA-|*3NVN9kLFrfQTz^!B7p~-7H+mHFg25V)W~+ z?owB1xkqcR|3}6(I>`kv68HzmU(YK61hhP?QE8@gf)ZEeD&XvU^BFyt-p}0hc7PSR z8wzb0{&fi?S2YAP)S=zbgiE|;ysp)PihhNR?^PtlwIYP~7DVMsfMglBl3jy= z9a$8dUCpxD8vQR$xeb?~lHvZR*>dsApHHYK^UqwUh7Car&I5oyn}v+ndRJjA`QAc7XT}_m+TWOB-r2(TJRmWmSrWCQi_3ejP8&5 zyjM6A#1)tc>M-I81uNuB$U`#lEb1rQIIu2@w2{?BPT0X-%2LkAGozI4W%()yk1mE; zKrT0o`R~y%vT^^Sy<-m=?l@%CYWm)6!;n$Av2Mh zsd3C2No>o^h*%82z!hR(6tXL!NWkS+W-^_)hWrHvi-Su>mDHA;_mi2Wg!6rM=rNP8T7*gqh z(V3x5hK^9+xn(ejZn%4f(h#ds1y$6Ewu8WaON3EH823oLRe%i*g!+MF7!zCz%muEn zsmyJxRhud@3CGaB?f;#@aun6Y*} zJ$}N(Nj#IM*lqceY4WDe2$?x+wtWAk`}WS__PqHE6f6u4^ZY&$kx@FEq_A=E2?`Y_ zCMCQ0d`~7VJtNcD41@Zd3GZZSU+FsZs}Eq@7MY1$UMYzIT3c>BApg-vojoPhT2|#(ezKL^74mWOMmK zu~e>9YgTo3qiJ(->+W{P?+!iUmV5gHOI2^;$#mw{#}-T9i4Lqc^D~f;9Pw;WB?p3` zux?R+*KeY_Bi-EC+|u55=U;aB_78Lp6UkIMb98)idX~-Q3y%7t(S z-Tm*i>o;!Rx_##^t^fY-+w{Tr)qrv2*ojkT&Rw{4<@zBw{)lwj9fFimRfwk*N|m}w zsdidj zNtYoLOIEc7a^%XxsiV%i>aM5W`np?x_ZsMagPqcMV{g8p&UChO78>h97rWG0KUm~RSNgG^EbyXh zUGGLW`?+7b)$Q(dw_p3Md;Q)Y-Pdoe@G5|jpO_ppqa^=Zapb!)c z90C#w8U_{)ev~OF8AX+ILC`TUv9NJ)@$d-<0YtoInLWK-Z{&k(x8n!pcU@ zi8zTly({A3Rm`VEsWRm%RH{;~M(ui2ylv2^NwXHM+O+G?sY|yhdbG61oN~hM=^Gdt z8Jn1znOj&|S$Ai5*#LkbFa!#NBakRG28+WJh$J$FN~1HFEH;PB;|qi$u|z79E0ij= zMjoznL6nqLQ~^K`7y^aC)zmdKwX|V7K@WTY(I|!1>`WY+n5uRnyl^L8B2%ce@tfFS zve+Chk1r64#1g5@&o*cT9T>*`oo=r`7!o8!Go$fjI%7Fr5G7erH9AnH z6PuvDil3@1p6mg&sQ@q#E>ba>4s_9j_chgxF}B2EHBEc zZrZND4C6E}>$V@~^>%+eTkTG_*B=Z=Y3Uo2Pa4FEzgf?}?ru1JbzSdJGUL*0`V zRnraAvK^Pr69j1fKlyQzW_eLobtA8!sHCi-3IKw@5GV|;rmmr>rLCi@r*B}0K%&qX zBV!X&Gb|2IAd<)w>I46iZkU$sxSk(`QJkb1P}mH+IMivn+#avx^Xqs))Jq20sHi5h z#cDIjW+*#93iNW`zbs)p5~)0QOvwW&u`h*3nW9dECN0`@=n_o~vBVKi0*NG%Op3R& zQg?HuQs+u_&QtRfBn%LFwk1iLEP0BQ0_Lbjnpiu?=**dL!-FhjZ{)?bK10S#nX_cg z7Dy5r#KW5dK^rZrT<;vMs@n9NzMmJjz1CHKb**N9+bE>g+G?*uwLC~hJIIcgo9G$q zDz&uI%P2EeS!EN4j-6M2H&M+&DP^MO&T)ZSylZXsH6TGl(c0Dn2lqmt&T#nk-U!D? z{8*_d<^uXL+W}Hz)S%A8T!C77EKZQDtat{)9Y2blA3Z2t!(G@_K4};YO+nvwJuKf1Y^6tO(^>l#T zoF8wE%P`zHWUffx_Tw>ds4tX1%Idbt~}eU1z2x+rR7G>~?p1adBU~9#=>Irsqv4 zY0-Eq9ghq0{*Sx^=i^fFNyTY;?@=_va`L?}a4DjL0B)GY|;iw}6fUZe;z^t}P z01Yw}hbmYh0Fkab@6?86(ZEF8PWZ3Vw7O*-A6C7LPt&9q@egNyUM4;~8PV(mKDVsM z0(+Vc8K+wozQFi@qdvz7;fiH?%f>(X(=DJ zjeIuyAn-@<&iF^dscmaAON1Ae*DKMYEt zk4Jv~C|sgmZI;~su;tV7mahd<*w4pO^0WLU3&y@K901K+sOf|s0jAPP7NP$YRSBF`#Bhh>-}_6;;Y?(B`$>mo?q zdiRJ%UD-dt>qb8<6JMW>ia%*;A7PcgZ7gBZGL*OxoDP`RUt`akp!jxB?+MZ}XO0>w zoEYo)@>x08WtZVKQPU%XO9Lu|(2dIvBv1y{kQYADOm|&bLGy|(^I|+-`RA>#Od$+- zBHw{*aXwu;=L61l*mA57hdFU>W}sn*6KrR3qZHdZNu8W$_=rTV8|xum2%Dm!cBbG` z{VIVMvd=eGQFT!bL|1_3)m?Z4UlQ8w#u{$Ap@uVopKp$^kT_|=g>y2o0Dv#e#nTq_$U(r6qLHFSjuJHxV{JqL z2*#{dEOg`gPyAub)0dv6X|q*u6QnU<KX@pqZ*qBDP^q zzGECEe`#GZrr`vF@EU>2nG;z_F$?p@Sf1H(2uxh@Q~JAl|{4m|HY!nZmxcHnZmr+dG&m=0fmrB)#p z#g}m&7Q+FjcVEVvNq3HR^fSxLBy+E>7juM9v3xQ~ooyaEm0=(=RGc^=LLXt$JV(w2 z*~(m&;G6b;_?DmaW>$SV2*)ObuZb5sO(_^Re81cu!A_%znE7~{FnjkK#T}z2FX=^A zVM!T?N)CpU7?FqyQLr@56oK}|-lh}~we^TrJ?xFV5_tEy5!mpSgMyo-y;`0r6|}iq zX8lMZgHz;Cj=r$#;%(87u~$d}rg>lf#}wO5F8`RoN02~v3vZ4=vCa;J@Pu@14d;Ac z!NzVIQcMa=>v<0Jb(@fC0*z)xtFoPCX@80-=RgcP+mqYRkv4xvL}UlCtX~DUUCdHU zRas_^6i#Y)9K)gqJ4!98vY$J!RMk&_d1+tC$}`(Ws8~2kE(4|TV*WX$tSCWHqupB{ zrfq&pgJ~zN^Eow=cEv>Q98FBmt-Siwb+BLGH+s_{ahx6F4=L^=qd?$dV_ zM&)9AS>=H*N(U-XrBQwWqb#M6 zmz`msp=@2PoQOsa}KrwG(|M^9vw=sT9Ntp)rIy!u0?Zqo6Y-qNdCg z^q>{6RUpum)^}K&D#;)d)FRa7J#7LHJ%DmGw013)Qk?8tq+aYDdibkisXC_~8&u6M zt9qoWvlK54us)0m7YL$QK#=&r(qFvG_q%Y9#chjpqcI^zc<-n1Nr`Ry^?X zPuGERZT!PxV}&W|Y%!VXAJRoGpacx0u0%`}voWAK?G90hO4m%ke7iA_PdN5gDDL=B z^+k6NB3V&5Onfepwm_r18|iHw#Tm1L)~N9FU?cG#53XG+fp@?@T*g?hL7P zy#=lB!G-@iAaprK1UI>q8c^!z?5(E`b)@1#Jeluf^!kddM}0+AgT8iZs4pk3oF3>uX*8XwR&NX-9V9$Ugu6jM8e z3TbX4yEhTV(a?7Q|M6i10Vw+0hc^WkMw}X!np8`KqtSj-m(B8r|DacMpTW^zsYeii zs2JSaLq=tQ&Sm3tjoDP4brNy~I`SQ2-EOU%V7v z3P%A#2qAgEP(lbHgc3q1rIb)g zDIu3aOt~$TftYeT*!H!BngM?09wo5hA%qY@)bHqBC7n{yn_|5dNa+cQRX<9fzWG~(zU7nk^Ovd$4WoPe z5Oe_rTMldXw^n}F`a}49h$rKZP)gv(xa?2$)?pYUT_AMsYSlea#OAgi)MLP%{rAjN)v9nt2GLgR=^? zsTJs4H~RP6>g??EIWwmNw?2d6cF8}b>Y6(5mRsSrS?R@k91L9+^Zf6W-2(flHQkM5?M2YQo>bG*FN+P=c+$S<$?lY5g*?DAaCm-($(+4C?L zkvaRR>_d!R^cIwc5!C~F8e()2%bP~RH6#OjTA@ttu7&N0_9$I5C=fyjHw%e(NDx8@Ej2Y# z%H2zCN3=)jnqkElV~p{Xs;xp(YQ$a=B`Zlv&xn~$jq1^eymU@ik?lf~Gsb0(kV@Cg zc#YXp1f@#dUED^io7&XdJOGt#QGa4fP=>p3R>r=<2nA^|0~J2>8zL`ky7)ksDB`9| zuXX8-NuNU?3S&2gE)?1KkxkSwMMyGj3?OsY-$YeA(at_`qL)Q?dC|H}VQY$ssCHpa zd@;CNfRNK5mH~4itMUPUwVQfuSk6 z3lv7r;`UHBuF-=45hf|IP9(Jiz;F?vtVa;I++>Qzvm1cXv$=(`)SA@801*z$3hS0x ztU3hfwPuVl#@0-`w(Pe=n8}?fHB({_ z1O&y}BaNPuM;d(;o%d3V)q&mp8`n^4%X^$W<{E;z4pNu<-3drMpG`N-B2Ht>{jL)P zaSP2g)@_^a*FKOE<4I#n&N+s?E4!AI5u10Kt~sMyhmqs7dJqqK7OAoc*60O?d>z>o zbIs@9agvzg8uMVF*JOVV z1P~~!Nl0U#liMr*T&T0@TzaWr5N#-a6tO80XQKa~q2nBCWYwFmhyZ8e8UL_}L16Gv zz2hR@Sv(e(0k01O96^4}@MIkJyqAq@mykLX|)WCJdNRiU|Ecc-Id)39;BOM_|KPi9n=PdeiR3En-3$ zx9nH^J%|YE^Mrm$3h&%4!C>`OAGdn$ap9y3Jpcv-As?s$L@)?S34O7brzH970Wcs4 z`9KvQfddRv02!pIE4<;_7$MY8Yl` zjni*r>tu|q96nB%60~dO((XoSBflJy87%qR!f)@8N=~}y-?@^k$!Mq{?Rsa>{nSX&>?S=O zsRzPLHAm7Tb2Nh#2n3*f;`=UqDIB3UKk{s`7XxkxSv)YXo5fQ)C14cft2u|t9J^me z8W~d9Wadb1AZIT!2&JuKctdL<}zSe<>8>PLy??A?=OZs2eKWj zQ#eHfmu$#!y>2Fy2%eJhw9W~;VwI=8aiyceP$((=P#((WN{@0xS>Y$r)N&&S2ewX`_)d%^`Cf}$w_M8YJTAfgaRi9|4?;v#&O-6&+XKgIDmfbsj*BRs+HUb3`L zMDUA~oqMtdzm(l6hOW^ru&~!!>mT9uG{68~tAC}x%iFunZm(3fDzr%4PX3^wEb;Phs966;F& zn0XANPYG)i$c?f*TB}*@a8obu8EfyH*D?*yZ|y zzBA-ouHxlVn$=7MW|BM!R3`P(LI#e`AaJGp@6C^Y=zn~#p9{W)F&pWSg~O5QamnGc zX>yT@C-RCqsV676v*IrHK4m9v1r z8z(4gJj{!&CsA3Uno%;#*1JGh9W+Rr-9G0S@s#_NRCd|y>4W|pZUyY<*(gyBa$wf@V?=YZls@RSndesl6_l7Fi zKfnL}jdunW?xucnyU!QytsbL3WpOffi#IY1-+ltc9m>zJe8ZD> z$wYss*d;Svq?v84CMh#5^;*AW)mkPev5(h>`^0AT)Ny*O9C3qix_`y1-6Dt5UF82=z5YZa;8_(Ejr8#f?`p+(GIPsAmL(xwp^e3 N#!G)s;2JO>~QPMdMZ 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

xz5_8aF@(pQs@zgto+ItjS_s2^}<|M=lFQ2 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,qWUc(1+qXDfx7@=bp~~)(TqS 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 Psah$GwaNdR#Q^{S={DW} literal 0 HcmV?d00001 diff --git a/pr/234/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt b/pr/234/docs/static.files/NanumBarunGothic-LICENSE-a37d393b.txt new file mode 100644 index 000000000..4b3edc29e --- /dev/null +++ b/pr/234/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/234/docs/static.files/SourceCodePro-It-fc8b9304.ttf.woff2 b/pr/234/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/234/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt b/pr/234/docs/static.files/SourceCodePro-LICENSE-67f54ca7.txt new file mode 100644 index 000000000..0d2941e14 --- /dev/null +++ b/pr/234/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/234/docs/static.files/SourceCodePro-Regular-8badfe75.ttf.woff2 b/pr/234/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/234/docs/static.files/SourceSerif4-Bold-6d4fd4c0.ttf.woff2 b/pr/234/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/234/docs/static.files/SourceSerif4-It-ca3b17ed.ttf.woff2 b/pr/234/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/234/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 b/pr/234/docs/static.files/SourceSerif4-Regular-6b053e98.ttf.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0263fc304226d90e224e53053855ad138303b70b GIT binary patch literal 76260 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 G0RRB5qhxRZ literal 0 HcmV?d00001 diff --git a/pr/234/docs/static.files/SourceSerif4-Semibold-457a13ac.ttf.woff2 b/pr/234/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/234/docs/static.files/favicon-044be391.svg b/pr/234/docs/static.files/favicon-044be391.svg new file mode 100644 index 000000000..8b34b5119 --- /dev/null +++ b/pr/234/docs/static.files/favicon-044be391.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/pr/234/docs/static.files/favicon-32x32-eab170b8.png b/pr/234/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/234/docs/static.files/normalize-9960930a.css b/pr/234/docs/static.files/normalize-9960930a.css new file mode 100644 index 000000000..469959f13 --- /dev/null +++ b/pr/234/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/234/docs/static.files/noscript-263c88ec.css b/pr/234/docs/static.files/noscript-263c88ec.css new file mode 100644 index 000000000..e41db77dc --- /dev/null +++ b/pr/234/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/234/docs/static.files/rust-logo-9a9549ea.svg b/pr/234/docs/static.files/rust-logo-9a9549ea.svg new file mode 100644 index 000000000..62424d8ff --- /dev/null +++ b/pr/234/docs/static.files/rust-logo-9a9549ea.svg @@ -0,0 +1,61 @@ + + + diff --git a/pr/234/docs/static.files/rustdoc-ca0dd0c4.css b/pr/234/docs/static.files/rustdoc-ca0dd0c4.css new file mode 100644 index 000000000..7e6df69c2 --- /dev/null +++ b/pr/234/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/234/docs/static.files/scrape-examples-2bbcccac.js b/pr/234/docs/static.files/scrape-examples-2bbcccac.js new file mode 100644 index 000000000..c90f297f9 --- /dev/null +++ b/pr/234/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/234/docs/static.files/search-8d3311b9.js b/pr/234/docs/static.files/search-8d3311b9.js new file mode 100644 index 000000000..5da8debd8 --- /dev/null +++ b/pr/234/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/234/docs/static.files/settings-c38705f0.js b/pr/234/docs/static.files/settings-c38705f0.js new file mode 100644 index 000000000..7e4939e5c --- /dev/null +++ b/pr/234/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/234/docs/static.files/src-script-813739b1.js b/pr/234/docs/static.files/src-script-813739b1.js new file mode 100644 index 000000000..bf546257c --- /dev/null +++ b/pr/234/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/234/docs/static.files/storage-e2aeef58.js b/pr/234/docs/static.files/storage-e2aeef58.js new file mode 100644 index 000000000..fb76ad5e9 --- /dev/null +++ b/pr/234/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/234/docs/static.files/stringdex-828709d0.js b/pr/234/docs/static.files/stringdex-828709d0.js new file mode 100644 index 000000000..b8268d530 --- /dev/null +++ b/pr/234/docs/static.files/stringdex-828709d0.js @@ -0,0 +1,2 @@ +const EMPTY_UINT8=new Uint8Array();class RoaringBitmap{constructor(u8array,startingOffset){const start=startingOffset?startingOffset:0;let i=start;this.keysAndCardinalities=EMPTY_UINT8;this.containers=[];this.consumed_len_bytes=0;if(u8array===null||u8array.length===i||u8array[i]===0){return this;}else if(u8array[i]>0xf0){const lspecial=u8array[i]&0x0f;this.keysAndCardinalities=new Uint8Array(lspecial*4);let pspecial=i+1;let key=u8array[pspecial+2]|(u8array[pspecial+3]<<8);let value=u8array[pspecial]|(u8array[pspecial+1]<<8);let entry=(key<<16)|value;let container;container=new RoaringBitmapArray(1,new Uint8Array(4));container.array[0]=value&0xFF;container.array[1]=(value>>8)&0xFF;this.containers.push(container);this.keysAndCardinalities[0]=key;this.keysAndCardinalities[1]=key>>8;pspecial+=4;for(let ispecial=1;ispecial>16;container=this.addToArrayAt(key);const cardinalityOld=container.cardinality;container.array[cardinalityOld*2]=value&0xFF;container.array[(cardinalityOld*2)+1]=(value>>8)&0xFF;container.cardinality=cardinalityOld+1;pspecial+=2;}this.consumed_len_bytes=pspecial-i;return this;}else if(u8array[i]<0x3a){const lspecial=u8array[i];this.keysAndCardinalities=new Uint8Array(lspecial*4);let pspecial=i+1;for(let ispecial=0;ispecial>8)&0xFF;container.cardinality=cardinalityOld+1;pspecial+=4;}this.consumed_len_bytes=pspecial-i;return this;}const has_runs=u8array[i]===0x3b;if(u8array[i]!==0x3a&&u8array[i]!==0x3b){throw new Error("not a roaring bitmap: "+u8array[i]);}const size=has_runs?((u8array[i+2]|(u8array[i+3]<<8))+1):((u8array[i+4]|(u8array[i+5]<<8)|(u8array[i+6]<<16)|(u8array[i+7]<<24)));i+=has_runs?4:8;let is_run;if(has_runs){const is_run_len=(size+7)>>3;is_run=new Uint8Array(u8array.buffer,i+u8array.byteOffset,is_run_len);i+=is_run_len;}else{is_run=EMPTY_UINT8;}this.keysAndCardinalities=u8array.subarray(i,i+(size*4));i+=size*4;let offsets=null;if(!has_runs||size>=4){offsets=[];for(let j=0;j>3]&(1<<(j&0x7))){const runcount=(u8array[i]|(u8array[i+1]<<8));i+=2;this.containers.push(new RoaringBitmapRun(runcount,new Uint8Array(u8array.buffer,i+u8array.byteOffset,runcount*4),));i+=runcount*4;}else if(cardinality>=4096){this.containers.push(new RoaringBitmapBits(new Uint8Array(u8array.buffer,i+u8array.byteOffset,8192,)));i+=8192;}else{const end=cardinality*2;this.containers.push(new RoaringBitmapArray(cardinality,new Uint8Array(u8array.buffer,i+u8array.byteOffset,end),));i+=end;}}this.consumed_len_bytes=i-start;}static makeSingleton(number){const result=new RoaringBitmap(null,0);result.keysAndCardinalities=Uint8Array.of((number>>16),(number>>24),0,0,);result.containers.push(new RoaringBitmapArray(1,Uint8Array.of(number,number>>8),));return result;}static everything(){if(EVERYTHING_BITMAP.isEmpty()){let i=0;const l=1<<16;const everything_range=new RoaringBitmapRun(1,Uint8Array.of(0,0,0xff,0xff));EVERYTHING_BITMAP.keysAndCardinalities=new Uint8Array(l*4);while(i>8;EVERYTHING_BITMAP.keysAndCardinalities[(i*4)+2]=0xff;EVERYTHING_BITMAP.keysAndCardinalities[(i*4)+3]=0xff;i+=1;}}return EVERYTHING_BITMAP;}static empty(){return EMPTY_BITMAP;}isEmpty(){return this.containers.length===0;}addToArrayAt(key){let mid=this.getContainerId(key);let container;if(mid===-1){container=new RoaringBitmapArray(0,new Uint8Array(2));mid=this.containers.length;this.containers.push(container);if(mid*4>this.keysAndCardinalities.length){const keysAndContainers=new Uint8Array(mid*8);keysAndContainers.set(this.keysAndCardinalities);this.keysAndCardinalities=keysAndContainers;}this.keysAndCardinalities[(mid*4)+0]=key;this.keysAndCardinalities[(mid*4)+1]=key>>8;}else{container=this.containers[mid];const cardinalityOld=this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8);const cardinality=cardinalityOld+1;this.keysAndCardinalities[(mid*4)+2]=cardinality;this.keysAndCardinalities[(mid*4)+3]=cardinality>>8;}const cardinalityOld=this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8);if(!(container instanceof RoaringBitmapArray)||container.array.byteLength<((cardinalityOld+1)*2)){const newBuf=new Uint8Array((cardinalityOld+1)*4);let idx=0;for(const cvalue of container.values()){newBuf[idx]=cvalue&0xFF;newBuf[idx+1]=(cvalue>>8)&0xFF;idx+=2;}if(container instanceof RoaringBitmapArray){container.cardinality=cardinalityOld;container.array=newBuf;return container;}const newcontainer=new RoaringBitmapArray(cardinalityOld,newBuf);this.containers[mid]=newcontainer;return newcontainer;}else{return container;}}union(that){if(this.isEmpty()){return that;}if(that.isEmpty()){return this;}if(this===RoaringBitmap.everything()||that===RoaringBitmap.everything()){return RoaringBitmap.everything();}let i=0;const il=this.containers.length;let j=0;const jl=that.containers.length;const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array((il+jl)*4);while(i=jl||(i=il||(jthatContainer.array.length?thisContainer.array.length:thatContainer.array.length,);let k=0;const kl=resultArray.length;while(k>8)&0xFF;k+=2;}result.containers.push(new RoaringBitmapArray(resultValues.length,resultArray,));card=resultValues.length;}result.keysAndCardinalities[k+0]=this.keysAndCardinalities[ik+0];result.keysAndCardinalities[k+1]=this.keysAndCardinalities[ik+1];card-=1;result.keysAndCardinalities[k+2]=card;result.keysAndCardinalities[k+3]=card>>8;i+=1;j+=1;}}return result;}intersection(that){if(this.isEmpty()||that.isEmpty()){return EMPTY_BITMAP;}if(this===RoaringBitmap.everything()){return that;}if(that===RoaringBitmap.everything()){return this;}let i=0;const il=this.containers.length;let j=0;const jl=that.containers.length;const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array((il>jl?il:jl)*4);while(i=jl||(i=il||(jthatContainer.array.length?thisContainer.array.length:thatContainer.array.length,);let k=0;const kl=resultArray.length;while(k>8)&0xFF;k+=2;}result.containers.push(new RoaringBitmapArray(resultValues.length,resultArray,));}}if(card!==0){result.keysAndCardinalities[k+0]=this.keysAndCardinalities[ik+0];result.keysAndCardinalities[k+1]=this.keysAndCardinalities[ik+1];card-=1;result.keysAndCardinalities[k+2]=card;result.keysAndCardinalities[k+3]=card>>8;}i+=1;j+=1;}}return result;}contains(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;const mid=this.getContainerId(key);return mid===-1?false:this.containers[mid].contains(value);}remove(keyvalue){const key=keyvalue>>16;const value=keyvalue&0xFFFF;const mid=this.getContainerId(key);if(mid===-1){return this;}const container=this.containers[mid];if(!container.contains(value)){return this;}const newCardinality=(this.keysAndCardinalities[(mid*4)+2]|(this.keysAndCardinalities[(mid*4)+3]<<8));const l=this.containers.length;const m=l-(newCardinality===0?1:0);const result=new RoaringBitmap(null,0);result.keysAndCardinalities=new Uint8Array(m*4);let j=0;for(let i=0;i>8;const card=newCardinality-1;result.keysAndCardinalities[(j*4)+2]=card;result.keysAndCardinalities[(j*4)+3]=card>>8;const newContainer=new RoaringBitmapArray(newCardinality,new Uint8Array(newCardinality*2),);let newContainerSlot=0;for(const containerValue of container.values()){if(containerValue!==value){newContainer.array[newContainerSlot]=value&0xFF;newContainerSlot+=1;newContainer.array[newContainerSlot]=value>>8;newContainerSlot+=1;}}result.containers.push(newContainer);j+=1;}}else{result.keysAndCardinalities[(j*4)+0]=this.keysAndCardinalities[(i*4)+0];result.keysAndCardinalities[(j*4)+1]=this.keysAndCardinalities[(i*4)+1];result.keysAndCardinalities[(j*4)+2]=this.keysAndCardinalities[(i*4)+2];result.keysAndCardinalities[(j*4)+3]=this.keysAndCardinalities[(i*4)+3];result.containers.push(this.containers[i]);j+=1;}}return result;}getContainerId(key){let left=0;let right=this.containers.length-1;while(left<=right){const mid=Math.floor((left+right)/2);const x=this.keysAndCardinalities[(mid*4)]|(this.keysAndCardinalities[(mid*4)+1]<<8);if(xkey){right=mid-1;}else{return mid;}}return-1;}*entries(){const l=this.containers.length;for(let i=0;i>1;const i=mid*4;const start=this.array[i]|(this.array[i+1]<<8);const lenm1=this.array[i+2]|(this.array[i+3]<<8);if((start+lenm1)value){right=mid-1;}else{return true;}}return false;}*values(){let i=0;while(i>1;const i=mid*2;const x=this.array[i]|(this.array[i+1]<<8);if(xvalue){right=mid-1;}else{return true;}}return false;}*values(){let i=0;const l=this.cardinality*2;while(i>3]&(1<<(value&7)));}*values(){let i=0;const l=this.array.length<<3;while(i=this.values.length*9){const keys=this.keys;const values=this.values;const l=values.length;this.capacityClass+=1;const capacity=1<otherDistance){const otherKey=keys.slice(j,j+6);values[slot]=value;value=otherValue;keys[j+0]=key[start+0];keys[j+1]=key[start+1];keys[j+2]=key[start+2];keys[j+3]=key[start+3];keys[j+4]=key[start+4];keys[j+5]=key[start+5];key=otherKey;start=0;distance=otherDistance;}distance+=1;slot=(slot+1)&mask;}}}get(key){if(key.length!==6){throw"invalid key";}return this.getWithOffsetKey(key,0);}getWithOffsetKey(key,start){const mask=~(0xffffffff<otherDistance){break;}}slot=(slot+1)&mask;}return undefined;}}function bitCount(n){n=(~~n)-((n>>1)&0x55555555);n=(n&0x33333333)+((n>>2)&0x33333333);return((n+(n>>4)&0xF0F0F0F)*0x1010101)>>24;}class Uint8ArraySearchPattern{constructor(needle){this.needle=needle;this.skipTable=[];const m=needle.length;for(let i=0;i<256;i+=1){this.skipTable.push(m);}for(let i=0;i=m){for(let i=m-1;i>=0;i-=1){if(haystack[skip+i]!==needle[i]){skip+=skipTable[haystack[skip+m-1]];continue search;}}return true;}return false;}}function loadDatabase(hooks){const callbacks={rr_:function(data){const dataObj=JSON.parse(data);for(const colName of Object.keys(dataObj)){if(Object.hasOwn(dataObj[colName],"N")){const counts=[];const countsstring=dataObj[colName]["N"];let i=0;const l=countsstring.length;while(i>4)&0x0f));const id3=id2+(((nodeid[4]&0x0f)<<8)|nodeid[5]);leaves=RoaringBitmap.makeSingleton(id1).union(RoaringBitmap.makeSingleton(id2)).union(RoaringBitmap.makeSingleton(id3));}else{leaves=RoaringBitmap.makeSingleton((nodeid[2]<<24)|(nodeid[3]<<16)|(nodeid[4]<<8)|nodeid[5],);}if(isWhole){const data=(nodeid[0]&0x20)!==0?Uint8Array.of(((nodeid[0]&0x0f)<<4)|(nodeid[1]>>4)):EMPTY_UINT8;newPromise=Promise.resolve(new PrefixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,EMPTY_SEARCH_TREE_BRANCHES,data,leaves,EMPTY_BITMAP,));}else{const data=(nodeid[0]&0xf0)===0x80?0:(((nodeid[0]&0x0f)<<4)|(nodeid[1]>>4));newPromise=Promise.resolve(new SuffixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,data,leaves,));}}else{const hashHex=makeHexFromUint8Array(nodeid);newPromise=new Promise((resolve,reject)=>{const cb=registry.searchTreeLoadPromiseCallbacks.get(nodeid);if(cb){registry.searchTreeLoadPromiseCallbacks.set(nodeid,(err,data)=>{cb(err,data);if(data){resolve(data);}else{reject(err);}});}else{registry.searchTreeLoadPromiseCallbacks.set(nodeid,(err,data)=>{if(data){resolve(data);}else{reject(err);}});hooks.loadTreeByHash(hashHex);}});}registry.searchTreePromises.set(nodeid,newPromise);return newPromise;},dataLoadByNameAndHash:function(name,hash){const existingBucket=registry.dataColumnsBuckets.get(hash);if(existingBucket){return existingBucket;}const hashHex=makeHexFromUint8Array(hash);const newBucket=new Promise((resolve,reject)=>{const cb=registry.dataColumnLoadPromiseCallbacks.get(hash);if(cb){registry.dataColumnLoadPromiseCallbacks.set(hash,(err,data)=>{cb(err,data);if(data){resolve(data);}else{reject(err);}});}else{registry.dataColumnLoadPromiseCallbacks.set(hash,(err,data)=>{if(data){resolve(data);}else{reject(err);}});hooks.loadDataByNameAndHash(name,hashHex);}});registry.dataColumnsBuckets.set(hash,newBucket);return newBucket;},};class SearchTreeBranches{constructor(length,nodeids){this.nodeids=nodeids;this.subtrees=[];for(let i=0;i=this.keys[i]){throw new Error("HERE");}i+=1;}}*entries(){let i=0;const l=this.keys.length;while(i>1;if(this.keys[mid]k){right=mid-1;}else{return mid;}}return-1;}getKey(i){return this.keys[i];}getKeys(){return this.keys;}}const EMPTY_SEARCH_TREE_BRANCHES=new SearchTreeBranchesArray(EMPTY_UINT8,EMPTY_UINT8,);const SHORT_ALPHABITMAP_CHARS=[];for(let i=0x61;i<=0x7A;++i){if(i===0x76||i===0x71){continue;}SHORT_ALPHABITMAP_CHARS.push(i);}const LONG_ALPHABITMAP_CHARS=[0x31,0x32,0x33,0x34,0x35,0x36];for(let i=0x61;i<=0x7A;++i){LONG_ALPHABITMAP_CHARS.push(i);}function makeSearchTreeBranchesAlphaBitmapClass(alphabitmap_chars,width){const bitwidth=width*8;const cls=class SearchTreeBranchesAlphaBitmap extends SearchTreeBranches{constructor(bitmap,nodeids){super(nodeids.length/6,nodeids);if(nodeids.length/6!==bitCount(bitmap)){throw new Error(`mismatch ${bitmap} ${nodeids}`);}this.bitmap=bitmap;this.nodeids=nodeids;}*entries(){let i=0;let j=0;while(i=this.subtrees.length?-1:result;}getKey(branch_index){return this.getKeys()[branch_index];}getKeys(){const length=bitCount(this.bitmap);const result=new Uint8Array(length);let result_index=0;for(let alpha_index=0;alpha_index=6?new Lev2TParametricDescription(w):new Lev1TParametricDescription(w);const stack=[[Promise.resolve(this.trie(dataColumn,searchPattern)),0]];const n=levParams.n;while(stack.length!==0){const[triePromise,levState]=stack.pop();const trie=await triePromise;for(const byte of trie.keysExcludeSuffixOnly()){const levPos=levParams.getPosition(levState);const vector=levParams.getVector(name,byte,levPos,Math.min(w,levPos+(2*n)+1),);const newLevState=levParams.transition(levState,levPos,vector,);if(newLevState>=0){const child=trie.child(byte);if(child){stack.push([child,newLevState]);if(levParams.isAccept(newLevState)){yield child;}}}}}}getCurrentLeaves(){return this.leaves_whole.union(this.leaves_suffix);}}class PrefixTrie{constructor(tree,offset,dataColumn,searchPattern){this.tree=tree;this.offset=offset;this.dataColumn=dataColumn;this.searchPattern=searchPattern;}matches(){if(this.offset===this.tree.data.length){return this.tree.leaves_whole;}else{return EMPTY_BITMAP;}}async*substringMatches(){let layer=[Promise.resolve(this.tree)];while(layer.length){const current_layer=layer;layer=[];for await(const tree of current_layer){let rejected=null;let leaves=tree.getCurrentLeaves();for(const leaf of leaves.entries()){const haystack=await this.dataColumn.at(leaf);if(haystack===undefined||!this.searchPattern.matches(haystack)){if(!rejected){rejected=[];}rejected.push(leaf);}}if(rejected){if(leaves.cardinality()!==rejected.length){for(const rej of rejected){leaves=leaves.remove(rej);}yield leaves;}}else{yield leaves;}}const subnodes=new HashTable();for await(const node of current_layer){const branches=node.branches;const l=branches.subtrees.length;for(let i=0;i0&&backlog[backlogSlot].length>1].length){const parentSlot=(backlogSlot-1)>>1;const parent=backlog[parentSlot];backlog[parentSlot]=backlog[backlogSlot];backlog[backlogSlot]=parent;backlogSlot=parentSlot;}}while(backlog.length!==0){const backlogEntry=backlog[0];if(minLength!==null&&backlogEntry.length>minLength){break;}if(!backlogEntry.bitmap.isEmpty()){yield backlogEntry.bitmap;}backlog[0]=backlog[backlog.length-1];backlog.length-=1;let backlogSlot=0;const backlogLength=backlog.length;while(backlogSlot{return node.trie(this.dataColumn,this.searchPattern);})]);i+=1;}return nodes;}else{const codePoint=data[this.offset];const trie=new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,);return[[codePoint,Promise.resolve(trie)]];}}keysExcludeSuffixOnly(){const data=this.tree.data;if(this.offset===data.length){return this.tree.might_have_prefix_branches.getKeys();}else{return Uint8Array.of(data[this.offset]);}}childrenExcludeSuffixOnly(){const data=this.tree.data;if(this.offset===data.length){const nodes=[];let i=0;for(const[k,v]of this.tree.might_have_prefix_branches.entries()){let node;if(v){node=v;}else{const newnode=this.tree.might_have_prefix_branches.getNodeID(i);if(!newnode){throw new Error(`malformed tree; no node for key ${k}`);}node=registry.searchTreeLoadByNodeID(newnode);this.tree.might_have_prefix_branches.subtrees[i]=node;this.tree.branches.subtrees[this.tree.branches.getIndex(k)]=node;}nodes.push([k,node.then(node=>{return node.trie(this.dataColumn,this.searchPattern);})]);i+=1;}return nodes;}else{const codePoint=data[this.offset];const trie=new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,);return[[codePoint,Promise.resolve(trie)]];}}child(byte){if(this.offset===this.tree.data.length){const i=this.tree.branches.getIndex(byte);if(i!==-1){let branch=this.tree.branches.subtrees[i];if(branch===null){const newnode=this.tree.branches.getNodeID(i);if(!newnode){throw new Error(`malformed tree; no node for key ${byte}`);}branch=registry.searchTreeLoadByNodeID(newnode);this.tree.branches.subtrees[i]=branch;const mhpI=this.tree.might_have_prefix_branches.getIndex(byte);if(mhpI!==-1){this.tree.might_have_prefix_branches.subtrees[mhpI]=branch;}}return branch.then(branch=>branch.trie(this.dataColumn,this.searchPattern));}}else if(this.tree.data[this.offset]===byte){return Promise.resolve(new PrefixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,));}return null;}}class SuffixSearchTree{constructor(branches,dataLen,leaves_suffix,){this.branches=branches;this.dataLen=dataLen;this.leaves_suffix=leaves_suffix;}trie(dataColumn,searchPattern){return new SuffixTrie(this,0,dataColumn,searchPattern);}async search(name,dataColumn){if(typeof name==="string"){const utf8encoder=new TextEncoder();name=utf8encoder.encode(name);}const searchPattern=new Uint8ArraySearchPattern(name);let trie=this.trie(dataColumn,searchPattern);for(const datum of name){const newTrie=trie.child(datum);if(newTrie){trie=await newTrie;}else{return null;}}return trie;}async*searchLev(_name,_dataColumn){}getCurrentLeaves(){return this.leaves_suffix;}}class SuffixTrie{constructor(tree,offset,dataColumn,searchPattern){this.tree=tree;this.offset=offset;this.dataColumn=dataColumn;this.searchPattern=searchPattern;}matches(){return EMPTY_BITMAP;}async*substringMatches(){let layer=[Promise.resolve(this.tree)];while(layer.length){const current_layer=layer;layer=[];for await(const tree of current_layer){let rejected=null;let leaves=tree.getCurrentLeaves();for(const leaf of leaves.entries()){const haystack=await this.dataColumn.at(leaf);if(haystack===undefined||!this.searchPattern.matches(haystack)){if(!rejected){rejected=[];}rejected.push(leaf);}}if(rejected){if(leaves.cardinality()!==rejected.length){for(const rej of rejected){leaves=leaves.remove(rej);}yield leaves;}}else{yield leaves;}}const subnodes=new HashTable();for await(const node of current_layer){const branches=node.branches;const l=branches.subtrees.length;for(let i=0;ibranch.trie(this.dataColumn,this.searchPattern));}}else{return Promise.resolve(new SuffixTrie(this.tree,this.offset+1,this.dataColumn,this.searchPattern,));}return null;}}class DataColumn{constructor(counts,hashes,emptyset,name,searchTree){this.searchTree=searchTree;this.hashes=hashes;this.emptyset=emptyset;this.name=name;this.buckets=[];this.bucket_keys=[];const l=counts.length;let k=0;let totalLength=0;for(let i=0;i=this.bucket_keys.length){return undefined;}else{const start=this.bucket_keys[idx];const bucket=this.buckets[idx];const data=this.buckets[idx].data;if(data===null){return this.atAsyncFetch(id,start,bucket);}else{return data[id-start];}}}}async atAsyncFetch(id,start,bucket){const{hash,end}=bucket;const dataSansEmptysetOrig=await registry.dataLoadByNameAndHash(this.name,hash,);let data=bucket.data;if(data!==null){return data[id-start];}const dataSansEmptyset=[...dataSansEmptysetOrig];let dataWithEmptyset=null;let pos=start;let insertCount=0;while(pos=48&&c<=63){dataSansEmptyset.push(backrefs[c-48]);i+=1;}else{let n=0;while(c<96){n=(n<<4)|(c&0xF);i+=1;c=data[i];}n=(n<<4)|(c&0xF);i+=1;const item=data.subarray(i,i+n);dataSansEmptyset.push(item);i+=n;backrefs.unshift(item);if(backrefs.length>16){backrefs.pop();}}}cb(null,dataSansEmptyset);}}function makeSearchTreeFromBase64(inputBase64){const input=makeUint8ArrayFromBase64(inputBase64);let i=0;const l=input.length;const stash=new HashTable();const hash=Uint8Array.of(0,0,0,0,0,0,0,0);const truncatedHash=new Uint8Array(hash.buffer,2,6);const hash_history=[];const data_history=[];let canonical=EMPTY_UINT8;let tree=new PrefixSearchTree(EMPTY_SEARCH_TREE_BRANCHES,EMPTY_SEARCH_TREE_BRANCHES,EMPTY_UINT8,EMPTY_BITMAP,EMPTY_BITMAP,);function makeBranchesFromBinaryData(input,i,compression_tag,){const is_pure_suffixes_only_node=(compression_tag&0x01)!==0x00;const is_stack_compressed=(compression_tag&0x02)!==0;const is_long_compressed=(compression_tag&0x04)!==0;const all_children_are_compressed=(compression_tag&0xF0)===0xF0&&!is_long_compressed;const any_children_are_compressed=(compression_tag&0xF0)!==0x00||is_long_compressed;const start_point=i;let cplen;let cslen;let alphabitmap=null;if(is_pure_suffixes_only_node){cplen=0;cslen=input[i];i+=1;if(cslen>=0xc0){alphabitmap=SearchTreeBranchesLongAlphaBitmap;cslen=cslen&0x3F;}else if(cslen>=0x80){alphabitmap=SearchTreeBranchesShortAlphaBitmap;cslen=cslen&0x7F;}}else{cplen=input[i];i+=1;cslen=input[i];i+=1;if(cplen===0xff&&cslen===0xff){cplen=0x100;cslen=0;}else if(cplen>=0xc0&&cslen>=0xc0){alphabitmap=SearchTreeBranchesLongAlphaBitmap;cplen=cplen&0x3F;cslen=cslen&0x3F;}else if(cplen>=0x80&&cslen>=0x80){alphabitmap=SearchTreeBranchesShortAlphaBitmap;cplen=cplen&0x7F;cslen=cslen&0x7F;}}let j=0;let cpnodes;if(any_children_are_compressed){cpnodes=cplen===0?EMPTY_UINT8:new Uint8Array(cplen*6);while(j=cplen||(csicsbranches[csi])){branchset[j]=csbranches[csi];const joff=j*6;const csioff=csi*6;hashes[joff+0]=csnodes[csioff+0];hashes[joff+1]=csnodes[csioff+1];hashes[joff+2]=csnodes[csioff+2];hashes[joff+3]=csnodes[csioff+3];hashes[joff+4]=csnodes[csioff+4];hashes[joff+5]=csnodes[csioff+5];csi+=1;}else{branchset[j]=cpbranches[cpi];const joff=j*6;const cpioff=cpi*6;hashes[joff+0]=cpnodes[cpioff+0];hashes[joff+1]=cpnodes[cpioff+1];hashes[joff+2]=cpnodes[cpioff+2];hashes[joff+3]=cpnodes[cpioff+3];hashes[joff+4]=cpnodes[cpioff+4];hashes[joff+5]=cpnodes[cpioff+5];cpi+=1;}j+=1;}branches=new SearchTreeBranchesArray(branchset,hashes);}i+=cslen;}return{consumed_len_bytes:i-start_point,cpbranches,csbranches,cpnodes,csnodes,branches,might_have_prefix_branches,};}while(i1){const is_long_compressed=(compression_tag&0x04)!==0;const is_data_compressed=(compression_tag&0x08)!==0;i+=1;if(is_long_compressed){compression_tag|=input[i]<<8;i+=1;compression_tag|=input[i]<<16;i+=1;}let dlen=input[i]&0x7F;no_leaves_flag=input[i]&0x80;i+=1;if(is_data_compressed){data=data_history[data_history.length-dlen-1];dlen=data.length;}else if(is_pure_suffixes_only_node){data=EMPTY_UINT8;}else{data=dlen===0?EMPTY_UINT8:new Uint8Array(input.buffer,i+input.byteOffset,dlen);i+=dlen;}const coffset=i;const{cpbranches,csbranches,cpnodes,csnodes,consumed_len_bytes:branches_consumed_len_bytes,branches,might_have_prefix_branches,}=makeBranchesFromBinaryData(input,i,compression_tag);i+=branches_consumed_len_bytes;let whole;let suffix;if(is_pure_suffixes_only_node){if(no_leaves_flag){whole=EMPTY_BITMAP;suffix=EMPTY_BITMAP;}else{suffix=input[i]===0?EMPTY_BITMAP1:new RoaringBitmap(input,i);i+=suffix.consumed_len_bytes;}tree=new SuffixSearchTree(branches,dlen,suffix,);const clen=(3+csnodes.length+csbranches.length+suffix.consumed_len_bytes);if(canonical.length{registry.searchTreeRootCallback=(error,data)=>{if(data){resolve(data);}else{reject(error);}};hooks.loadRoot(callbacks);});}if(typeof window!=="undefined"){window.Stringdex={loadDatabase,};window.RoaringBitmap=RoaringBitmap;if(window.StringdexOnload){window.StringdexOnload.forEach(cb=>cb(window.Stringdex));}}else{module.exports.Stringdex={loadDatabase,};module.exports.RoaringBitmap=RoaringBitmap;}const makeUint8ArrayFromBase64=Uint8Array.fromBase64?Uint8Array.fromBase64:(string=>{const bytes_as_string=atob(string);const l=bytes_as_string.length;const bytes=new Uint8Array(l);for(let i=0;i{const alpha={"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9,"a":10,"b":11,"A":10,"B":11,"c":12,"d":13,"C":12,"D":13,"e":14,"f":15,"E":14,"F":15,};const l=string.length>>1;const bytes=new Uint8Array(l);for(let i=0;iarray.toHex()):(array=>{const alpha=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",];const l=array.length;const v=[];for(let i=0;i>4]);v.push(alpha[array[i]&0xf]);}return v.join("");});function siphashOfBytes(input,k0lo,k0hi,k1lo,k1hi,output){let v0lo=k0lo ^ 0x70736575;let v0hi=k0hi ^ 0x736f6d65;let v1lo=k1lo ^ 0x6e646f6d;let v1hi=k1hi ^ 0x646f7261;let v2lo=k0lo ^ 0x6e657261;let v2hi=k0hi ^ 0x6c796765;let v3lo=k1lo ^ 0x79746573;let v3hi=k1hi ^ 0x74656462;const inputLength=input.length;let inputI=0;const left=inputLength&0x7;let milo=0;let mihi=0;while(inputI>>8;output[5]=(v0lo ^ v1lo ^ v2lo ^ v3lo)>>>16;output[4]=(v0lo ^ v1lo ^ v2lo ^ v3lo)>>>24;output[3]=(v0hi ^ v1hi ^ v2hi ^ v3hi)&0xff;output[2]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>8;output[1]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>16;output[0]=(v0hi ^ v1hi ^ v2hi ^ v3hi)>>>24;function u8ToU64le(offset,length){const n0=offset>>0)+(v1lo>>>0)>0xffffffff)?1:0))|0;v0lo=(v0lo+v1lo)|0;let v1lo_=v1lo;let v1hi_=v1hi;v1lo=(v1lo_<<13)|(v1hi_>>>19);v1hi=(v1hi_<<13)|(v1lo_>>>19);v1lo ^=v0lo;v1hi ^=v0hi;const v0lo_=v0lo;const v0hi_=v0hi;v0lo=v0hi_;v0hi=v0lo_;v2hi=(v2hi+v3hi+(((v2lo>>>0)+(v3lo>>>0)>0xffffffff)?1:0))|0;v2lo=(v2lo+v3lo)|0;let v3lo_=v3lo;let v3hi_=v3hi;v3lo=(v3lo_<<16)|(v3hi_>>>16);v3hi=(v3hi_<<16)|(v3lo_>>>16);v3lo ^=v2lo;v3hi ^=v2hi;v0hi=(v0hi+v3hi+(((v0lo>>>0)+(v3lo>>>0)>0xffffffff)?1:0))|0;v0lo=(v0lo+v3lo)|0;v3lo_=v3lo;v3hi_=v3hi;v3lo=(v3lo_<<21)|(v3hi_>>>11);v3hi=(v3hi_<<21)|(v3lo_>>>11);v3lo ^=v0lo;v3hi ^=v0hi;v2hi=(v2hi+v1hi+(((v2lo>>>0)+(v1lo>>>0)>0xffffffff)?1:0))|0;v2lo=(v2lo+v1lo)|0;v1lo_=v1lo;v1hi_=v1hi;v1lo=(v1lo_<<17)|(v1hi_>>>15);v1hi=(v1hi_<<17)|(v1lo_>>>15);v1lo ^=v2lo;v1hi ^=v2hi;const v2lo_=v2lo;const v2hi_=v2hi;v2lo=v2hi_;v2hi=v2lo_;}}class ParametricDescription{constructor(w,n,minErrors){this.w=w;this.n=n;this.minErrors=minErrors;}isAccept(absState){const state=Math.floor(absState/(this.w+1));const offset=absState%(this.w+1);return this.w-offset+this.minErrors[state]<=this.n;}getPosition(absState){return absState%(this.w+1);}getVector(name,charCode,pos,end){let vector=0;for(let i=pos;i>5;const bitStart=bitLoc&31;if(bitStart+bitsPerValue<=32){return((data[dataLoc]>>bitStart)&this.MASKS[bitsPerValue-1]);}else{const part=32-bitStart;return ~~(((data[dataLoc]>>bitStart)&this.MASKS[part-1])+((data[1+dataLoc]&this.MASKS[bitsPerValue-part-1])<Dir"],["impl<'arbitrary> Arbitrary<'arbitrary> for Side"],["impl<'arbitrary> Arbitrary<'arbitrary> for StreamId"],["impl<'arbitrary> Arbitrary<'arbitrary> for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[709]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.Args.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.Args.js new file mode 100644 index 000000000..0ebbfe755 --- /dev/null +++ b/pr/234/docs/trait.impl/clap_builder/derive/trait.Args.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Args for Opt"]]],["perf",[["impl Args for Opt"],["impl Args for Opt"],["impl Args for CommonOpt"]]],["quinn_perf",[["impl Args for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[116,368,132]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js new file mode 100644 index 000000000..6b11f051f --- /dev/null +++ b/pr/234/docs/trait.impl/clap_builder/derive/trait.CommandFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl CommandFactory for Opt"]]],["perf",[["impl CommandFactory for Opt"],["impl CommandFactory for Opt"],["impl CommandFactory for CommonOpt"]]],["quinn_perf",[["impl CommandFactory for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,398,142]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js new file mode 100644 index 000000000..48a955b6f --- /dev/null +++ b/pr/234/docs/trait.impl/clap_builder/derive/trait.FromArgMatches.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl FromArgMatches for Opt"]]],["perf",[["impl FromArgMatches for Opt"],["impl FromArgMatches for Opt"],["impl FromArgMatches for CommonOpt"]]],["quinn_perf",[["impl FromArgMatches for Commands"],["impl FromArgMatches for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[126,398,276]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.Parser.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.Parser.js new file mode 100644 index 000000000..823b963bf --- /dev/null +++ b/pr/234/docs/trait.impl/clap_builder/derive/trait.Parser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Parser for Opt"]]],["perf",[["impl Parser for Opt"],["impl Parser for Opt"],["impl Parser for CommonOpt"]]],["quinn_perf",[["impl Parser for Cli"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[118,374,134]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.Subcommand.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.Subcommand.js new file mode 100644 index 000000000..18f0f2f47 --- /dev/null +++ b/pr/234/docs/trait.impl/clap_builder/derive/trait.Subcommand.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["quinn_perf",[["impl Subcommand for Commands"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[146]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js b/pr/234/docs/trait.impl/clap_builder/derive/trait.ValueEnum.js new file mode 100644 index 000000000..c39cd5dd7 --- /dev/null +++ b/pr/234/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/234/docs/trait.impl/core/clone/trait.Clone.js b/pr/234/docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 000000000..b4edd1538 --- /dev/null +++ b/pr/234/docs/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Clone for CipherSuite"],["impl Clone for Opt"]]],["iroh_quinn",[["impl Clone for ReadError"],["impl Clone for ReadExactError"],["impl Clone for ReadToEndError"],["impl Clone for ResetError"],["impl Clone for SendDatagramError"],["impl Clone for StoppedError"],["impl Clone for WriteError"],["impl Clone for Connection"],["impl Clone for Endpoint"],["impl Clone for EndpointStats"],["impl Clone for WeakConnectionHandle"]]],["iroh_quinn_proto",[["impl Clone for ClosePathError"],["impl Clone for ConfigError"],["impl Clone for ConnectError"],["impl Clone for ConnectionError"],["impl Clone for Dir"],["impl Clone for EcnCodepoint"],["impl Clone for FinishError"],["impl Clone for LongType"],["impl Clone for PacketDecodeError"],["impl Clone for PathError"],["impl Clone for PathEvent"],["impl Clone for PathStatus"],["impl Clone for ProtectedHeader"],["impl Clone for ReadError"],["impl Clone for ReadableError"],["impl Clone for SendDatagramError"],["impl Clone for SetPathStatusError"],["impl Clone for Side"],["impl Clone for WriteError"],["impl Clone for Event"],["impl Clone for Error"],["impl Clone for UnexpectedEnd"],["impl Clone for Bbr"],["impl Clone for BbrConfig"],["impl Clone for Cubic"],["impl Clone for CubicConfig"],["impl Clone for NewReno"],["impl Clone for NewRenoConfig"],["impl Clone for NoInitialCipherSuite"],["impl Clone for AckFrequencyConfig"],["impl Clone for ApplicationClose"],["impl Clone for ClientConfig"],["impl Clone for ClosedPath"],["impl Clone for ClosedStream"],["impl Clone for ConnectionClose"],["impl Clone for ConnectionHandle"],["impl Clone for ConnectionId"],["impl Clone for ConnectionStats"],["impl Clone for Datagram"],["impl Clone for EndpointConfig"],["impl Clone for FrameStats"],["impl Clone for FrameType"],["impl Clone for IdleTimeout"],["impl Clone for InvalidCid"],["impl Clone for MtuDiscoveryConfig"],["impl Clone for PathId"],["impl Clone for PathStats"],["impl Clone for ProtectedInitialHeader"],["impl Clone for RandomConnectionIdGenerator"],["impl Clone for RttEstimator"],["impl Clone for ServerConfig"],["impl Clone for ShouldTransmit"],["impl Clone for StreamId"],["impl Clone for TransportConfig"],["impl Clone for Error"],["impl Clone for Code"],["impl Clone for UdpStats"],["impl Clone for ValidationTokenConfig"],["impl Clone for VarInt"],["impl Clone for VarIntBoundsExceeded"],["impl Clone for Written"],["impl Clone for TransportParameters"]]],["iroh_quinn_udp",[["impl Clone for EcnCodepoint"],["impl Clone for RecvMeta"],["impl<'a> Clone for Transmit<'a>"]]],["perf",[["impl Clone for CongestionAlgorithm"],["impl Clone for OpenStreamStats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[499,2982,17792,851,568]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/cmp/trait.Eq.js b/pr/234/docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 000000000..3df6d107a --- /dev/null +++ b/pr/234/docs/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Eq for CipherSuite"]]],["iroh_quinn",[["impl Eq for ReadError"],["impl Eq for ReadExactError"],["impl Eq for ReadToEndError"],["impl Eq for ResetError"],["impl Eq for SendDatagramError"],["impl Eq for StoppedError"],["impl Eq for WriteError"]]],["iroh_quinn_proto",[["impl Eq for ClosePathError"],["impl Eq for ConfigError"],["impl Eq for ConnectError"],["impl Eq for ConnectionError"],["impl Eq for Dir"],["impl Eq for EcnCodepoint"],["impl Eq for FinishError"],["impl Eq for LongType"],["impl Eq for PacketDecodeError"],["impl Eq for PathError"],["impl Eq for PathEvent"],["impl Eq for PathStatus"],["impl Eq for ReadError"],["impl Eq for ReadableError"],["impl Eq for SendDatagramError"],["impl Eq for SetPathStatusError"],["impl Eq for Side"],["impl Eq for StreamEvent"],["impl Eq for WriteError"],["impl Eq for Error"],["impl Eq for UnexpectedEnd"],["impl Eq for ExportKeyingMaterialError"],["impl Eq for ApplicationClose"],["impl Eq for Chunk"],["impl Eq for ClosedPath"],["impl Eq for ClosedStream"],["impl Eq for ConnectionClose"],["impl Eq for ConnectionHandle"],["impl Eq for ConnectionId"],["impl Eq for FrameType"],["impl Eq for IdleTimeout"],["impl Eq for PathId"],["impl Eq for PathStats"],["impl Eq for ShouldTransmit"],["impl Eq for StreamId"],["impl Eq for Error"],["impl Eq for Code"],["impl Eq for VarInt"],["impl Eq for VarIntBoundsExceeded"],["impl Eq for Written"],["impl Eq for TransportParameters"]]],["iroh_quinn_udp",[["impl Eq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[251,1794,11080,282]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/cmp/trait.Ord.js b/pr/234/docs/trait.impl/core/cmp/trait.Ord.js new file mode 100644 index 000000000..8af068c55 --- /dev/null +++ b/pr/234/docs/trait.impl/core/cmp/trait.Ord.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Ord for Dir"],["impl Ord for PacketDecodeError"],["impl Ord for ReadError"],["impl Ord for ReadableError"],["impl Ord for SendDatagramError"],["impl Ord for Side"],["impl Ord for WriteError"],["impl Ord for ConnectionHandle"],["impl Ord for ConnectionId"],["impl Ord for IdleTimeout"],["impl Ord for PathId"],["impl Ord for StreamId"],["impl Ord for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3470]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/cmp/trait.PartialEq.js b/pr/234/docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 000000000..ca2274433 --- /dev/null +++ b/pr/234/docs/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl PartialEq for CipherSuite"]]],["iroh_quinn",[["impl PartialEq for ReadError"],["impl PartialEq for ReadExactError"],["impl PartialEq for ReadToEndError"],["impl PartialEq for ResetError"],["impl PartialEq for SendDatagramError"],["impl PartialEq for StoppedError"],["impl PartialEq for WriteError"]]],["iroh_quinn_proto",[["impl PartialEq for ClosePathError"],["impl PartialEq for ConfigError"],["impl PartialEq for ConnectError"],["impl PartialEq for ConnectionError"],["impl PartialEq for Dir"],["impl PartialEq for EcnCodepoint"],["impl PartialEq for FinishError"],["impl PartialEq for LongType"],["impl PartialEq for PacketDecodeError"],["impl PartialEq for PathError"],["impl PartialEq for PathEvent"],["impl PartialEq for PathStatus"],["impl PartialEq for ReadError"],["impl PartialEq for ReadableError"],["impl PartialEq for SendDatagramError"],["impl PartialEq for SetPathStatusError"],["impl PartialEq for Side"],["impl PartialEq for StreamEvent"],["impl PartialEq for WriteError"],["impl PartialEq for Error"],["impl PartialEq for UnexpectedEnd"],["impl PartialEq for ExportKeyingMaterialError"],["impl PartialEq for ApplicationClose"],["impl PartialEq for Chunk"],["impl PartialEq for ClosedPath"],["impl PartialEq for ClosedStream"],["impl PartialEq for ConnectionClose"],["impl PartialEq for ConnectionHandle"],["impl PartialEq for ConnectionId"],["impl PartialEq for FrameType"],["impl PartialEq for IdleTimeout"],["impl PartialEq for PathId"],["impl PartialEq for PathStats"],["impl PartialEq for ShouldTransmit"],["impl PartialEq for StreamId"],["impl PartialEq for Error"],["impl PartialEq for Code"],["impl PartialEq for VarInt"],["impl PartialEq for VarIntBoundsExceeded"],["impl PartialEq for Written"],["impl PartialEq for TransportParameters"]]],["iroh_quinn_udp",[["impl PartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[272,1941,11941,303]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/cmp/trait.PartialOrd.js b/pr/234/docs/trait.impl/core/cmp/trait.PartialOrd.js new file mode 100644 index 000000000..fa81432c6 --- /dev/null +++ b/pr/234/docs/trait.impl/core/cmp/trait.PartialOrd.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl PartialOrd for Dir"],["impl PartialOrd for PacketDecodeError"],["impl PartialOrd for ReadError"],["impl PartialOrd for ReadableError"],["impl PartialOrd for SendDatagramError"],["impl PartialOrd for Side"],["impl PartialOrd for WriteError"],["impl PartialOrd for ConnectionHandle"],["impl PartialOrd for ConnectionId"],["impl PartialOrd for IdleTimeout"],["impl PartialOrd for PathId"],["impl PartialOrd for StreamId"],["impl PartialOrd for VarInt"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3743]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/convert/trait.From.js b/pr/234/docs/trait.impl/core/convert/trait.From.js new file mode 100644 index 000000000..4a0bba414 --- /dev/null +++ b/pr/234/docs/trait.impl/core/convert/trait.From.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl From<ConnectionError> for ReadError"],["impl From<ConnectionError> for ResetError"],["impl From<ConnectionError> for SendDatagramError"],["impl From<ConnectionError> for StoppedError"],["impl From<ConnectionError> for WriteError"],["impl From<ReadError> for ReadExactError"],["impl From<ReadError> for ReadToEndError"],["impl From<ReadError> for Error"],["impl From<ResetError> for ReadError"],["impl From<ResetError> for Error"],["impl From<StoppedError> for WriteError"],["impl From<StoppedError> for Error"],["impl From<WriteError> for Error"],["impl From<ReadableError> for ReadError"],["impl From<ClosedStream> for WriteError"]]],["iroh_quinn_proto",[["impl From<ConnectionError> for Error"],["impl From<Dir> for StreamType"],["impl From<PathEvent> for Event"],["impl From<Side> for Side"],["impl From<Error> for TransportError"],["impl From<u16> for VarInt"],["impl From<u32> for VarInt"],["impl From<u8> for VarInt"],["impl From<TryFromIntError> for ConfigError"],["impl From<UnexpectedEnd> for PacketDecodeError"],["impl From<UnexpectedEnd> for Error"],["impl From<UnsupportedVersion> for ConnectError"],["impl From<ClosedStream> for Error"],["impl From<ConnectionHandle> for usize"],["impl From<StreamId> for u64"],["impl From<StreamId> for VarInt"],["impl From<Error> for ConnectionError"],["impl From<Error> for ConnectionClose"],["impl From<Code> for u64"],["impl From<Code> for VarInt"],["impl From<VarInt> for u64"],["impl From<VarInt> for IdleTimeout"],["impl From<VarInt> for StreamId"],["impl From<VarInt> for Code"],["impl From<VarIntBoundsExceeded> for ConfigError"],["impl From<Unspecified> for CryptoError"],["impl<T: Into<u32>> From<T> for PathId"]]],["iroh_quinn_udp",[["impl<'s, S> From<&'s S> for UdpSockRef<'s>
where\n S: AsFd,
"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[6044,10936,620]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/convert/trait.TryFrom.js b/pr/234/docs/trait.impl/core/convert/trait.TryFrom.js new file mode 100644 index 000000000..79e9e3025 --- /dev/null +++ b/pr/234/docs/trait.impl/core/convert/trait.TryFrom.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl TryFrom<u128> for VarInt"],["impl TryFrom<u64> for 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":[3313]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/default/trait.Default.js b/pr/234/docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 000000000..51d382b93 --- /dev/null +++ b/pr/234/docs/trait.impl/core/default/trait.Default.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Default for Stats"],["impl Default for StreamStats"]]],["bulk",[["impl Default for ClientStats"]]],["iroh_quinn",[["impl Default for EndpointStats"]]],["iroh_quinn_proto",[["impl Default for PathStatus"],["impl Default for BbrConfig"],["impl Default for ControllerMetrics"],["impl Default for CubicConfig"],["impl Default for NewRenoConfig"],["impl Default for AckFrequencyConfig"],["impl Default for BloomTokenLog"],["impl Default for ClosedPath"],["impl Default for ClosedStream"],["impl Default for ConnectionStats"],["impl Default for EndpointConfig"],["impl Default for FrameStats"],["impl Default for HashedConnectionIdGenerator"],["impl Default for IdleTimeout"],["impl Default for MtuDiscoveryConfig"],["impl Default for PathId"],["impl Default for PathStats"],["impl Default for RandomConnectionIdGenerator"],["impl Default for ShouldTransmit"],["impl Default for TokenMemoryCache"],["impl Default for TransportConfig"],["impl Default for UdpStats"],["impl Default for ValidationTokenConfig"],["impl Default for VarInt"],["impl Default for Written"]]],["iroh_quinn_udp",[["impl Default for RecvMeta"]]],["perf",[["impl Default for OpenStreamStats"],["impl Default for Stats"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[557,278,302,7570,299,565]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/error/trait.Error.js b/pr/234/docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 000000000..212b4f6df --- /dev/null +++ b/pr/234/docs/trait.impl/core/error/trait.Error.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Error for ReadError"],["impl Error for ReadExactError"],["impl Error for ReadToEndError"],["impl Error for ResetError"],["impl Error for SendDatagramError"],["impl Error for StoppedError"],["impl Error for WriteError"],["impl Error for RetryError"]]],["iroh_quinn_proto",[["impl Error for ClosePathError"],["impl Error for ConfigError"],["impl Error for ConnectError"],["impl Error for ConnectionError"],["impl Error for FinishError"],["impl Error for PacketDecodeError"],["impl Error for PathError"],["impl Error for ReadError"],["impl Error for ReadableError"],["impl Error for SendDatagramError"],["impl Error for SetPathStatusError"],["impl Error for WriteError"],["impl Error for Error"],["impl Error for Error"],["impl Error for UnexpectedEnd"],["impl Error for NoInitialCipherSuite"],["impl Error for ClosedPath"],["impl Error for ClosedStream"],["impl Error for RetryError"],["impl Error for Error"],["impl Error for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2150,6038]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/fmt/trait.Debug.js b/pr/234/docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 000000000..aee266cf3 --- /dev/null +++ b/pr/234/docs/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Debug for CipherSuite"],["impl Debug for TransferResult"],["impl Debug for Opt"]]],["certificate",[["impl Debug for SkipServerVerification"]]],["iroh_quinn",[["impl Debug for ReadError"],["impl Debug for ReadExactError"],["impl Debug for ReadToEndError"],["impl Debug for ResetError"],["impl Debug for SendDatagramError"],["impl Debug for StoppedError"],["impl Debug for WriteError"],["impl Debug for Connecting"],["impl Debug for Connection"],["impl Debug for Endpoint"],["impl Debug for EndpointStats"],["impl Debug for Incoming"],["impl Debug for IncomingFuture"],["impl Debug for Path"],["impl Debug for RecvStream"],["impl Debug for RetryError"],["impl Debug for SendStream"],["impl Debug for SmolRuntime"],["impl Debug for TokioRuntime"],["impl Debug for WeakConnectionHandle"]]],["iroh_quinn_proto",[["impl Debug for ClosePathError"],["impl Debug for ConfigError"],["impl Debug for ConnectError"],["impl Debug for ConnectionError"],["impl Debug for Dir"],["impl Debug for EcnCodepoint"],["impl Debug for Event"],["impl Debug for FinishError"],["impl Debug for LongType"],["impl Debug for PacketDecodeError"],["impl Debug for PathError"],["impl Debug for PathEvent"],["impl Debug for PathStatus"],["impl Debug for ProtectedHeader"],["impl Debug for ReadError"],["impl Debug for ReadableError"],["impl Debug for SendDatagramError"],["impl Debug for SetPathStatusError"],["impl Debug for Side"],["impl Debug for StreamEvent"],["impl Debug for WriteError"],["impl Debug for Error"],["impl Debug for Event"],["impl Debug for Error"],["impl Debug for UnexpectedEnd"],["impl Debug for Bbr"],["impl Debug for BbrConfig"],["impl Debug for ControllerMetrics"],["impl Debug for Cubic"],["impl Debug for CubicConfig"],["impl Debug for NewReno"],["impl Debug for NewRenoConfig"],["impl Debug for NoInitialCipherSuite"],["impl Debug for CryptoError"],["impl Debug for ExportKeyingMaterialError"],["impl Debug for UnsupportedVersion"],["impl Debug for AcceptError"],["impl Debug for AckFrequencyConfig"],["impl Debug for ApplicationClose"],["impl Debug for Chunk"],["impl Debug for ClientConfig"],["impl Debug for ClosedPath"],["impl Debug for ClosedStream"],["impl Debug for Connection"],["impl Debug for ConnectionClose"],["impl Debug for ConnectionEvent"],["impl Debug for ConnectionHandle"],["impl Debug for ConnectionId"],["impl Debug for ConnectionStats"],["impl Debug for Datagram"],["impl Debug for Endpoint"],["impl Debug for EndpointConfig"],["impl Debug for EndpointEvent"],["impl Debug for FrameStats"],["impl Debug for FrameType"],["impl Debug for IdleTimeout"],["impl Debug for Incoming"],["impl Debug for InvalidCid"],["impl Debug for MtuDiscoveryConfig"],["impl Debug for PartialDecode"],["impl Debug for PathId"],["impl Debug for PathStats"],["impl Debug for ProtectedInitialHeader"],["impl Debug for QlogFileFactory"],["impl Debug for RandomConnectionIdGenerator"],["impl Debug for RetryError"],["impl Debug for RttEstimator"],["impl Debug for ServerConfig"],["impl Debug for ShouldTransmit"],["impl Debug for StreamId"],["impl Debug for TokenMemoryCache"],["impl Debug for Transmit"],["impl Debug for TransportConfig"],["impl Debug for Error"],["impl Debug for Code"],["impl Debug for UdpStats"],["impl Debug for ValidationTokenConfig"],["impl Debug for VarInt"],["impl Debug for VarIntBoundsExceeded"],["impl Debug for Written"],["impl Debug for TransportParameters"]]],["iroh_quinn_udp",[["impl Debug for EcnCodepoint"],["impl Debug for RecvMeta"],["impl Debug for UdpSocketState"],["impl<'a> Debug for Transmit<'a>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[768,318,5293,22919,1121]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/fmt/trait.Display.js b/pr/234/docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 000000000..9def6d6a1 --- /dev/null +++ b/pr/234/docs/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Display for ReadError"],["impl Display for ReadExactError"],["impl Display for ReadToEndError"],["impl Display for ResetError"],["impl Display for SendDatagramError"],["impl Display for StoppedError"],["impl Display for WriteError"],["impl Display for RetryError"]]],["iroh_quinn_proto",[["impl Display for ClosePathError"],["impl Display for ConfigError"],["impl Display for ConnectError"],["impl Display for ConnectionError"],["impl Display for Dir"],["impl Display for FinishError"],["impl Display for 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 FrameType"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["impl Display for Error"],["impl Display for Code"],["impl Display for VarInt"],["impl Display for VarIntBoundsExceeded"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[2166,8589]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/future/future/trait.Future.js b/pr/234/docs/trait.impl/core/future/future/trait.Future.js new file mode 100644 index 000000000..3c2fb2ac9 --- /dev/null +++ b/pr/234/docs/trait.impl/core/future/future/trait.Future.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Future for Accept<'_>"],["impl Future for AcceptBi<'_>"],["impl Future for AcceptUni<'_>"],["impl Future for Connecting"],["impl Future for IncomingFuture"],["impl Future for OnClosed"],["impl Future for OpenBi<'_>"],["impl Future for OpenPath"],["impl Future for OpenUni<'_>"],["impl Future for ReadDatagram<'_>"],["impl Future for SendDatagram<'_>"],["impl Future for ZeroRttAccepted"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[3503]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/future/into_future/trait.IntoFuture.js b/pr/234/docs/trait.impl/core/future/into_future/trait.IntoFuture.js new file mode 100644 index 000000000..b07d4bb68 --- /dev/null +++ b/pr/234/docs/trait.impl/core/future/into_future/trait.IntoFuture.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl IntoFuture for Incoming"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[318]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/hash/trait.Hash.js b/pr/234/docs/trait.impl/core/hash/trait.Hash.js new file mode 100644 index 000000000..bbca65b59 --- /dev/null +++ b/pr/234/docs/trait.impl/core/hash/trait.Hash.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Hash for Dir"],["impl Hash for PacketDecodeError"],["impl Hash for ReadError"],["impl Hash for ReadableError"],["impl Hash for SendDatagramError"],["impl Hash for Side"],["impl Hash for WriteError"],["impl Hash for ConnectionHandle"],["impl Hash for ConnectionId"],["impl Hash for 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":[3535]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.Copy.js b/pr/234/docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 000000000..09a6b223d --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Copy for CipherSuite"],["impl Copy for Opt"]]],["iroh_quinn",[["impl Copy for EndpointStats"]]],["iroh_quinn_proto",[["impl Copy for Dir"],["impl Copy for EcnCodepoint"],["impl Copy for LongType"],["impl Copy for PathError"],["impl Copy for PathStatus"],["impl Copy for Side"],["impl Copy for Error"],["impl Copy for UnexpectedEnd"],["impl Copy for ConnectionHandle"],["impl Copy for ConnectionId"],["impl Copy for FrameStats"],["impl Copy for FrameType"],["impl Copy for IdleTimeout"],["impl Copy for InvalidCid"],["impl Copy for PathId"],["impl Copy for PathStats"],["impl Copy for RandomConnectionIdGenerator"],["impl Copy for RttEstimator"],["impl Copy for ShouldTransmit"],["impl Copy for StreamId"],["impl Copy for Code"],["impl Copy for UdpStats"],["impl Copy for VarInt"],["impl Copy for VarIntBoundsExceeded"],["impl Copy for Written"],["impl Copy for TransportParameters"]]],["iroh_quinn_udp",[["impl Copy for EcnCodepoint"],["impl Copy for RecvMeta"]]],["perf",[["impl Copy for CongestionAlgorithm"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[497,291,7348,561,285]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.Freeze.js b/pr/234/docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 000000000..51bd49c3a --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Freeze for CipherSuite",1,["bench::CipherSuite"]],["impl Freeze for Stats",1,["bench::stats::Stats"]],["impl Freeze for StreamStats",1,["bench::stats::StreamStats"]],["impl Freeze for TransferResult",1,["bench::stats::TransferResult"]],["impl Freeze for Opt",1,["bench::Opt"]]]],["bulk",[["impl Freeze for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Freeze for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !Freeze for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !Freeze for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !Freeze for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !Freeze for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !Freeze for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !Freeze for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !Freeze for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !Freeze for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !Freeze for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Freeze for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Freeze for Connection",1,["iroh_quinn::connection::Connection"]],["impl Freeze for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Freeze for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Freeze for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Freeze for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Freeze for Path",1,["iroh_quinn::path::Path"]],["impl Freeze for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Freeze for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Freeze for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Freeze for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Freeze for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Freeze for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Freeze for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> !Freeze for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !Freeze for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !Freeze for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !Freeze for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !Freeze for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !Freeze for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !Freeze for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !Freeze for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !Freeze for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !Freeze for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !Freeze for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl !Freeze for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !Freeze for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl !Freeze for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl !Freeze for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl !Freeze for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !Freeze for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl !Freeze for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl !Freeze for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !Freeze for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Freeze for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Freeze for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Freeze for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Freeze for Dir",1,["iroh_quinn_proto::Dir"]],["impl Freeze for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Freeze for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Freeze for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Freeze for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Freeze for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Freeze for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Freeze for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Freeze for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Freeze for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Freeze for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Freeze for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Freeze for Side",1,["iroh_quinn_proto::Side"]],["impl Freeze for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Freeze for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Freeze for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Freeze for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Freeze for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Freeze for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Freeze for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Freeze for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Freeze for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Freeze for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Freeze for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Freeze for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Freeze for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Freeze for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Freeze for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Freeze for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Freeze for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Freeze for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Freeze for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Freeze for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Freeze for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Freeze for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Freeze for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Freeze for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Freeze for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Freeze for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Freeze for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Freeze for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Freeze for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Freeze for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Freeze for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Freeze for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Freeze for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Freeze for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Freeze for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Freeze for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Freeze for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Freeze for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Freeze for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Freeze for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Freeze for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Freeze for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Freeze for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Freeze for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Freeze for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Freeze for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Freeze for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Freeze for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Freeze for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Freeze for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Freeze for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Freeze for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Freeze for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Freeze for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Freeze for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Freeze for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Freeze for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Freeze for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Freeze for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Freeze for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Freeze for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Freeze for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Freeze for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Freeze for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Freeze for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Freeze for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Freeze for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Freeze for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Freeze for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Freeze for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Freeze for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Freeze for KeyPair<T>
where\n T: Freeze,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl !Freeze for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl Freeze for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Freeze for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl<'a> Freeze for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Freeze for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !Freeze for StreamStats",1,["perf::stats::StreamStats"]],["impl Freeze for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Freeze for Opt",1,["perf::client::Opt"]],["impl Freeze for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Freeze for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Freeze for Opt",1,["perf::server::Opt"]],["impl Freeze for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Freeze for Stats",1,["perf::stats::Stats"]],["impl Freeze for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Freeze for Commands",1,["quinn_perf::Commands"]],["impl Freeze for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1466,297,369,9550,35066,1629,2824,575]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.Send.js b/pr/234/docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 000000000..e566d20a5 --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Send for CipherSuite",1,["bench::CipherSuite"]],["impl Send for Stats",1,["bench::stats::Stats"]],["impl Send for StreamStats",1,["bench::stats::StreamStats"]],["impl Send for TransferResult",1,["bench::stats::TransferResult"]],["impl Send for Opt",1,["bench::Opt"]]]],["bulk",[["impl Send for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Send for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Send for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Send for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Send for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Send for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Send for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Send for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Send for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Send for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Send for Connection",1,["iroh_quinn::connection::Connection"]],["impl Send for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Send for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Send for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Send for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Send for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Send for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Send for Path",1,["iroh_quinn::path::Path"]],["impl Send for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Send for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Send for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Send for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Send for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Send for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Send for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> Send for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> Send for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> Send for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> Send for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> Send for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> Send for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> Send for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl Send for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Send for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Send for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Send for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Send for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Send for Dir",1,["iroh_quinn_proto::Dir"]],["impl Send for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Send for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Send for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Send for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Send for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Send for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Send for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Send for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Send for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Send for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Send for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Send for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Send for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Send for Side",1,["iroh_quinn_proto::Side"]],["impl Send for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Send for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Send for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Send for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Send for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Send for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Send for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Send for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Send for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Send for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Send for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Send for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Send for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Send for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Send for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Send for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Send for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Send for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Send for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Send for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Send for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Send for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Send for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Send for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Send for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Send for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Send for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Send for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Send for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Send for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Send for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Send for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Send for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Send for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Send for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Send for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Send for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Send for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Send for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Send for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Send for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Send for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Send for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Send for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Send for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Send for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Send for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Send for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Send for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Send for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Send for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Send for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Send for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Send for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Send for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Send for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Send for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Send for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Send for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Send for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Send for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Send for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Send for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Send for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Send for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Send for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Send for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Send for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Send for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Send for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Send for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Send for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Send for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Send for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Send for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Send for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Send for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Send for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Send for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Send for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Send for KeyPair<T>
where\n T: Send,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Send for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Send for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Send for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Send for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Send for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Send for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Send for Opt",1,["perf::client::Opt"]],["impl Send for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Send for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Send for Opt",1,["perf::server::Opt"]],["impl Send for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Send for Stats",1,["perf::stats::Stats"]],["impl Send for StreamStats",1,["perf::stats::StreamStats"]],["impl Send for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Send for Commands",1,["quinn_perf::Commands"]],["impl Send for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1436,291,363,9354,34441,1598,2769,563]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/pr/234/docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 000000000..52bf2ee51 --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl StructuralPartialEq for CipherSuite"]]],["iroh_quinn",[["impl StructuralPartialEq for ReadError"],["impl StructuralPartialEq for ReadExactError"],["impl StructuralPartialEq for ReadToEndError"],["impl StructuralPartialEq for ResetError"],["impl StructuralPartialEq for SendDatagramError"],["impl StructuralPartialEq for StoppedError"],["impl StructuralPartialEq for WriteError"]]],["iroh_quinn_proto",[["impl StructuralPartialEq for ClosePathError"],["impl StructuralPartialEq for ConfigError"],["impl StructuralPartialEq for ConnectError"],["impl StructuralPartialEq for ConnectionError"],["impl StructuralPartialEq for Dir"],["impl StructuralPartialEq for EcnCodepoint"],["impl StructuralPartialEq for FinishError"],["impl StructuralPartialEq for LongType"],["impl StructuralPartialEq for PacketDecodeError"],["impl StructuralPartialEq for PathError"],["impl StructuralPartialEq for PathEvent"],["impl StructuralPartialEq for PathStatus"],["impl StructuralPartialEq for ReadError"],["impl StructuralPartialEq for ReadableError"],["impl StructuralPartialEq for SendDatagramError"],["impl StructuralPartialEq for SetPathStatusError"],["impl StructuralPartialEq for Side"],["impl StructuralPartialEq for StreamEvent"],["impl StructuralPartialEq for WriteError"],["impl StructuralPartialEq for Error"],["impl StructuralPartialEq for UnexpectedEnd"],["impl StructuralPartialEq for ExportKeyingMaterialError"],["impl StructuralPartialEq for ApplicationClose"],["impl StructuralPartialEq for Chunk"],["impl StructuralPartialEq for ClosedPath"],["impl StructuralPartialEq for ClosedStream"],["impl StructuralPartialEq for ConnectionClose"],["impl StructuralPartialEq for ConnectionHandle"],["impl StructuralPartialEq for ConnectionId"],["impl StructuralPartialEq for FrameType"],["impl StructuralPartialEq for IdleTimeout"],["impl StructuralPartialEq for PathId"],["impl StructuralPartialEq for PathStats"],["impl StructuralPartialEq for ShouldTransmit"],["impl StructuralPartialEq for StreamId"],["impl StructuralPartialEq for Code"],["impl StructuralPartialEq for VarInt"],["impl StructuralPartialEq for VarIntBoundsExceeded"],["impl StructuralPartialEq for Written"],["impl StructuralPartialEq for TransportParameters"]]],["iroh_quinn_udp",[["impl StructuralPartialEq for EcnCodepoint"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[308,2193,13092,339]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.Sync.js b/pr/234/docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 000000000..8d31df71a --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Sync for CipherSuite",1,["bench::CipherSuite"]],["impl Sync for Stats",1,["bench::stats::Stats"]],["impl Sync for StreamStats",1,["bench::stats::StreamStats"]],["impl Sync for TransferResult",1,["bench::stats::TransferResult"]],["impl Sync for Opt",1,["bench::Opt"]]]],["bulk",[["impl Sync for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Sync for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Sync for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Sync for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Sync for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Sync for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Sync for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Sync for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Sync for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Sync for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Sync for Connection",1,["iroh_quinn::connection::Connection"]],["impl Sync for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Sync for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Sync for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Sync for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Sync for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Sync for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Sync for Path",1,["iroh_quinn::path::Path"]],["impl Sync for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Sync for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Sync for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Sync for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Sync for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Sync for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Sync for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> Sync for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> Sync for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> Sync for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> Sync for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> Sync for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> Sync for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> Sync for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl Sync for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Sync for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Sync for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Sync for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Sync for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Sync for Dir",1,["iroh_quinn_proto::Dir"]],["impl Sync for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Sync for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Sync for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Sync for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Sync for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Sync for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Sync for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Sync for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Sync for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Sync for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Sync for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Sync for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Sync for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Sync for Side",1,["iroh_quinn_proto::Side"]],["impl Sync for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Sync for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Sync for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Sync for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Sync for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Sync for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Sync for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Sync for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Sync for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Sync for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Sync for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Sync for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Sync for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Sync for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Sync for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Sync for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Sync for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Sync for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Sync for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Sync for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Sync for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Sync for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Sync for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Sync for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Sync for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Sync for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Sync for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Sync for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Sync for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Sync for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Sync for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Sync for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Sync for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Sync for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Sync for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Sync for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Sync for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Sync for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Sync for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Sync for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Sync for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Sync for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Sync for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Sync for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Sync for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Sync for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Sync for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Sync for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Sync for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Sync for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Sync for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Sync for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Sync for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Sync for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Sync for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Sync for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Sync for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Sync for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Sync for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Sync for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Sync for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Sync for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Sync for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Sync for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Sync for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Sync for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Sync for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Sync for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Sync for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Sync for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Sync for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Sync for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Sync for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Sync for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Sync for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Sync for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Sync for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Sync for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Sync for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Sync for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Sync for KeyPair<T>
where\n T: Sync,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Sync for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Sync for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Sync for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Sync for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Sync for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Sync for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Sync for Opt",1,["perf::client::Opt"]],["impl Sync for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Sync for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Sync for Opt",1,["perf::server::Opt"]],["impl Sync for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Sync for Stats",1,["perf::stats::Stats"]],["impl Sync for StreamStats",1,["perf::stats::StreamStats"]],["impl Sync for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Sync for Commands",1,["quinn_perf::Commands"]],["impl Sync for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1436,291,363,9354,34441,1598,2769,563]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.Unpin.js b/pr/234/docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 000000000..5a573b62c --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl Unpin for CipherSuite",1,["bench::CipherSuite"]],["impl Unpin for Stats",1,["bench::stats::Stats"]],["impl Unpin for StreamStats",1,["bench::stats::StreamStats"]],["impl Unpin for TransferResult",1,["bench::stats::TransferResult"]],["impl Unpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl Unpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl Unpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl Unpin for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl Unpin for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl Unpin for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl Unpin for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl Unpin for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl Unpin for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl Unpin for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl Unpin for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl Unpin for Connection",1,["iroh_quinn::connection::Connection"]],["impl Unpin for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl Unpin for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl Unpin for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl Unpin for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl Unpin for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl Unpin for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl Unpin for Path",1,["iroh_quinn::path::Path"]],["impl Unpin for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl Unpin for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl Unpin for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl Unpin for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl Unpin for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl Unpin for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl Unpin for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'__pin, 'a> Unpin for Accept<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for AcceptBi<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for AcceptUni<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for OpenBi<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for OpenUni<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for ReadDatagram<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"],["impl<'__pin, 'a> Unpin for SendDatagram<'a>
where\n PinnedFieldsOf<__Origin<'__pin, 'a>>: Unpin,
"]]],["iroh_quinn_proto",[["impl Unpin for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl Unpin for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl Unpin for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl Unpin for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl Unpin for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl Unpin for Dir",1,["iroh_quinn_proto::Dir"]],["impl Unpin for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl Unpin for Event",1,["iroh_quinn_proto::connection::Event"]],["impl Unpin for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl Unpin for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl Unpin for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl Unpin for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl Unpin for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl Unpin for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl Unpin for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl Unpin for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl Unpin for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl Unpin for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl Unpin for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl Unpin for Side",1,["iroh_quinn_proto::Side"]],["impl Unpin for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl Unpin for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl Unpin for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl Unpin for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl Unpin for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl Unpin for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl Unpin for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl Unpin for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl Unpin for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl Unpin for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl Unpin for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl Unpin for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl Unpin for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl Unpin for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl Unpin for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl Unpin for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl Unpin for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl Unpin for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl Unpin for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl Unpin for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl Unpin for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl Unpin for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl Unpin for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl Unpin for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl Unpin for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl Unpin for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl Unpin for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl Unpin for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl Unpin for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl Unpin for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl Unpin for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl Unpin for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl Unpin for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl Unpin for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl Unpin for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl Unpin for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl Unpin for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl Unpin for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl Unpin for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl Unpin for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl Unpin for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl Unpin for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl Unpin for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl Unpin for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl Unpin for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl Unpin for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl Unpin for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl Unpin for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl Unpin for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl Unpin for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl Unpin for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl Unpin for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl Unpin for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl Unpin for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl Unpin for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl Unpin for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl Unpin for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl Unpin for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl Unpin for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl Unpin for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl Unpin for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl Unpin for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl Unpin for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl Unpin for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl Unpin for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl Unpin for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl Unpin for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl Unpin for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl Unpin for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl Unpin for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl Unpin for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl Unpin for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl Unpin for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl Unpin for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl Unpin for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> Unpin for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> Unpin for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> Unpin for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> Unpin for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> Unpin for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> Unpin for KeyPair<T>
where\n T: Unpin,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl Unpin for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl Unpin for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl Unpin for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> Unpin for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> Unpin for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl Unpin for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl Unpin for Opt",1,["perf::client::Opt"]],["impl Unpin for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl Unpin for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl Unpin for Opt",1,["perf::server::Opt"]],["impl Unpin for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl Unpin for Stats",1,["perf::stats::Stats"]],["impl Unpin for StreamStats",1,["perf::stats::StreamStats"]],["impl Unpin for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl Unpin for Commands",1,["quinn_perf::Commands"]],["impl Unpin for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1451,294,366,10800,34747,1613,2796,569]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/marker/trait.UnsafeUnpin.js b/pr/234/docs/trait.impl/core/marker/trait.UnsafeUnpin.js new file mode 100644 index 000000000..350a106c9 --- /dev/null +++ b/pr/234/docs/trait.impl/core/marker/trait.UnsafeUnpin.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnsafeUnpin for CipherSuite",1,["bench::CipherSuite"]],["impl UnsafeUnpin for Stats",1,["bench::stats::Stats"]],["impl UnsafeUnpin for StreamStats",1,["bench::stats::StreamStats"]],["impl UnsafeUnpin for TransferResult",1,["bench::stats::TransferResult"]],["impl UnsafeUnpin for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnsafeUnpin for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl UnsafeUnpin for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl UnsafeUnpin for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl UnsafeUnpin for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl UnsafeUnpin for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl UnsafeUnpin for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl UnsafeUnpin for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl UnsafeUnpin for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl UnsafeUnpin for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl UnsafeUnpin for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl UnsafeUnpin for Connection",1,["iroh_quinn::connection::Connection"]],["impl UnsafeUnpin for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl UnsafeUnpin for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl UnsafeUnpin for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl UnsafeUnpin for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl UnsafeUnpin for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl UnsafeUnpin for Path",1,["iroh_quinn::path::Path"]],["impl UnsafeUnpin for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl UnsafeUnpin for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl UnsafeUnpin for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl UnsafeUnpin for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl UnsafeUnpin for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl UnsafeUnpin for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl UnsafeUnpin for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl<'a> !UnsafeUnpin for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !UnsafeUnpin for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !UnsafeUnpin for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !UnsafeUnpin for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !UnsafeUnpin for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !UnsafeUnpin for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !UnsafeUnpin for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl UnsafeUnpin for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl UnsafeUnpin for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl UnsafeUnpin for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl UnsafeUnpin for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl UnsafeUnpin for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl UnsafeUnpin for Dir",1,["iroh_quinn_proto::Dir"]],["impl UnsafeUnpin for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl UnsafeUnpin for Event",1,["iroh_quinn_proto::connection::Event"]],["impl UnsafeUnpin for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl UnsafeUnpin for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl UnsafeUnpin for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl UnsafeUnpin for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl UnsafeUnpin for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl UnsafeUnpin for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl UnsafeUnpin for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl UnsafeUnpin for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl UnsafeUnpin for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl UnsafeUnpin for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl UnsafeUnpin for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl UnsafeUnpin for Side",1,["iroh_quinn_proto::Side"]],["impl UnsafeUnpin for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl UnsafeUnpin for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl UnsafeUnpin for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl UnsafeUnpin for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl UnsafeUnpin for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl UnsafeUnpin for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl UnsafeUnpin for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl UnsafeUnpin for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl UnsafeUnpin for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl UnsafeUnpin for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl UnsafeUnpin for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl UnsafeUnpin for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl UnsafeUnpin for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnsafeUnpin for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl UnsafeUnpin for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl UnsafeUnpin for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl UnsafeUnpin for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl UnsafeUnpin for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl UnsafeUnpin for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl UnsafeUnpin for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl UnsafeUnpin for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl UnsafeUnpin for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl UnsafeUnpin for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl UnsafeUnpin for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl UnsafeUnpin for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl UnsafeUnpin for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl UnsafeUnpin for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl UnsafeUnpin for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl UnsafeUnpin for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl UnsafeUnpin for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl UnsafeUnpin for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl UnsafeUnpin for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl UnsafeUnpin for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl UnsafeUnpin for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl UnsafeUnpin for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl UnsafeUnpin for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl UnsafeUnpin for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl UnsafeUnpin for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl UnsafeUnpin for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl UnsafeUnpin for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl UnsafeUnpin for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl UnsafeUnpin for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnsafeUnpin for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl UnsafeUnpin for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl UnsafeUnpin for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl UnsafeUnpin for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl UnsafeUnpin for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl UnsafeUnpin for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl UnsafeUnpin for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl UnsafeUnpin for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl UnsafeUnpin for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl UnsafeUnpin for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl UnsafeUnpin for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl UnsafeUnpin for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl UnsafeUnpin for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnsafeUnpin for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl UnsafeUnpin for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl UnsafeUnpin for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl UnsafeUnpin for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl UnsafeUnpin for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl UnsafeUnpin for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl UnsafeUnpin for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl UnsafeUnpin for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl UnsafeUnpin for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl UnsafeUnpin for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl UnsafeUnpin for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl UnsafeUnpin for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl UnsafeUnpin for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl UnsafeUnpin for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl UnsafeUnpin for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl UnsafeUnpin for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl UnsafeUnpin for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl UnsafeUnpin for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> UnsafeUnpin for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> UnsafeUnpin for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> UnsafeUnpin for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> UnsafeUnpin for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> UnsafeUnpin for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> UnsafeUnpin for KeyPair<T>
where\n T: UnsafeUnpin,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl UnsafeUnpin for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl UnsafeUnpin for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl UnsafeUnpin for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> UnsafeUnpin for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> UnsafeUnpin for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl UnsafeUnpin for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl UnsafeUnpin for Opt",1,["perf::client::Opt"]],["impl UnsafeUnpin for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl UnsafeUnpin for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl UnsafeUnpin for Opt",1,["perf::server::Opt"]],["impl UnsafeUnpin for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl UnsafeUnpin for Stats",1,["perf::stats::Stats"]],["impl UnsafeUnpin for StreamStats",1,["perf::stats::StreamStats"]],["impl UnsafeUnpin for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl UnsafeUnpin for Commands",1,["quinn_perf::Commands"]],["impl UnsafeUnpin for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[826,312,384,5701,21997,988,1671,605]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/bit/trait.Not.js b/pr/234/docs/trait.impl/core/ops/bit/trait.Not.js new file mode 100644 index 000000000..5b2b391db --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/bit/trait.Not.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Not for Side"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[275]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/deref/trait.Deref.js b/pr/234/docs/trait.impl/core/ops/deref/trait.Deref.js new file mode 100644 index 000000000..7b1e3385e --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/deref/trait.Deref.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Deref for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[315]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/deref/trait.DerefMut.js b/pr/234/docs/trait.impl/core/ops/deref/trait.DerefMut.js new file mode 100644 index 000000000..0fbf8f6b5 --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/deref/trait.DerefMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl DerefMut for ConnectionId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[324]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/drop/trait.Drop.js b/pr/234/docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 000000000..4f190db7b --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl Drop for Incoming"],["impl Drop for OnClosed"],["impl Drop for RecvStream"],["impl Drop for SendStream"]]],["iroh_quinn_proto",[["impl Drop for Chunks<'_>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1084,303]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/index/trait.Index.js b/pr/234/docs/trait.impl/core/ops/index/trait.Index.js new file mode 100644 index 000000000..dabc7ba10 --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/index/trait.Index.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl Index<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[361]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/ops/index/trait.IndexMut.js b/pr/234/docs/trait.impl/core/ops/index/trait.IndexMut.js new file mode 100644 index 000000000..ebfe83045 --- /dev/null +++ b/pr/234/docs/trait.impl/core/ops/index/trait.IndexMut.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta>"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[370]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 000000000..14909e96e --- /dev/null +++ b/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl RefUnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl RefUnwindSafe for Stats",1,["bench::stats::Stats"]],["impl RefUnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl RefUnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl RefUnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl RefUnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !RefUnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !RefUnwindSafe for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !RefUnwindSafe for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !RefUnwindSafe for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !RefUnwindSafe for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !RefUnwindSafe for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !RefUnwindSafe for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !RefUnwindSafe for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !RefUnwindSafe for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl !RefUnwindSafe for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl !RefUnwindSafe for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !RefUnwindSafe for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl !RefUnwindSafe for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl !RefUnwindSafe for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl !RefUnwindSafe for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl !RefUnwindSafe for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl RefUnwindSafe for Connection",1,["iroh_quinn::connection::Connection"]],["impl RefUnwindSafe for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl RefUnwindSafe for Path",1,["iroh_quinn::path::Path"]],["impl RefUnwindSafe for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl RefUnwindSafe for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl RefUnwindSafe for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl RefUnwindSafe for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl RefUnwindSafe for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl<'a> !RefUnwindSafe for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !RefUnwindSafe for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !RefUnwindSafe for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !RefUnwindSafe for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !RefUnwindSafe for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !RefUnwindSafe for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !RefUnwindSafe for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !RefUnwindSafe for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !RefUnwindSafe for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !RefUnwindSafe for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !RefUnwindSafe for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl !RefUnwindSafe for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl !RefUnwindSafe for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl !RefUnwindSafe for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl !RefUnwindSafe for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !RefUnwindSafe for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl !RefUnwindSafe for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !RefUnwindSafe for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl !RefUnwindSafe for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl !RefUnwindSafe for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !RefUnwindSafe for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl !RefUnwindSafe for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl !RefUnwindSafe for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl !RefUnwindSafe for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl !RefUnwindSafe for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl !RefUnwindSafe for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl RefUnwindSafe for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl RefUnwindSafe for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl RefUnwindSafe for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl RefUnwindSafe for Dir",1,["iroh_quinn_proto::Dir"]],["impl RefUnwindSafe for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl RefUnwindSafe for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl RefUnwindSafe for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl RefUnwindSafe for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl RefUnwindSafe for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl RefUnwindSafe for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl RefUnwindSafe for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl RefUnwindSafe for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl RefUnwindSafe for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl RefUnwindSafe for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl RefUnwindSafe for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl RefUnwindSafe for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl RefUnwindSafe for Side",1,["iroh_quinn_proto::Side"]],["impl RefUnwindSafe for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl RefUnwindSafe for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl RefUnwindSafe for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl RefUnwindSafe for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl RefUnwindSafe for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl RefUnwindSafe for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl RefUnwindSafe for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl RefUnwindSafe for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl RefUnwindSafe for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl RefUnwindSafe for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl RefUnwindSafe for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl RefUnwindSafe for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl RefUnwindSafe for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl RefUnwindSafe for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl RefUnwindSafe for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl RefUnwindSafe for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl RefUnwindSafe for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl RefUnwindSafe for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl RefUnwindSafe for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl RefUnwindSafe for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl RefUnwindSafe for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl RefUnwindSafe for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl RefUnwindSafe for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl RefUnwindSafe for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl RefUnwindSafe for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl RefUnwindSafe for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl RefUnwindSafe for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl RefUnwindSafe for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl RefUnwindSafe for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl RefUnwindSafe for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl RefUnwindSafe for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl RefUnwindSafe for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl RefUnwindSafe for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl RefUnwindSafe for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl RefUnwindSafe for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl RefUnwindSafe for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl RefUnwindSafe for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl RefUnwindSafe for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl RefUnwindSafe for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl RefUnwindSafe for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl RefUnwindSafe for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl RefUnwindSafe for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl RefUnwindSafe for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl RefUnwindSafe for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl RefUnwindSafe for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl RefUnwindSafe for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl RefUnwindSafe for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl RefUnwindSafe for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl RefUnwindSafe for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl RefUnwindSafe for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl RefUnwindSafe for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl RefUnwindSafe for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl RefUnwindSafe for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl RefUnwindSafe for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl RefUnwindSafe for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl RefUnwindSafe for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl RefUnwindSafe for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl RefUnwindSafe for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl RefUnwindSafe for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> !RefUnwindSafe for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> !RefUnwindSafe for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> !RefUnwindSafe for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<'a> RefUnwindSafe for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> RefUnwindSafe for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<T> RefUnwindSafe for KeyPair<T>
where\n T: RefUnwindSafe,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl RefUnwindSafe for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl RefUnwindSafe for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl RefUnwindSafe for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> RefUnwindSafe for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> RefUnwindSafe for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !RefUnwindSafe for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl !RefUnwindSafe for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl RefUnwindSafe for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl RefUnwindSafe for Opt",1,["perf::client::Opt"]],["impl RefUnwindSafe for Opt",1,["perf::server::Opt"]],["impl RefUnwindSafe for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl RefUnwindSafe for Stats",1,["perf::stats::Stats"]],["impl RefUnwindSafe for StreamStats",1,["perf::stats::StreamStats"]],["impl RefUnwindSafe for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl RefUnwindSafe for Commands",1,["quinn_perf::Commands"]],["impl RefUnwindSafe for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1686,341,414,10876,39563,1848,3221,663]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 000000000..a18232896 --- /dev/null +++ b/pr/234/docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["bench",[["impl UnwindSafe for CipherSuite",1,["bench::CipherSuite"]],["impl UnwindSafe for Stats",1,["bench::stats::Stats"]],["impl UnwindSafe for StreamStats",1,["bench::stats::StreamStats"]],["impl UnwindSafe for TransferResult",1,["bench::stats::TransferResult"]],["impl UnwindSafe for Opt",1,["bench::Opt"]]]],["bulk",[["impl UnwindSafe for ClientStats",1,["bulk::ClientStats"]]]],["certificate",[["impl !UnwindSafe for SkipServerVerification",1,["certificate::SkipServerVerification"]]]],["iroh_quinn",[["impl !UnwindSafe for ReadError",1,["iroh_quinn::recv_stream::ReadError"]],["impl !UnwindSafe for ReadExactError",1,["iroh_quinn::recv_stream::ReadExactError"]],["impl !UnwindSafe for ReadToEndError",1,["iroh_quinn::recv_stream::ReadToEndError"]],["impl !UnwindSafe for ResetError",1,["iroh_quinn::recv_stream::ResetError"]],["impl !UnwindSafe for SendDatagramError",1,["iroh_quinn::connection::SendDatagramError"]],["impl !UnwindSafe for StoppedError",1,["iroh_quinn::send_stream::StoppedError"]],["impl !UnwindSafe for WriteError",1,["iroh_quinn::send_stream::WriteError"]],["impl !UnwindSafe for Connecting",1,["iroh_quinn::connection::Connecting"]],["impl !UnwindSafe for Endpoint",1,["iroh_quinn::endpoint::Endpoint"]],["impl !UnwindSafe for Incoming",1,["iroh_quinn::incoming::Incoming"]],["impl !UnwindSafe for IncomingFuture",1,["iroh_quinn::incoming::IncomingFuture"]],["impl !UnwindSafe for OnClosed",1,["iroh_quinn::connection::OnClosed"]],["impl !UnwindSafe for OpenPath",1,["iroh_quinn::path::OpenPath"]],["impl !UnwindSafe for RetryError",1,["iroh_quinn::incoming::RetryError"]],["impl !UnwindSafe for ZeroRttAccepted",1,["iroh_quinn::connection::ZeroRttAccepted"]],["impl UnwindSafe for Connection",1,["iroh_quinn::connection::Connection"]],["impl UnwindSafe for EndpointStats",1,["iroh_quinn::endpoint::EndpointStats"]],["impl UnwindSafe for Path",1,["iroh_quinn::path::Path"]],["impl UnwindSafe for RecvStream",1,["iroh_quinn::recv_stream::RecvStream"]],["impl UnwindSafe for SendStream",1,["iroh_quinn::send_stream::SendStream"]],["impl UnwindSafe for SmolRuntime",1,["iroh_quinn::runtime::smol::SmolRuntime"]],["impl UnwindSafe for TokioRuntime",1,["iroh_quinn::runtime::tokio::TokioRuntime"]],["impl UnwindSafe for WeakConnectionHandle",1,["iroh_quinn::connection::WeakConnectionHandle"]],["impl<'a> !UnwindSafe for Accept<'a>",1,["iroh_quinn::endpoint::Accept"]],["impl<'a> !UnwindSafe for AcceptBi<'a>",1,["iroh_quinn::connection::AcceptBi"]],["impl<'a> !UnwindSafe for AcceptUni<'a>",1,["iroh_quinn::connection::AcceptUni"]],["impl<'a> !UnwindSafe for OpenBi<'a>",1,["iroh_quinn::connection::OpenBi"]],["impl<'a> !UnwindSafe for OpenUni<'a>",1,["iroh_quinn::connection::OpenUni"]],["impl<'a> !UnwindSafe for ReadDatagram<'a>",1,["iroh_quinn::connection::ReadDatagram"]],["impl<'a> !UnwindSafe for SendDatagram<'a>",1,["iroh_quinn::connection::SendDatagram"]]]],["iroh_quinn_proto",[["impl !UnwindSafe for ConnectionError",1,["iroh_quinn_proto::connection::ConnectionError"]],["impl !UnwindSafe for DatagramEvent",1,["iroh_quinn_proto::endpoint::DatagramEvent"]],["impl !UnwindSafe for Event",1,["iroh_quinn_proto::connection::Event"]],["impl !UnwindSafe for QuicClientConfig",1,["iroh_quinn_proto::crypto::rustls::QuicClientConfig"]],["impl !UnwindSafe for QuicServerConfig",1,["iroh_quinn_proto::crypto::rustls::QuicServerConfig"]],["impl !UnwindSafe for TlsSession",1,["iroh_quinn_proto::crypto::rustls::TlsSession"]],["impl !UnwindSafe for Keys",1,["iroh_quinn_proto::crypto::Keys"]],["impl !UnwindSafe for AcceptError",1,["iroh_quinn_proto::endpoint::AcceptError"]],["impl !UnwindSafe for ClientConfig",1,["iroh_quinn_proto::config::ClientConfig"]],["impl !UnwindSafe for Connection",1,["iroh_quinn_proto::connection::Connection"]],["impl !UnwindSafe for Endpoint",1,["iroh_quinn_proto::endpoint::Endpoint"]],["impl !UnwindSafe for EndpointConfig",1,["iroh_quinn_proto::config::EndpointConfig"]],["impl !UnwindSafe for Incoming",1,["iroh_quinn_proto::endpoint::Incoming"]],["impl !UnwindSafe for QlogConfig",1,["iroh_quinn_proto::config::qlog::QlogConfig"]],["impl !UnwindSafe for RetryError",1,["iroh_quinn_proto::endpoint::RetryError"]],["impl !UnwindSafe for ServerConfig",1,["iroh_quinn_proto::config::ServerConfig"]],["impl !UnwindSafe for TransportConfig",1,["iroh_quinn_proto::config::transport::TransportConfig"]],["impl !UnwindSafe for Error",1,["iroh_quinn_proto::transport_error::Error"]],["impl !UnwindSafe for ValidationTokenConfig",1,["iroh_quinn_proto::config::ValidationTokenConfig"]],["impl UnwindSafe for ClosePathError",1,["iroh_quinn_proto::connection::ClosePathError"]],["impl UnwindSafe for ConfigError",1,["iroh_quinn_proto::config::ConfigError"]],["impl UnwindSafe for ConnectError",1,["iroh_quinn_proto::endpoint::ConnectError"]],["impl UnwindSafe for Dir",1,["iroh_quinn_proto::Dir"]],["impl UnwindSafe for EcnCodepoint",1,["iroh_quinn_proto::shared::EcnCodepoint"]],["impl UnwindSafe for FinishError",1,["iroh_quinn_proto::connection::streams::send::FinishError"]],["impl UnwindSafe for LongType",1,["iroh_quinn_proto::packet::LongType"]],["impl UnwindSafe for PacketDecodeError",1,["iroh_quinn_proto::packet::PacketDecodeError"]],["impl UnwindSafe for PathError",1,["iroh_quinn_proto::connection::PathError"]],["impl UnwindSafe for PathEvent",1,["iroh_quinn_proto::connection::paths::PathEvent"]],["impl UnwindSafe for PathStatus",1,["iroh_quinn_proto::connection::paths::PathStatus"]],["impl UnwindSafe for ProtectedHeader",1,["iroh_quinn_proto::packet::ProtectedHeader"]],["impl UnwindSafe for ReadError",1,["iroh_quinn_proto::connection::streams::recv::ReadError"]],["impl UnwindSafe for ReadableError",1,["iroh_quinn_proto::connection::streams::recv::ReadableError"]],["impl UnwindSafe for SendDatagramError",1,["iroh_quinn_proto::connection::datagrams::SendDatagramError"]],["impl UnwindSafe for SetPathStatusError",1,["iroh_quinn_proto::connection::paths::SetPathStatusError"]],["impl UnwindSafe for Side",1,["iroh_quinn_proto::Side"]],["impl UnwindSafe for StreamEvent",1,["iroh_quinn_proto::connection::streams::StreamEvent"]],["impl UnwindSafe for WriteError",1,["iroh_quinn_proto::connection::streams::send::WriteError"]],["impl UnwindSafe for Error",1,["iroh_quinn_proto::iroh_hp::Error"]],["impl UnwindSafe for Event",1,["iroh_quinn_proto::iroh_hp::Event"]],["impl UnwindSafe for Error",1,["iroh_quinn_proto::transport_parameters::Error"]],["impl UnwindSafe for UnexpectedEnd",1,["iroh_quinn_proto::coding::UnexpectedEnd"]],["impl UnwindSafe for Bbr",1,["iroh_quinn_proto::congestion::bbr::Bbr"]],["impl UnwindSafe for BbrConfig",1,["iroh_quinn_proto::congestion::bbr::BbrConfig"]],["impl UnwindSafe for ControllerMetrics",1,["iroh_quinn_proto::congestion::ControllerMetrics"]],["impl UnwindSafe for Cubic",1,["iroh_quinn_proto::congestion::cubic::Cubic"]],["impl UnwindSafe for CubicConfig",1,["iroh_quinn_proto::congestion::cubic::CubicConfig"]],["impl UnwindSafe for NewReno",1,["iroh_quinn_proto::congestion::new_reno::NewReno"]],["impl UnwindSafe for NewRenoConfig",1,["iroh_quinn_proto::congestion::new_reno::NewRenoConfig"]],["impl UnwindSafe for HandshakeData",1,["iroh_quinn_proto::crypto::rustls::HandshakeData"]],["impl UnwindSafe for NoInitialCipherSuite",1,["iroh_quinn_proto::crypto::rustls::NoInitialCipherSuite"]],["impl UnwindSafe for CryptoError",1,["iroh_quinn_proto::crypto::CryptoError"]],["impl UnwindSafe for ExportKeyingMaterialError",1,["iroh_quinn_proto::crypto::ExportKeyingMaterialError"]],["impl UnwindSafe for UnsupportedVersion",1,["iroh_quinn_proto::crypto::UnsupportedVersion"]],["impl UnwindSafe for AckFrequencyConfig",1,["iroh_quinn_proto::config::transport::AckFrequencyConfig"]],["impl UnwindSafe for ApplicationClose",1,["iroh_quinn_proto::frame::ApplicationClose"]],["impl UnwindSafe for BloomTokenLog",1,["iroh_quinn_proto::bloom_token_log::BloomTokenLog"]],["impl UnwindSafe for Chunk",1,["iroh_quinn_proto::connection::assembler::Chunk"]],["impl UnwindSafe for ClosedPath",1,["iroh_quinn_proto::connection::paths::ClosedPath"]],["impl UnwindSafe for ClosedStream",1,["iroh_quinn_proto::connection::streams::ClosedStream"]],["impl UnwindSafe for ConnectionClose",1,["iroh_quinn_proto::frame::ConnectionClose"]],["impl UnwindSafe for ConnectionEvent",1,["iroh_quinn_proto::shared::ConnectionEvent"]],["impl UnwindSafe for ConnectionHandle",1,["iroh_quinn_proto::endpoint::ConnectionHandle"]],["impl UnwindSafe for ConnectionId",1,["iroh_quinn_proto::shared::ConnectionId"]],["impl UnwindSafe for ConnectionStats",1,["iroh_quinn_proto::connection::stats::ConnectionStats"]],["impl UnwindSafe for Datagram",1,["iroh_quinn_proto::frame::Datagram"]],["impl UnwindSafe for EndpointEvent",1,["iroh_quinn_proto::shared::EndpointEvent"]],["impl UnwindSafe for FixedLengthConnectionIdParser",1,["iroh_quinn_proto::packet::FixedLengthConnectionIdParser"]],["impl UnwindSafe for FrameStats",1,["iroh_quinn_proto::connection::stats::FrameStats"]],["impl UnwindSafe for FrameType",1,["iroh_quinn_proto::frame::FrameType"]],["impl UnwindSafe for HashedConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::HashedConnectionIdGenerator"]],["impl UnwindSafe for IdleTimeout",1,["iroh_quinn_proto::config::transport::IdleTimeout"]],["impl UnwindSafe for InvalidCid",1,["iroh_quinn_proto::cid_generator::InvalidCid"]],["impl UnwindSafe for MtuDiscoveryConfig",1,["iroh_quinn_proto::config::transport::MtuDiscoveryConfig"]],["impl UnwindSafe for NoneTokenLog",1,["iroh_quinn_proto::token::NoneTokenLog"]],["impl UnwindSafe for NoneTokenStore",1,["iroh_quinn_proto::token::NoneTokenStore"]],["impl UnwindSafe for PartialDecode",1,["iroh_quinn_proto::packet::PartialDecode"]],["impl UnwindSafe for PathId",1,["iroh_quinn_proto::connection::paths::PathId"]],["impl UnwindSafe for PathStats",1,["iroh_quinn_proto::connection::stats::PathStats"]],["impl UnwindSafe for ProtectedInitialHeader",1,["iroh_quinn_proto::packet::ProtectedInitialHeader"]],["impl UnwindSafe for QlogFileFactory",1,["iroh_quinn_proto::config::qlog::QlogFileFactory"]],["impl UnwindSafe for RandomConnectionIdGenerator",1,["iroh_quinn_proto::cid_generator::RandomConnectionIdGenerator"]],["impl UnwindSafe for RttEstimator",1,["iroh_quinn_proto::connection::paths::RttEstimator"]],["impl UnwindSafe for ShouldTransmit",1,["iroh_quinn_proto::connection::streams::ShouldTransmit"]],["impl UnwindSafe for StdSystemTime",1,["iroh_quinn_proto::config::StdSystemTime"]],["impl UnwindSafe for StreamId",1,["iroh_quinn_proto::StreamId"]],["impl UnwindSafe for TokenMemoryCache",1,["iroh_quinn_proto::token_memory_cache::TokenMemoryCache"]],["impl UnwindSafe for TokenReuseError",1,["iroh_quinn_proto::token::TokenReuseError"]],["impl UnwindSafe for Transmit",1,["iroh_quinn_proto::Transmit"]],["impl UnwindSafe for Code",1,["iroh_quinn_proto::transport_error::Code"]],["impl UnwindSafe for UdpStats",1,["iroh_quinn_proto::connection::stats::UdpStats"]],["impl UnwindSafe for VarInt",1,["iroh_quinn_proto::varint::VarInt"]],["impl UnwindSafe for VarIntBoundsExceeded",1,["iroh_quinn_proto::varint::VarIntBoundsExceeded"]],["impl UnwindSafe for Written",1,["iroh_quinn_proto::connection::streams::send::Written"]],["impl UnwindSafe for TransportParameters",1,["iroh_quinn_proto::transport_parameters::TransportParameters"]],["impl<'a> !UnwindSafe for Chunks<'a>",1,["iroh_quinn_proto::connection::streams::recv::Chunks"]],["impl<'a> !UnwindSafe for Datagrams<'a>",1,["iroh_quinn_proto::connection::datagrams::Datagrams"]],["impl<'a> !UnwindSafe for RecvStream<'a>",1,["iroh_quinn_proto::connection::streams::RecvStream"]],["impl<'a> !UnwindSafe for SendStream<'a>",1,["iroh_quinn_proto::connection::streams::SendStream"]],["impl<'a> !UnwindSafe for Streams<'a>",1,["iroh_quinn_proto::connection::streams::Streams"]],["impl<T> UnwindSafe for KeyPair<T>
where\n T: UnwindSafe,
",1,["iroh_quinn_proto::crypto::KeyPair"]]]],["iroh_quinn_udp",[["impl UnwindSafe for EcnCodepoint",1,["iroh_quinn_udp::EcnCodepoint"]],["impl UnwindSafe for RecvMeta",1,["iroh_quinn_udp::RecvMeta"]],["impl UnwindSafe for UdpSocketState",1,["iroh_quinn_udp::imp::UdpSocketState"]],["impl<'a> UnwindSafe for Transmit<'a>",1,["iroh_quinn_udp::Transmit"]],["impl<'a> UnwindSafe for UdpSockRef<'a>",1,["iroh_quinn_udp::UdpSockRef"]]]],["perf",[["impl !UnwindSafe for NoProtectionClientConfig",1,["perf::noprotection::NoProtectionClientConfig"]],["impl !UnwindSafe for NoProtectionServerConfig",1,["perf::noprotection::NoProtectionServerConfig"]],["impl UnwindSafe for CongestionAlgorithm",1,["perf::CongestionAlgorithm"]],["impl UnwindSafe for Opt",1,["perf::client::Opt"]],["impl UnwindSafe for Opt",1,["perf::server::Opt"]],["impl UnwindSafe for OpenStreamStats",1,["perf::stats::OpenStreamStats"]],["impl UnwindSafe for Stats",1,["perf::stats::Stats"]],["impl UnwindSafe for StreamStats",1,["perf::stats::StreamStats"]],["impl UnwindSafe for CommonOpt",1,["perf::CommonOpt"]]]],["quinn_perf",[["impl UnwindSafe for Commands",1,["quinn_perf::Commands"]],["impl UnwindSafe for Cli",1,["quinn_perf::Cli"]]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[1641,332,405,10606,38647,1803,3140,645]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/core/str/traits/trait.FromStr.js b/pr/234/docs/trait.impl/core/str/traits/trait.FromStr.js new file mode 100644 index 000000000..8d57e8916 --- /dev/null +++ b/pr/234/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/234/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js b/pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[157]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js b/pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/234/docs/trait.impl/futures_io/if_std/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[158]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/identity_hash/trait.IdentityHashable.js b/pr/234/docs/trait.impl/identity_hash/trait.IdentityHashable.js new file mode 100644 index 000000000..dd10c652c --- /dev/null +++ b/pr/234/docs/trait.impl/identity_hash/trait.IdentityHashable.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[["impl IdentityHashable for PathId"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[170]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js b/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.AsyncTimer.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js b/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js new file mode 100644 index 000000000..4cabdc2d0 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn/runtime/trait.Runtime.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js b/pr/234/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/cid_generator/trait.ConnectionIdGenerator.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/coding/trait.Codec.js b/pr/234/docs/trait.impl/iroh_quinn_proto/coding/trait.Codec.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/coding/trait.Codec.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[23]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js b/pr/234/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/config/qlog/trait.QlogFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js b/pr/234/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/config/trait.TimeSource.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js b/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.Controller.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js b/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/congestion/trait.ControllerFactory.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.AeadKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js new file mode 100644 index 000000000..692621ae4 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ClientConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]],["perf",[["impl ClientConfig for NoProtectionClientConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24,347]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HandshakeTokenKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HeaderKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.HmacKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.PacketKey.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js new file mode 100644 index 000000000..45d00a6be --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.ServerConfig.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]],["perf",[["impl ServerConfig for NoProtectionServerConfig"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24,347]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/crypto/trait.Session.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js b/pr/234/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js new file mode 100644 index 000000000..ed3b1991d --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/packet/trait.ConnectionIdParser.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[23]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js b/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenLog.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js b/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js new file mode 100644 index 000000000..648d7f9c8 --- /dev/null +++ b/pr/234/docs/trait.impl/iroh_quinn_proto/token/trait.TokenStore.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[]],["iroh_quinn_proto",[]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[17,24]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js b/pr/234/docs/trait.impl/rustls/verify/trait.ServerCertVerifier.js new file mode 100644 index 000000000..45f6b7887 --- /dev/null +++ b/pr/234/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/234/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js b/pr/234/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js new file mode 100644 index 000000000..f5d531735 --- /dev/null +++ b/pr/234/docs/trait.impl/tokio/io/async_read/trait.AsyncRead.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncRead for RecvStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[157]} \ No newline at end of file diff --git a/pr/234/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js b/pr/234/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js new file mode 100644 index 000000000..53ae8c728 --- /dev/null +++ b/pr/234/docs/trait.impl/tokio/io/async_write/trait.AsyncWrite.js @@ -0,0 +1,9 @@ +(function() { + var implementors = Object.fromEntries([["iroh_quinn",[["impl AsyncWrite for SendStream"]]]]); + if (window.register_implementors) { + window.register_implementors(implementors); + } else { + window.pending_implementors = implementors; + } +})() +//{"start":57,"fragment_lengths":[158]} \ No newline at end of file diff --git a/pr/234/docs/type.impl/core/result/enum.Result.js b/pr/234/docs/type.impl/core/result/enum.Result.js new file mode 100644 index 000000000..3b8adb67a --- /dev/null +++ b/pr/234/docs/type.impl/core/result/enum.Result.js @@ -0,0 +1,9 @@ +(function() { + var type_impls = Object.fromEntries([["iroh_quinn_proto",[["
§

impl<'a, T, E> Arbitrary<'a> for Result<T, E>
where\n T: Arbitrary<'a>,\n E: Arbitrary<'a>,

§

fn arbitrary(u: &mut Unstructured<'a>) -> Result<Result<T, E>, Error>

Generate an arbitrary value of Self from the given unstructured data. Read more
§

fn size_hint(depth: usize) -> (usize, Option<usize>)

Get a size hint for how many bytes out of an Unstructured this type\nneeds to construct itself. Read more
§

fn try_size_hint(\n depth: usize,\n) -> Result<(usize, Option<usize>), MaxRecursionReached>

Get a size hint for how many bytes out of an Unstructured this type\nneeds to construct itself. Read more
§

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self, Error>

Generate an arbitrary value of Self from the entirety of the given\nunstructured data. Read more
","Arbitrary<'a>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Clone for Result<T, E>
where\n T: Clone,\n E: Clone,

Source§

fn clone(&self) -> Result<T, E>

Returns a duplicate of the value. Read more
Source§

fn clone_from(&mut self, source: &Result<T, E>)

Performs copy-assignment from source. Read more
","Clone","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Debug for Result<T, E>
where\n T: Debug,\n E: Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
","Debug","iroh_quinn_proto::coding::Result"],["
Source§

impl<'de, T, E> Deserialize<'de> for Result<T, E>
where\n T: Deserialize<'de>,\n E: Deserialize<'de>,

Source§

fn deserialize<D>(\n deserializer: D,\n) -> Result<Result<T, E>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
","Deserialize<'de>","iroh_quinn_proto::coding::Result"],["
Source§

impl<'de, T, TAs, E, EAs> DeserializeAs<'de, Result<T, E>> for Result<TAs, EAs>
where\n TAs: DeserializeAs<'de, T>,\n EAs: DeserializeAs<'de, E>,

Source§

fn deserialize_as<D>(\n deserializer: D,\n) -> Result<Result<T, E>, <D as Deserializer<'de>>::Error>
where\n D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer.
","DeserializeAs<'de, Result>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E>
where\n V: FromIterator<A>,

Source§

fn from_iter<I>(iter: I) -> Result<V, E>
where\n I: IntoIterator<Item = Result<A, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err occur, a\ncontainer with the values of each Result is returned.

\n

Here is an example which increments every integer in a vector,\nchecking for overflow:

\n\n
let v = vec![1, 2];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n    x.checked_add(1).ok_or(\"Overflow!\")\n).collect();\nassert_eq!(res, Ok(vec![2, 3]));
\n

Here is another example that tries to subtract one from another list\nof integers, this time checking for underflow:

\n\n
let v = vec![1, 2, 0];\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32|\n    x.checked_sub(1).ok_or(\"Underflow!\")\n).collect();\nassert_eq!(res, Err(\"Underflow!\"));
\n

Here is a variation on the previous example, showing that no\nfurther elements are taken from iter after the first Err.

\n\n
let v = vec![3, 2, 1, 10];\nlet mut shared = 0;\nlet res: Result<Vec<u32>, &'static str> = v.iter().map(|x: &u32| {\n    shared += x;\n    x.checked_sub(2).ok_or(\"Underflow!\")\n}).collect();\nassert_eq!(res, Err(\"Underflow!\"));\nassert_eq!(shared, 6);
\n

Since the third element caused an underflow, no further elements were taken,\nso the final value of shared is 6 (= 3 + 2 + 1), not 16.

\n
","FromIterator>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E, F> FromResidual<Result<Infallible, E>> for Result<T, F>
where\n F: From<E>,

Source§

fn from_residual(residual: Result<Infallible, E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E, F> FromResidual<Yeet<E>> for Result<T, F>
where\n F: From<E>,

Source§

fn from_residual(_: Yeet<E>) -> Result<T, F>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from a compatible Residual type. Read more
","FromResidual>","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Hash for Result<T, E>
where\n T: Hash,\n E: Hash,

Source§

fn hash<__H>(&self, state: &mut __H)
where\n __H: Hasher,

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where\n H: Hasher,\n Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
","Hash","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> IntoIterator for Result<T, E>

Source§

fn into_iter(self) -> IntoIter<T>

Returns a consuming iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(5);\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, [5]);\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nlet v: Vec<u32> = x.into_iter().collect();\nassert_eq!(v, []);
Source§

type Item = T

The type of the elements being iterated over.
Source§

type IntoIter = IntoIter<T>

Which kind of iterator are we turning this into?
","IntoIterator","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> Ord for Result<T, E>
where\n T: Ord,\n E: Ord,

Source§

fn cmp(&self, other: &Result<T, E>) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where\n Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where\n Self: Sized,

Restrict a value to a certain interval. Read more
","Ord","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> PartialEq for Result<T, E>
where\n T: PartialEq,\n E: PartialEq,

Source§

fn eq(&self, other: &Result<T, E>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient,\nand should not be overridden without very good reason.
","PartialEq","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> PartialOrd for Result<T, E>
where\n T: PartialOrd,\n E: PartialOrd,

Source§

fn partial_cmp(&self, other: &Result<T, E>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the\n<= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the >\noperator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by\nthe >= operator. Read more
","PartialOrd","iroh_quinn_proto::coding::Result"],["
1.16.0 · Source§

impl<T, U, E> Product<Result<U, E>> for Result<T, E>
where\n T: Product<U>,

Source§

fn product<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err\noccur, the product of all elements is returned.

\n
§Examples
\n

This multiplies each number in a vector of strings,\nif a string could not be parsed the operation returns Err:

\n\n
let nums = vec![\"5\", \"10\", \"1\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert_eq!(total, Ok(100));\nlet nums = vec![\"5\", \"10\", \"one\", \"2\"];\nlet total: Result<usize, _> = nums.iter().map(|w| w.parse::<usize>()).product();\nassert!(total.is_err());
","Product>","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Residual<T> for Result<Infallible, E>

Source§

type TryType = Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2_residual)
The “return” type of this meta-function.
","Residual","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<&T, E>

1.59.0 (const: 1.83.0) · Source

pub const fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
1.59.0 · Source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
let val = 12;\nlet x: Result<&i32, i32> = Ok(&val);\nassert_eq!(x, Ok(&12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<&mut T, E>

1.59.0 (const: 1.83.0) · Source

pub const fn copied(self) -> Result<T, E>
where\n T: Copy,

Maps a Result<&mut T, E> to a Result<T, E> by copying the contents of the\nOk part.

\n
§Examples
\n
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet copied = x.copied();\nassert_eq!(copied, Ok(12));
1.59.0 · Source

pub fn cloned(self) -> Result<T, E>
where\n T: Clone,

Maps a Result<&mut T, E> to a Result<T, E> by cloning the contents of the\nOk part.

\n
§Examples
\n
let mut val = 12;\nlet x: Result<&mut i32, i32> = Ok(&mut val);\nassert_eq!(x, Ok(&mut 12));\nlet cloned = x.cloned();\nassert_eq!(cloned, Ok(12));
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<Option<T>, E>

1.33.0 (const: 1.83.0) · Source

pub const fn transpose(self) -> Option<Result<T, E>>

Transposes a Result of an Option into an Option of a Result.

\n

Ok(None) will be mapped to None.\nOk(Some(_)) and Err(_) will be mapped to Some(Ok(_)) and Some(Err(_)).

\n
§Examples
\n
#[derive(Debug, Eq, PartialEq)]\nstruct SomeErr;\n\nlet x: Result<Option<i32>, SomeErr> = Ok(Some(5));\nlet y: Option<Result<i32, SomeErr>> = Some(Ok(5));\nassert_eq!(x.transpose(), y);
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<Result<T, E>, E>

1.89.0 (const: 1.89.0) · Source

pub const fn flatten(self) -> Result<T, E>

Converts from Result<Result<T, E>, E> to Result<T, E>

\n
§Examples
\n
let x: Result<Result<&'static str, u32>, u32> = Ok(Ok(\"hello\"));\nassert_eq!(Ok(\"hello\"), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Ok(Err(6));\nassert_eq!(Err(6), x.flatten());\n\nlet x: Result<Result<&'static str, u32>, u32> = Err(6);\nassert_eq!(Err(6), x.flatten());
\n

Flattening only removes one level of nesting at a time:

\n\n
let x: Result<Result<Result<&'static str, u32>, u32>, u32> = Ok(Ok(Ok(\"hello\")));\nassert_eq!(Ok(Ok(\"hello\")), x.flatten());\nassert_eq!(Ok(\"hello\"), x.flatten().flatten());
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Result<T, E>

1.0.0 (const: 1.48.0) · Source

pub const fn is_ok(&self) -> bool

Returns true if the result is Ok.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_ok(), true);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_ok(), false);
1.70.0 (const: unstable) · Source

pub fn is_ok_and<F>(self, f: F) -> bool
where\n F: FnOnce(T) -> bool,

Returns true if the result is Ok and the value inside of it matches a predicate.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.is_ok_and(|x| x > 1), true);\n\nlet x: Result<u32, &str> = Ok(0);\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<u32, &str> = Err(\"hey\");\nassert_eq!(x.is_ok_and(|x| x > 1), false);\n\nlet x: Result<String, &str> = Ok(\"ownership\".to_string());\nassert_eq!(x.as_ref().is_ok_and(|x| x.len() > 1), true);\nprintln!(\"still alive {:?}\", x);
1.0.0 (const: 1.48.0) · Source

pub const fn is_err(&self) -> bool

Returns true if the result is Err.

\n
§Examples
\n
let x: Result<i32, &str> = Ok(-3);\nassert_eq!(x.is_err(), false);\n\nlet x: Result<i32, &str> = Err(\"Some error message\");\nassert_eq!(x.is_err(), true);
1.70.0 (const: unstable) · Source

pub fn is_err_and<F>(self, f: F) -> bool
where\n F: FnOnce(E) -> bool,

Returns true if the result is Err and the value inside of it matches a predicate.

\n
§Examples
\n
use std::io::{Error, ErrorKind};\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::NotFound, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), true);\n\nlet x: Result<u32, Error> = Err(Error::new(ErrorKind::PermissionDenied, \"!\"));\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, Error> = Ok(123);\nassert_eq!(x.is_err_and(|x| x.kind() == ErrorKind::NotFound), false);\n\nlet x: Result<u32, String> = Err(\"ownership\".to_string());\nassert_eq!(x.as_ref().is_err_and(|x| x.len() > 1), true);\nprintln!(\"still alive {:?}\", x);
1.0.0 (const: unstable) · Source

pub fn ok(self) -> Option<T>

Converts from Result<T, E> to Option<T>.

\n

Converts self into an Option<T>, consuming self,\nand discarding the error, if any.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.ok(), Some(2));\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.ok(), None);
1.0.0 (const: unstable) · Source

pub fn err(self) -> Option<E>

Converts from Result<T, E> to Option<E>.

\n

Converts self into an Option<E>, consuming self,\nand discarding the success value, if any.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.err(), None);\n\nlet x: Result<u32, &str> = Err(\"Nothing here\");\nassert_eq!(x.err(), Some(\"Nothing here\"));
1.0.0 (const: 1.48.0) · Source

pub const fn as_ref(&self) -> Result<&T, &E>

Converts from &Result<T, E> to Result<&T, &E>.

\n

Produces a new Result, containing a reference\ninto the original, leaving the original in place.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.as_ref(), Ok(&2));\n\nlet x: Result<u32, &str> = Err(\"Error\");\nassert_eq!(x.as_ref(), Err(&\"Error\"));
1.0.0 (const: 1.83.0) · Source

pub const fn as_mut(&mut self) -> Result<&mut T, &mut E>

Converts from &mut Result<T, E> to Result<&mut T, &mut E>.

\n
§Examples
\n
fn mutate(r: &mut Result<i32, i32>) {\n    match r.as_mut() {\n        Ok(v) => *v = 42,\n        Err(e) => *e = 0,\n    }\n}\n\nlet mut x: Result<i32, i32> = Ok(2);\nmutate(&mut x);\nassert_eq!(x.unwrap(), 42);\n\nlet mut x: Result<i32, i32> = Err(13);\nmutate(&mut x);\nassert_eq!(x.unwrap_err(), 0);
1.0.0 (const: unstable) · Source

pub fn map<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> U,

Maps a Result<T, E> to Result<U, E> by applying a function to a\ncontained Ok value, leaving an Err value untouched.

\n

This function can be used to compose the results of two functions.

\n
§Examples
\n

Print the numbers on each line of a string multiplied by two.

\n\n
let line = \"1\\n2\\n3\\n4\\n\";\n\nfor num in line.lines() {\n    match num.parse::<i32>().map(|i| i * 2) {\n        Ok(n) => println!(\"{n}\"),\n        Err(..) => {}\n    }\n}
1.41.0 (const: unstable) · Source

pub fn map_or<U, F>(self, default: U, f: F) -> U
where\n F: FnOnce(T) -> U,

Returns the provided default (if Err), or\napplies a function to the contained value (if Ok).

\n

Arguments passed to map_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use map_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
let x: Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or(42, |v| v.len()), 3);\n\nlet x: Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or(42, |v| v.len()), 42);
1.41.0 (const: unstable) · Source

pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where\n D: FnOnce(E) -> U,\n F: FnOnce(T) -> U,

Maps a Result<T, E> to U by applying fallback function default to\na contained Err value, or function f to a contained Ok value.

\n

This function can be used to unpack a successful result\nwhile handling an error.

\n
§Examples
\n
let k = 21;\n\nlet x : Result<_, &str> = Ok(\"foo\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 3);\n\nlet x : Result<&str, _> = Err(\"bar\");\nassert_eq!(x.map_or_else(|e| k * 2, |v| v.len()), 42);
Source

pub const fn map_or_default<U, F>(self, f: F) -> U
where\n F: FnOnce(T) -> U,\n U: Default,

🔬This is a nightly-only experimental API. (result_option_map_or_default)

Maps a Result<T, E> to a U by applying function f to the contained\nvalue if the result is Ok, otherwise if Err, returns the\ndefault value for the type U.

\n
§Examples
\n
#![feature(result_option_map_or_default)]\n\nlet x: Result<_, &str> = Ok(\"foo\");\nlet y: Result<&str, _> = Err(\"bar\");\n\nassert_eq!(x.map_or_default(|x| x.len()), 3);\nassert_eq!(y.map_or_default(|y| y.len()), 0);
1.0.0 (const: unstable) · Source

pub fn map_err<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> F,

Maps a Result<T, E> to Result<T, F> by applying a function to a\ncontained Err value, leaving an Ok value untouched.

\n

This function can be used to pass through a successful result while handling\nan error.

\n
§Examples
\n
fn stringify(x: u32) -> String { format!(\"error code: {x}\") }\n\nlet x: Result<u32, u32> = Ok(2);\nassert_eq!(x.map_err(stringify), Ok(2));\n\nlet x: Result<u32, u32> = Err(13);\nassert_eq!(x.map_err(stringify), Err(\"error code: 13\".to_string()));
1.76.0 (const: unstable) · Source

pub fn inspect<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&T),

Calls a function with a reference to the contained value if Ok.

\n

Returns the original result.

\n
§Examples
\n
let x: u8 = \"4\"\n    .parse::<u8>()\n    .inspect(|x| println!(\"original: {x}\"))\n    .map(|x| x.pow(3))\n    .expect(\"failed to parse number\");
1.76.0 (const: unstable) · Source

pub fn inspect_err<F>(self, f: F) -> Result<T, E>
where\n F: FnOnce(&E),

Calls a function with a reference to the contained value if Err.

\n

Returns the original result.

\n
§Examples
\n
use std::{fs, io};\n\nfn read() -> io::Result<String> {\n    fs::read_to_string(\"address.txt\")\n        .inspect_err(|e| eprintln!(\"failed to read file: {e}\"))\n}
1.47.0 (const: unstable) · Source

pub fn as_deref(&self) -> Result<&<T as Deref>::Target, &E>
where\n T: Deref,

Converts from Result<T, E> (or &Result<T, E>) to Result<&<T as Deref>::Target, &E>.

\n

Coerces the Ok variant of the original Result via Deref\nand returns the new Result.

\n
§Examples
\n
let x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&str, &u32> = Ok(\"hello\");\nassert_eq!(x.as_deref(), y);\n\nlet x: Result<String, u32> = Err(42);\nlet y: Result<&str, &u32> = Err(&42);\nassert_eq!(x.as_deref(), y);
1.47.0 (const: unstable) · Source

pub fn as_deref_mut(&mut self) -> Result<&mut <T as Deref>::Target, &mut E>
where\n T: DerefMut,

Converts from Result<T, E> (or &mut Result<T, E>) to Result<&mut <T as DerefMut>::Target, &mut E>.

\n

Coerces the Ok variant of the original Result via DerefMut\nand returns the new Result.

\n
§Examples
\n
let mut s = \"HELLO\".to_string();\nlet mut x: Result<String, u32> = Ok(\"hello\".to_string());\nlet y: Result<&mut str, &mut u32> = Ok(&mut s);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);\n\nlet mut i = 42;\nlet mut x: Result<String, u32> = Err(42);\nlet y: Result<&mut str, &mut u32> = Err(&mut i);\nassert_eq!(x.as_deref_mut().map(|x| { x.make_ascii_uppercase(); x }), y);
1.0.0 (const: unstable) · Source

pub fn iter(&self) -> Iter<'_, T>

Returns an iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(7);\nassert_eq!(x.iter().next(), Some(&7));\n\nlet x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter().next(), None);
1.0.0 (const: unstable) · Source

pub fn iter_mut(&mut self) -> IterMut<'_, T>

Returns a mutable iterator over the possibly contained value.

\n

The iterator yields one value if the result is Result::Ok, otherwise none.

\n
§Examples
\n
let mut x: Result<u32, &str> = Ok(7);\nmatch x.iter_mut().next() {\n    Some(v) => *v = 40,\n    None => {},\n}\nassert_eq!(x, Ok(40));\n\nlet mut x: Result<u32, &str> = Err(\"nothing!\");\nassert_eq!(x.iter_mut().next(), None);
1.4.0 · Source

pub fn expect(self, msg: &str) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nInstead, prefer to use pattern matching and handle the Err\ncase explicitly, or call unwrap_or, unwrap_or_else, or\nunwrap_or_default.

\n
§Panics
\n

Panics if the value is an Err, with a panic message including the\npassed message, and the content of the Err.

\n
§Examples
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.expect(\"Testing expect\"); // panics with `Testing expect: emergency failure`
§Recommended Message Style
\n

We recommend that expect messages are used to describe the reason you\nexpect the Result should be Ok.

\n\n
let path = std::env::var(\"IMPORTANT_PATH\")\n    .expect(\"env variable `IMPORTANT_PATH` should be set by `wrapper_script.sh`\");
\n

Hint: If you’re having trouble remembering how to phrase expect\nerror messages remember to focus on the word “should” as in “env\nvariable should be set by blah” or “the given binary should be available\nand executable by the current user”.

\n

For more detail on expect message styles and the reasoning behind our recommendation please\nrefer to the section on “Common Message\nStyles” in the\nstd::error module docs.

\n
1.0.0 · Source

pub fn unwrap(self) -> T
where\n E: Debug,

Returns the contained Ok value, consuming the self value.

\n

Because this function may panic, its use is generally discouraged.\nPanics are meant for unrecoverable errors, and\nmay abort the entire program.

\n

Instead, prefer to use the ? (try) operator, or pattern matching\nto handle the Err case explicitly, or call unwrap_or,\nunwrap_or_else, or unwrap_or_default.

\n
§Panics
\n

Panics if the value is an Err, with a panic message provided by the\nErr’s value.

\n
§Examples
\n

Basic usage:

\n\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(x.unwrap(), 2);
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nx.unwrap(); // panics with `emergency failure`
1.16.0 (const: unstable) · Source

pub fn unwrap_or_default(self) -> T
where\n T: Default,

Returns the contained Ok value or a default

\n

Consumes the self argument then, if Ok, returns the contained\nvalue, otherwise if Err, returns the default value for that\ntype.

\n
§Examples
\n

Converts a string to an integer, turning poorly-formed strings\ninto 0 (the default value for integers). parse converts\na string to any other type that implements FromStr, returning an\nErr on error.

\n\n
let good_year_from_input = \"1909\";\nlet bad_year_from_input = \"190blarg\";\nlet good_year = good_year_from_input.parse().unwrap_or_default();\nlet bad_year = bad_year_from_input.parse().unwrap_or_default();\n\nassert_eq!(1909, good_year);\nassert_eq!(0, bad_year);
1.17.0 · Source

pub fn expect_err(self, msg: &str) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a panic message including the\npassed message, and the content of the Ok.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(10);\nx.expect_err(\"Testing expect_err\"); // panics with `Testing expect_err: 10`
1.0.0 · Source

pub fn unwrap_err(self) -> E
where\n T: Debug,

Returns the contained Err value, consuming the self value.

\n
§Panics
\n

Panics if the value is an Ok, with a custom panic message provided\nby the Ok’s value.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nx.unwrap_err(); // panics with `2`
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(x.unwrap_err(), \"emergency failure\");
Source

pub const fn into_ok(self) -> T
where\n E: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Ok value, but never panics.

\n

Unlike unwrap, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap as a maintainability safeguard that will fail\nto compile if the error type of the Result is later changed\nto an error that can actually occur.

\n
§Examples
\n
\nfn only_good_news() -> Result<String, !> {\n    Ok(\"this is fine\".into())\n}\n\nlet s: String = only_good_news().into_ok();\nprintln!(\"{s}\");
Source

pub const fn into_err(self) -> E
where\n T: Into<!>,

🔬This is a nightly-only experimental API. (unwrap_infallible)

Returns the contained Err value, but never panics.

\n

Unlike unwrap_err, this method is known to never panic on the\nresult types it is implemented for. Therefore, it can be used\ninstead of unwrap_err as a maintainability safeguard that will fail\nto compile if the ok type of the Result is later changed\nto a type that can actually occur.

\n
§Examples
\n
\nfn only_bad_news() -> Result<!, String> {\n    Err(\"Oops, it failed\".into())\n}\n\nlet error: String = only_bad_news().into_err();\nprintln!(\"{error}\");
1.0.0 (const: unstable) · Source

pub fn and<U>(self, res: Result<U, E>) -> Result<U, E>

Returns res if the result is Ok, otherwise returns the Err value of self.

\n

Arguments passed to and are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use and_then, which is\nlazily evaluated.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<&str, &str> = Ok(\"foo\");\nassert_eq!(x.and(y), Err(\"early error\"));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<&str, &str> = Err(\"late error\");\nassert_eq!(x.and(y), Err(\"not a 2\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<&str, &str> = Ok(\"different result type\");\nassert_eq!(x.and(y), Ok(\"different result type\"));
1.0.0 (const: unstable) · Source

pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where\n F: FnOnce(T) -> Result<U, E>,

Calls op if the result is Ok, otherwise returns the Err value of self.

\n

This function can be used for control flow based on Result values.

\n
§Examples
\n
fn sq_then_to_string(x: u32) -> Result<String, &'static str> {\n    x.checked_mul(x).map(|sq| sq.to_string()).ok_or(\"overflowed\")\n}\n\nassert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));\nassert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err(\"overflowed\"));\nassert_eq!(Err(\"not a number\").and_then(sq_then_to_string), Err(\"not a number\"));
\n

Often used to chain fallible operations that may return Err.

\n\n
use std::{io::ErrorKind, path::Path};\n\n// Note: on Windows \"/\" maps to \"C:\\\"\nlet root_modified_time = Path::new(\"/\").metadata().and_then(|md| md.modified());\nassert!(root_modified_time.is_ok());\n\nlet should_fail = Path::new(\"/bad/path\").metadata().and_then(|md| md.modified());\nassert!(should_fail.is_err());\nassert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
1.0.0 (const: unstable) · Source

pub fn or<F>(self, res: Result<T, F>) -> Result<T, F>

Returns res if the result is Err, otherwise returns the Ok value of self.

\n

Arguments passed to or are eagerly evaluated; if you are passing the\nresult of a function call, it is recommended to use or_else, which is\nlazily evaluated.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"early error\");\nlet y: Result<u32, &str> = Ok(2);\nassert_eq!(x.or(y), Ok(2));\n\nlet x: Result<u32, &str> = Err(\"not a 2\");\nlet y: Result<u32, &str> = Err(\"late error\");\nassert_eq!(x.or(y), Err(\"late error\"));\n\nlet x: Result<u32, &str> = Ok(2);\nlet y: Result<u32, &str> = Ok(100);\nassert_eq!(x.or(y), Ok(2));
1.0.0 (const: unstable) · Source

pub fn or_else<F, O>(self, op: O) -> Result<T, F>
where\n O: FnOnce(E) -> Result<T, F>,

Calls op if the result is Err, otherwise returns the Ok value of self.

\n

This function can be used for control flow based on result values.

\n
§Examples
\n
fn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }\nfn err(x: u32) -> Result<u32, u32> { Err(x) }\n\nassert_eq!(Ok(2).or_else(sq).or_else(sq), Ok(2));\nassert_eq!(Ok(2).or_else(err).or_else(sq), Ok(2));\nassert_eq!(Err(3).or_else(sq).or_else(err), Ok(9));\nassert_eq!(Err(3).or_else(err).or_else(err), Err(3));
1.0.0 (const: unstable) · Source

pub fn unwrap_or(self, default: T) -> T

Returns the contained Ok value or a provided default.

\n

Arguments passed to unwrap_or are eagerly evaluated; if you are passing\nthe result of a function call, it is recommended to use unwrap_or_else,\nwhich is lazily evaluated.

\n
§Examples
\n
let default = 2;\nlet x: Result<u32, &str> = Ok(9);\nassert_eq!(x.unwrap_or(default), 9);\n\nlet x: Result<u32, &str> = Err(\"error\");\nassert_eq!(x.unwrap_or(default), default);
1.0.0 (const: unstable) · Source

pub fn unwrap_or_else<F>(self, op: F) -> T
where\n F: FnOnce(E) -> T,

Returns the contained Ok value or computes it from a closure.

\n
§Examples
\n
fn count(x: &str) -> usize { x.len() }\n\nassert_eq!(Ok(2).unwrap_or_else(count), 2);\nassert_eq!(Err(\"foo\").unwrap_or_else(count), 3);
1.58.0 · Source

pub unsafe fn unwrap_unchecked(self) -> T

Returns the contained Ok value, consuming the self value,\nwithout checking that the value is not an Err.

\n
§Safety
\n

Calling this method on an Err is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nassert_eq!(unsafe { x.unwrap_unchecked() }, 2);
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nunsafe { x.unwrap_unchecked() }; // Undefined behavior!
1.58.0 · Source

pub unsafe fn unwrap_err_unchecked(self) -> E

Returns the contained Err value, consuming the self value,\nwithout checking that the value is not an Ok.

\n
§Safety
\n

Calling this method on an Ok is undefined behavior.

\n
§Examples
\n
let x: Result<u32, &str> = Ok(2);\nunsafe { x.unwrap_err_unchecked() }; // Undefined behavior!
\n
let x: Result<u32, &str> = Err(\"emergency failure\");\nassert_eq!(unsafe { x.unwrap_err_unchecked() }, \"emergency failure\");
",0,"iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Serialize for Result<T, E>
where\n T: Serialize,\n E: Serialize,

Source§

fn serialize<S>(\n &self,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer. Read more
","Serialize","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, TAs, E, EAs> SerializeAs<Result<T, E>> for Result<TAs, EAs>
where\n TAs: SerializeAs<T>,\n EAs: SerializeAs<E>,

Source§

fn serialize_as<S>(\n source: &Result<T, E>,\n serializer: S,\n) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where\n S: Serializer,

Serialize this value into the given Serde serializer.
","SerializeAs>","iroh_quinn_proto::coding::Result"],["
1.16.0 · Source§

impl<T, U, E> Sum<Result<U, E>> for Result<T, E>
where\n T: Sum<U>,

Source§

fn sum<I>(iter: I) -> Result<T, E>
where\n I: Iterator<Item = Result<U, E>>,

Takes each element in the Iterator: if it is an Err, no further\nelements are taken, and the Err is returned. Should no Err\noccur, the sum of all elements is returned.

\n
§Examples
\n

This sums up every integer in a vector, rejecting the sum if a negative\nelement is encountered:

\n\n
let f = |&x: &i32| if x < 0 { Err(\"Negative element found\") } else { Ok(x) };\nlet v = vec![1, 2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Ok(3));\nlet v = vec![1, -2];\nlet res: Result<i32, _> = v.iter().map(f).sum();\nassert_eq!(res, Err(\"Negative element found\"));
","Sum>","iroh_quinn_proto::coding::Result"],["
1.61.0 · Source§

impl<T, E> Termination for Result<T, E>
where\n T: Termination,\n E: Debug,

Source§

fn report(self) -> ExitCode

Is called to get the representation of the value as status code.\nThis status code is returned to the operating system.
","Termination","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> Try for Result<T, E>

Source§

type Output = T

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value produced by ? when not short-circuiting.
Source§

type Residual = Result<Infallible, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
The type of the value passed to FromResidual::from_residual\nas part of ? when short-circuiting. Read more
Source§

fn from_output(output: <Result<T, E> as Try>::Output) -> Result<T, E>

🔬This is a nightly-only experimental API. (try_trait_v2)
Constructs the type from its Output type. Read more
Source§

fn branch(\n self,\n) -> ControlFlow<<Result<T, E> as Try>::Residual, <Result<T, E> as Try>::Output>

🔬This is a nightly-only experimental API. (try_trait_v2)
Used in ? to decide whether the operator should produce a value\n(because this returned ControlFlow::Continue)\nor propagate a value back to the caller\n(because this returned ControlFlow::Break). Read more
","Try","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> CloneFromCell for Result<T, E>
where\n T: CloneFromCell,\n E: CloneFromCell,

","CloneFromCell","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> Copy for Result<T, E>
where\n T: Copy,\n E: Copy,

","Copy","iroh_quinn_proto::coding::Result"],["
1.0.0 (const: unstable) · Source§

impl<T, E> Eq for Result<T, E>
where\n T: Eq,\n E: Eq,

","Eq","iroh_quinn_proto::coding::Result"],["
1.0.0 · Source§

impl<T, E> StructuralPartialEq for Result<T, E>

","StructuralPartialEq","iroh_quinn_proto::coding::Result"],["
Source§

impl<T, E> UseCloned for Result<T, E>
where\n T: UseCloned,\n E: UseCloned,

","UseCloned","iroh_quinn_proto::coding::Result"]]]]); + if (window.register_type_impls) { + window.register_type_impls(type_impls); + } else { + window.pending_type_impls = type_impls; + } +})() +//{"start":55,"fragment_lengths":[185513]} \ No newline at end of file