From 55addefbd0d7ddc2f5770164918320d372e27522 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 14 Jan 2026 12:41:31 +0000 Subject: [PATCH] deploy: fbe1d68c25715027f6846443b8995058d25de69c --- pr/323/docs/help.html | 2 +- .../docs/iroh_quinn/enum.ConnectionError.html | 6 +- pr/323/docs/iroh_quinn/enum.FrameType.html | 10 +- .../iroh_quinn/struct.ApplicationClose.html | 6 +- .../iroh_quinn/struct.ConnectionClose.html | 6 +- .../iroh_quinn/struct.ConnectionStats.html | 4 +- pr/323/docs/iroh_quinn/struct.FrameStats.html | 2 +- pr/323/docs/iroh_quinn/struct.PathStats.html | 6 +- .../coding/struct.UnexpectedEnd.html | 2 +- .../coding/trait.Decodable.html | 2 +- .../coding/trait.Encodable.html | 2 +- .../iroh_quinn_proto/enum.ClosePathError.html | 6 +- .../enum.ConnectionError.html | 6 +- pr/323/docs/iroh_quinn_proto/enum.Dir.html | 2 +- pr/323/docs/iroh_quinn_proto/enum.Event.html | 4 +- .../docs/iroh_quinn_proto/enum.FrameType.html | 6 +- .../docs/iroh_quinn_proto/enum.LongType.html | 6 +- .../iroh_quinn_proto/enum.MaybeFrame.html | 6 +- .../enum.PacketDecodeError.html | 12 +- .../docs/iroh_quinn_proto/enum.PathError.html | 6 +- .../docs/iroh_quinn_proto/enum.PathEvent.html | 2 +- .../enum.ProtectedHeader.html | 8 +- .../enum.SendDatagramError.html | 12 +- .../struct.ApplicationClose.html | 6 +- .../iroh_quinn_proto/struct.Connection.html | 104 +- .../struct.ConnectionClose.html | 6 +- .../struct.ConnectionStats.html | 4 +- .../iroh_quinn_proto/struct.Datagram.html | 7 +- .../iroh_quinn_proto/struct.DatagramInfo.html | 4 +- .../struct.FixedLengthConnectionIdParser.html | 6 +- .../iroh_quinn_proto/struct.FrameStats.html | 2 +- .../struct.InvalidFrameId.html | 6 +- .../struct.MultipathNotNegotiated.html | 4 +- .../iroh_quinn_proto/struct.PathStats.html | 6 +- .../struct.ProtectedInitialHeader.html | 4 +- .../iroh_quinn_proto/struct.StreamInfo.html | 4 +- .../struct.TransportError.html | 2 +- .../trait.ConnectionIdParser.html | 6 +- pr/323/docs/search.index/00037727d41b.js | 1 - pr/323/docs/search.index/001277e366cd.js | 1 - pr/323/docs/search.index/008b39f12aaf.js | 1 + pr/323/docs/search.index/009511e34648.js | 1 + pr/323/docs/search.index/0095923ba754.js | 1 - pr/323/docs/search.index/00f09416a63d.js | 1 - pr/323/docs/search.index/00f55497d47e.js | 1 - pr/323/docs/search.index/019bd84d4b2c.js | 1 + pr/323/docs/search.index/024f62593f95.js | 1 + pr/323/docs/search.index/024f75b34b20.js | 1 - pr/323/docs/search.index/0259b3ee8225.js | 1 + pr/323/docs/search.index/0424910c4c21.js | 1 + pr/323/docs/search.index/059aa106437a.js | 1 + pr/323/docs/search.index/05c72ebeb6e8.js | 1 - pr/323/docs/search.index/05cf24e46b23.js | 1 - pr/323/docs/search.index/05e417058650.js | 1 - pr/323/docs/search.index/068aba71b8f6.js | 1 + pr/323/docs/search.index/06a55ef026d4.js | 1 - pr/323/docs/search.index/06cdfcefc624.js | 1 + pr/323/docs/search.index/0773c651efc1.js | 1 - pr/323/docs/search.index/082c33a78589.js | 1 - pr/323/docs/search.index/0842ec51c54a.js | 1 - pr/323/docs/search.index/08695ab5ffcf.js | 1 + pr/323/docs/search.index/08b434a15736.js | 1 - pr/323/docs/search.index/094547135a51.js | 1 + pr/323/docs/search.index/09751f9ed1f1.js | 1 + pr/323/docs/search.index/09d1f93a766a.js | 1 + pr/323/docs/search.index/09f0b7b1cb10.js | 1 - pr/323/docs/search.index/0a5ddaea07dc.js | 1 + pr/323/docs/search.index/0a68956b7003.js | 1 + pr/323/docs/search.index/0adb0f113264.js | 1 - pr/323/docs/search.index/0adc4fcce01e.js | 1 + pr/323/docs/search.index/0af4e11d16e4.js | 1 + pr/323/docs/search.index/0b049d31f2d1.js | 1 - pr/323/docs/search.index/0b8810d9f049.js | 1 - pr/323/docs/search.index/0bfb65fd762c.js | 1 - pr/323/docs/search.index/0c4100bc39d6.js | 1 - pr/323/docs/search.index/0c9fbf07a2ce.js | 1 + pr/323/docs/search.index/0cad63531c89.js | 1 - pr/323/docs/search.index/0cc8efee7221.js | 1 + pr/323/docs/search.index/0dbe969ddebd.js | 1 - pr/323/docs/search.index/0e34ce7f7192.js | 1 - pr/323/docs/search.index/0f0fa73a3135.js | 1 + pr/323/docs/search.index/0f103fffad43.js | 1 - pr/323/docs/search.index/0f1e1d1cc48f.js | 1 - pr/323/docs/search.index/0f434453b623.js | 1 - pr/323/docs/search.index/0f835c2828ff.js | 1 - pr/323/docs/search.index/1017c7914851.js | 1 - pr/323/docs/search.index/106c7a38daa5.js | 1 + pr/323/docs/search.index/10c44e4b991a.js | 1 + pr/323/docs/search.index/1119c1193e11.js | 1 - pr/323/docs/search.index/118928699d86.js | 1 - pr/323/docs/search.index/11ac60f101c2.js | 1 - pr/323/docs/search.index/11e6700fadac.js | 1 + pr/323/docs/search.index/11fa761b7a02.js | 1 + pr/323/docs/search.index/122324c9163d.js | 1 + pr/323/docs/search.index/124a72a66be7.js | 1 + pr/323/docs/search.index/125c69e2afa8.js | 1 - pr/323/docs/search.index/12aab9d7ac8d.js | 1 - pr/323/docs/search.index/12b95dafd39c.js | 1 + pr/323/docs/search.index/12ecb21c67a4.js | 1 + pr/323/docs/search.index/1323b5cdf876.js | 1 - pr/323/docs/search.index/1365e7b5bd0a.js | 1 + pr/323/docs/search.index/138a5af4d179.js | 1 - pr/323/docs/search.index/13de217540ce.js | 1 + pr/323/docs/search.index/1409f2140ee5.js | 1 - pr/323/docs/search.index/1446e2dc05e2.js | 1 + pr/323/docs/search.index/1455b2c199cd.js | 1 - pr/323/docs/search.index/1487b03e5721.js | 1 + pr/323/docs/search.index/14e256e1d6e5.js | 1 + pr/323/docs/search.index/1511e66727f1.js | 1 - pr/323/docs/search.index/15358d1515f8.js | 1 + pr/323/docs/search.index/1552619586d7.js | 1 - pr/323/docs/search.index/159c51570c5d.js | 1 + pr/323/docs/search.index/15a27ab8af3a.js | 1 + pr/323/docs/search.index/15dba3e34fef.js | 1 + pr/323/docs/search.index/16033c7bb8f7.js | 1 + pr/323/docs/search.index/16045633eb64.js | 1 - pr/323/docs/search.index/16e43e96c3f0.js | 1 - pr/323/docs/search.index/17ad98e37001.js | 1 + pr/323/docs/search.index/18c051295b43.js | 1 - pr/323/docs/search.index/19476e380db8.js | 1 - pr/323/docs/search.index/1a238fdc3e6a.js | 1 + pr/323/docs/search.index/1a59836e6e94.js | 1 - pr/323/docs/search.index/1a6a4539ae89.js | 1 - pr/323/docs/search.index/1a6b9bf2988f.js | 1 - pr/323/docs/search.index/1a6f5bcc1ce3.js | 1 + pr/323/docs/search.index/1a85850fa4c4.js | 1 + pr/323/docs/search.index/1b3b27c553dd.js | 1 + pr/323/docs/search.index/1b5a76a5a4b0.js | 1 - pr/323/docs/search.index/1bf5442726c7.js | 1 - pr/323/docs/search.index/1c336ea7d8f9.js | 1 + pr/323/docs/search.index/1c84f4d721b0.js | 1 - pr/323/docs/search.index/1cf369215e3f.js | 1 - pr/323/docs/search.index/1d3a063d9304.js | 1 + pr/323/docs/search.index/1d528d74dc33.js | 1 + pr/323/docs/search.index/1dd9eac90f78.js | 1 + pr/323/docs/search.index/1e602721a639.js | 1 - pr/323/docs/search.index/1f0367152a3f.js | 1 - pr/323/docs/search.index/1f11eee293c6.js | 1 - pr/323/docs/search.index/1f1d2b61eca6.js | 1 - pr/323/docs/search.index/1f6d06e60df8.js | 1 - pr/323/docs/search.index/1f850a139dfa.js | 1 - pr/323/docs/search.index/1fb072ef9a66.js | 1 - pr/323/docs/search.index/200f734b64d0.js | 1 + pr/323/docs/search.index/2025f39bb570.js | 1 - pr/323/docs/search.index/2033cb7c9d15.js | 1 + pr/323/docs/search.index/2050f119410b.js | 1 + pr/323/docs/search.index/20b4057caa1a.js | 1 - pr/323/docs/search.index/20d3c333f469.js | 1 + pr/323/docs/search.index/210ec896a061.js | 1 + pr/323/docs/search.index/21bd1e52c0f9.js | 1 - pr/323/docs/search.index/21cb922259a9.js | 1 + pr/323/docs/search.index/21f7fe292e8a.js | 1 - pr/323/docs/search.index/2207279438e6.js | 1 + pr/323/docs/search.index/225a0e027688.js | 1 - pr/323/docs/search.index/22a0b3614808.js | 1 + pr/323/docs/search.index/22a72c827db8.js | 1 + pr/323/docs/search.index/24a80ee69d19.js | 1 + pr/323/docs/search.index/24eee7d39d3b.js | 1 - pr/323/docs/search.index/252df03c9a7b.js | 1 - pr/323/docs/search.index/254b4b1e845b.js | 1 - pr/323/docs/search.index/2560f8defded.js | 1 + pr/323/docs/search.index/256fee405b93.js | 1 + pr/323/docs/search.index/25a2665de949.js | 1 + pr/323/docs/search.index/25b74d0e760c.js | 1 - pr/323/docs/search.index/25b9a7be65ac.js | 1 + pr/323/docs/search.index/266afabfc690.js | 1 - pr/323/docs/search.index/2670b688dca6.js | 1 - pr/323/docs/search.index/26e84f065723.js | 1 - pr/323/docs/search.index/2713c22c8bb8.js | 1 + pr/323/docs/search.index/27198464c776.js | 1 - pr/323/docs/search.index/271cda0a044e.js | 1 - pr/323/docs/search.index/2737d083eea6.js | 1 + pr/323/docs/search.index/27677e8fce37.js | 1 - pr/323/docs/search.index/278470046fa3.js | 1 - pr/323/docs/search.index/27bc89bd5eae.js | 1 - pr/323/docs/search.index/281e2c9c5d7a.js | 1 + pr/323/docs/search.index/28d0ee3283f9.js | 1 - pr/323/docs/search.index/28e355da3bd0.js | 1 + pr/323/docs/search.index/2912fbd679c0.js | 1 + pr/323/docs/search.index/291d7178c4eb.js | 1 + pr/323/docs/search.index/29233aa98cf2.js | 1 + pr/323/docs/search.index/29912ff817d4.js | 1 - pr/323/docs/search.index/29ffaf5f711f.js | 1 - pr/323/docs/search.index/2a155524c2da.js | 1 + pr/323/docs/search.index/2a2e9d6f638a.js | 1 - pr/323/docs/search.index/2a977e08489a.js | 1 + pr/323/docs/search.index/2b09faf5f111.js | 1 - pr/323/docs/search.index/2b2e5db4b206.js | 1 + pr/323/docs/search.index/2b35726b25dd.js | 1 - pr/323/docs/search.index/2b47a005bd3d.js | 1 + pr/323/docs/search.index/2b89b9636a42.js | 1 + pr/323/docs/search.index/2c835af2195f.js | 1 + pr/323/docs/search.index/2cd2c4b18c64.js | 1 + pr/323/docs/search.index/2ce4b4057c53.js | 1 - pr/323/docs/search.index/2d2960f9b8d4.js | 1 - pr/323/docs/search.index/2d73282266f1.js | 1 + pr/323/docs/search.index/2dcb4666ca26.js | 1 + pr/323/docs/search.index/2eb9123a10fd.js | 1 - pr/323/docs/search.index/2f94b1f135cc.js | 1 + pr/323/docs/search.index/2fac3831697f.js | 1 - pr/323/docs/search.index/2fd891c7dddc.js | 1 - pr/323/docs/search.index/3010308ae38e.js | 1 + pr/323/docs/search.index/304f63ce8492.js | 1 + pr/323/docs/search.index/307a630e6173.js | 1 + pr/323/docs/search.index/30d311112226.js | 1 - pr/323/docs/search.index/30e3052528a0.js | 1 + pr/323/docs/search.index/30f0116407be.js | 1 + pr/323/docs/search.index/31047b1ed472.js | 1 + pr/323/docs/search.index/310c7951c52b.js | 1 + pr/323/docs/search.index/312e80bd916b.js | 1 - pr/323/docs/search.index/31a625c2b211.js | 1 + pr/323/docs/search.index/31f87a770c85.js | 1 + pr/323/docs/search.index/31fa229a9dc8.js | 1 + pr/323/docs/search.index/325b7d9a2242.js | 1 + pr/323/docs/search.index/33aa643b366e.js | 1 - pr/323/docs/search.index/33b38805b1c6.js | 1 - pr/323/docs/search.index/3450a1e0ecdc.js | 1 + pr/323/docs/search.index/346918baff2e.js | 1 + pr/323/docs/search.index/348567575911.js | 1 - pr/323/docs/search.index/34e0c30a19a6.js | 1 - pr/323/docs/search.index/3525cfddb103.js | 1 + pr/323/docs/search.index/35485c36125e.js | 1 + pr/323/docs/search.index/35baefe8b7a4.js | 1 + pr/323/docs/search.index/3766e569bd8e.js | 1 + pr/323/docs/search.index/378bf69c5e2d.js | 1 + pr/323/docs/search.index/382876485374.js | 1 + pr/323/docs/search.index/383ef2254913.js | 1 + pr/323/docs/search.index/38cb6e24cf5d.js | 1 - pr/323/docs/search.index/3922a16454e0.js | 1 - pr/323/docs/search.index/392fb1700c95.js | 1 - pr/323/docs/search.index/393da311e403.js | 1 - pr/323/docs/search.index/398e9b9f2f38.js | 1 + pr/323/docs/search.index/3a529860f4ab.js | 1 - pr/323/docs/search.index/3a646e8890e2.js | 1 + pr/323/docs/search.index/3a6bd631205f.js | 1 - pr/323/docs/search.index/3a74061c4f9f.js | 1 - pr/323/docs/search.index/3adb9518fd74.js | 1 - pr/323/docs/search.index/3afe7bef2754.js | 1 - pr/323/docs/search.index/3b1e455f5b16.js | 1 - pr/323/docs/search.index/3c6024526c76.js | 1 + pr/323/docs/search.index/3cc516400042.js | 1 + pr/323/docs/search.index/3ce49910eea7.js | 1 - pr/323/docs/search.index/3d023b7ea367.js | 1 + pr/323/docs/search.index/3d1db5465a6b.js | 1 + pr/323/docs/search.index/3db858c2a458.js | 1 + pr/323/docs/search.index/3e94edaedbd9.js | 1 + pr/323/docs/search.index/3f16c6444948.js | 1 - pr/323/docs/search.index/3f968c92ee8a.js | 1 + pr/323/docs/search.index/3fa1cc510dc9.js | 1 + pr/323/docs/search.index/3fd5ad3a998a.js | 1 + pr/323/docs/search.index/400291f3e805.js | 1 + pr/323/docs/search.index/402fdd03797d.js | 1 + pr/323/docs/search.index/40301c557124.js | 1 + pr/323/docs/search.index/40909919ad1e.js | 1 + pr/323/docs/search.index/4092c180885b.js | 1 + pr/323/docs/search.index/409b3322db69.js | 1 - pr/323/docs/search.index/40c49d5174fc.js | 1 + pr/323/docs/search.index/40d03efa707b.js | 1 - pr/323/docs/search.index/40fcfec120bb.js | 1 - pr/323/docs/search.index/4165ec97becb.js | 1 - pr/323/docs/search.index/4179e32fae1c.js | 1 - pr/323/docs/search.index/4204d4b478b6.js | 1 - pr/323/docs/search.index/4238388d0318.js | 1 + pr/323/docs/search.index/4265138dd576.js | 1 + pr/323/docs/search.index/427536da220b.js | 1 + pr/323/docs/search.index/4296b91ec07a.js | 1 - pr/323/docs/search.index/42b912b0d8d6.js | 1 - pr/323/docs/search.index/43625da5722d.js | 1 - pr/323/docs/search.index/43be9d36d389.js | 1 + pr/323/docs/search.index/43e96dc71aca.js | 1 + pr/323/docs/search.index/440ac285a659.js | 1 - pr/323/docs/search.index/442424c43e39.js | 1 + pr/323/docs/search.index/44630589167e.js | 1 - pr/323/docs/search.index/44a3f431018f.js | 1 - pr/323/docs/search.index/44bac3e74251.js | 1 + pr/323/docs/search.index/44bbaddcd3f2.js | 1 + pr/323/docs/search.index/44d998731581.js | 1 + pr/323/docs/search.index/44f8510a4206.js | 1 - pr/323/docs/search.index/4508fe205566.js | 1 - pr/323/docs/search.index/456883a708a4.js | 1 + pr/323/docs/search.index/4599163a6a37.js | 1 - pr/323/docs/search.index/45d12af4ff3b.js | 1 - pr/323/docs/search.index/464fd77205b4.js | 1 + pr/323/docs/search.index/4682f9d07fab.js | 1 - pr/323/docs/search.index/46a11179261c.js | 1 + pr/323/docs/search.index/46a6871d7ff2.js | 1 + pr/323/docs/search.index/46b30dba3752.js | 1 - pr/323/docs/search.index/46e6bf297811.js | 1 + pr/323/docs/search.index/46f8dda12533.js | 1 + pr/323/docs/search.index/47b13875e45c.js | 1 - pr/323/docs/search.index/47b2eb85726d.js | 1 + pr/323/docs/search.index/47cdb0491b20.js | 1 - pr/323/docs/search.index/47d3233d790f.js | 1 - pr/323/docs/search.index/48917fd6b226.js | 1 - pr/323/docs/search.index/48b3ceed3817.js | 1 - pr/323/docs/search.index/48d55f753fb7.js | 1 - pr/323/docs/search.index/4909652ee282.js | 1 - pr/323/docs/search.index/491a1690ed91.js | 1 + pr/323/docs/search.index/49b3709bea71.js | 1 + pr/323/docs/search.index/4a10d1b50063.js | 1 - pr/323/docs/search.index/4a869b28035c.js | 1 - pr/323/docs/search.index/4b0aae8452bf.js | 1 + pr/323/docs/search.index/4b88b05de3be.js | 1 - pr/323/docs/search.index/4bcec2741bca.js | 1 + pr/323/docs/search.index/4bf42a7ae759.js | 1 - pr/323/docs/search.index/4c58caa77bbd.js | 1 + pr/323/docs/search.index/4d0ce69e1262.js | 1 + pr/323/docs/search.index/4d1ba35b8882.js | 1 - pr/323/docs/search.index/4d826694c35e.js | 1 - pr/323/docs/search.index/4e219bf4cf57.js | 1 + pr/323/docs/search.index/4e3799776c96.js | 1 - pr/323/docs/search.index/4e4aaeeb80ed.js | 1 - pr/323/docs/search.index/4eb88f9761c0.js | 1 - pr/323/docs/search.index/4f2436d925ad.js | 1 - pr/323/docs/search.index/4f6d913c8284.js | 1 + pr/323/docs/search.index/4fa3a94ba9bc.js | 1 - pr/323/docs/search.index/4fab115143ff.js | 1 - pr/323/docs/search.index/4fe84adc4549.js | 1 + pr/323/docs/search.index/50097845f0e4.js | 1 + pr/323/docs/search.index/504119401aae.js | 1 + pr/323/docs/search.index/5259902f1872.js | 1 - pr/323/docs/search.index/52f2f408bfbd.js | 1 + pr/323/docs/search.index/5324e2ec6b58.js | 1 + pr/323/docs/search.index/53369ac5a189.js | 1 - pr/323/docs/search.index/5347b986d048.js | 1 - pr/323/docs/search.index/536138962c01.js | 1 + pr/323/docs/search.index/543c1d58de30.js | 1 + pr/323/docs/search.index/54422e1b039e.js | 1 + pr/323/docs/search.index/546c31e19f10.js | 1 + pr/323/docs/search.index/547e81c6465c.js | 1 - pr/323/docs/search.index/556039691422.js | 1 - pr/323/docs/search.index/55b84fdfac9a.js | 1 + pr/323/docs/search.index/56c46b53f55b.js | 1 + pr/323/docs/search.index/56c583f9554a.js | 1 + pr/323/docs/search.index/56fbecce9190.js | 1 - pr/323/docs/search.index/574d932e9c29.js | 1 - pr/323/docs/search.index/5765c75d05c7.js | 1 + pr/323/docs/search.index/577580cd62c1.js | 1 - pr/323/docs/search.index/5785ea697f45.js | 1 + pr/323/docs/search.index/57f124ccc65e.js | 1 - pr/323/docs/search.index/57fa81511bf9.js | 1 - pr/323/docs/search.index/582e538ca031.js | 1 + pr/323/docs/search.index/5833b407ef6d.js | 1 - pr/323/docs/search.index/58539c8b579b.js | 1 - pr/323/docs/search.index/588805b23add.js | 1 - pr/323/docs/search.index/590f4c9d8e22.js | 1 - pr/323/docs/search.index/591de50f5474.js | 1 + pr/323/docs/search.index/596235b6df67.js | 1 + pr/323/docs/search.index/5a18d0d28110.js | 1 + pr/323/docs/search.index/5a23f547788d.js | 1 - pr/323/docs/search.index/5a45b3b18493.js | 1 - pr/323/docs/search.index/5a6385e2310f.js | 1 - pr/323/docs/search.index/5a813a4820e0.js | 1 - pr/323/docs/search.index/5ae4a918d2e4.js | 1 - pr/323/docs/search.index/5b38d17093ab.js | 1 + pr/323/docs/search.index/5bb7576d5482.js | 1 + pr/323/docs/search.index/5beaadc1f163.js | 1 + pr/323/docs/search.index/5c64021fc741.js | 1 - pr/323/docs/search.index/5d359e55eab7.js | 1 - pr/323/docs/search.index/5d6375c5609f.js | 1 + pr/323/docs/search.index/5dd3ab5f1ef1.js | 1 + pr/323/docs/search.index/5dd8acc05993.js | 1 + pr/323/docs/search.index/5dd964887ac3.js | 1 - pr/323/docs/search.index/5e6ac22b141e.js | 1 + pr/323/docs/search.index/5fd52e1e09cb.js | 1 + pr/323/docs/search.index/5ff8ca3dd389.js | 1 + pr/323/docs/search.index/601ea84f2bcd.js | 1 + pr/323/docs/search.index/602101d51cf9.js | 1 + pr/323/docs/search.index/604c5b6bbfc7.js | 1 + pr/323/docs/search.index/604fecc750c5.js | 1 + pr/323/docs/search.index/60d3db74dc82.js | 1 + pr/323/docs/search.index/611bcb3d4014.js | 1 - pr/323/docs/search.index/617a2f2dcad5.js | 1 + pr/323/docs/search.index/618c79cfcc3c.js | 1 + pr/323/docs/search.index/61b15e853fbd.js | 1 + pr/323/docs/search.index/61d7e34f1682.js | 1 + pr/323/docs/search.index/62030eb677af.js | 1 - pr/323/docs/search.index/621b17540369.js | 1 + pr/323/docs/search.index/628077ed8c99.js | 1 + pr/323/docs/search.index/62e9a3f54499.js | 1 + pr/323/docs/search.index/6303a0a8f283.js | 1 + pr/323/docs/search.index/6342e9f2829a.js | 1 + pr/323/docs/search.index/63ffb5fa209f.js | 1 + pr/323/docs/search.index/642a5cb13021.js | 1 - pr/323/docs/search.index/6465615c7c4e.js | 1 - pr/323/docs/search.index/64816476bdd2.js | 1 - pr/323/docs/search.index/648a36bbbcb1.js | 1 - pr/323/docs/search.index/64a47a036ee9.js | 1 - pr/323/docs/search.index/64d11c8fb329.js | 1 - pr/323/docs/search.index/64e752cb7ef8.js | 1 - pr/323/docs/search.index/64eb9b668729.js | 1 - pr/323/docs/search.index/65694b9e3122.js | 1 - pr/323/docs/search.index/65e587e4fe56.js | 1 - pr/323/docs/search.index/65f055028fa9.js | 1 - pr/323/docs/search.index/663cc194c319.js | 1 + pr/323/docs/search.index/6642cdb55a94.js | 1 + pr/323/docs/search.index/674a3988b3e5.js | 1 + pr/323/docs/search.index/6757d6132188.js | 1 + pr/323/docs/search.index/675e04200622.js | 1 + pr/323/docs/search.index/678443ae36ae.js | 1 - pr/323/docs/search.index/679d38ece98b.js | 1 - pr/323/docs/search.index/67d6567c6683.js | 1 + pr/323/docs/search.index/681f74d11d66.js | 1 - pr/323/docs/search.index/684759f8b36f.js | 1 + pr/323/docs/search.index/6977b4c99668.js | 1 - pr/323/docs/search.index/69bf077966ff.js | 1 - pr/323/docs/search.index/69eecfc94a5f.js | 1 + pr/323/docs/search.index/6a91ed0cfbc4.js | 1 - pr/323/docs/search.index/6a9268da8c28.js | 1 + pr/323/docs/search.index/6a9ce3d21505.js | 1 + pr/323/docs/search.index/6adf1d99a4d5.js | 1 - pr/323/docs/search.index/6b465541ac5c.js | 1 - pr/323/docs/search.index/6b51a243b1b9.js | 1 - pr/323/docs/search.index/6b5ec36867cc.js | 1 - pr/323/docs/search.index/6b85aaae5278.js | 1 + pr/323/docs/search.index/6bdb48f6e8fe.js | 1 - pr/323/docs/search.index/6bf8cc22533d.js | 1 + pr/323/docs/search.index/6c103fa720e9.js | 1 + pr/323/docs/search.index/6c11604302a6.js | 1 - pr/323/docs/search.index/6c2ea040f627.js | 1 - pr/323/docs/search.index/6c6bec7a76cc.js | 1 - pr/323/docs/search.index/6c99d3e874b0.js | 1 + pr/323/docs/search.index/6c9fa969dbc1.js | 1 + pr/323/docs/search.index/6c9fb3b20c08.js | 1 + pr/323/docs/search.index/6ca02f986331.js | 1 + pr/323/docs/search.index/6ca19f0a3313.js | 1 - pr/323/docs/search.index/6ccad3bd4a7d.js | 1 - pr/323/docs/search.index/6da9782962b9.js | 1 - pr/323/docs/search.index/6dc00ec2b861.js | 1 + pr/323/docs/search.index/6e4b7a20ef6a.js | 1 - pr/323/docs/search.index/6ee2f712ff40.js | 1 - pr/323/docs/search.index/6fcb853616ae.js | 1 - pr/323/docs/search.index/6ff8f30623c6.js | 1 + pr/323/docs/search.index/704f43eab3eb.js | 1 + pr/323/docs/search.index/7092b04deb4e.js | 1 + pr/323/docs/search.index/711db6f37c54.js | 1 - pr/323/docs/search.index/713ea441eaa3.js | 1 - pr/323/docs/search.index/72352df403a5.js | 1 - pr/323/docs/search.index/729cae89d66a.js | 1 + pr/323/docs/search.index/731dde1dce82.js | 1 - pr/323/docs/search.index/734aaab2a00b.js | 1 - pr/323/docs/search.index/7393667e232e.js | 1 + pr/323/docs/search.index/7489318a49dc.js | 1 - pr/323/docs/search.index/7517f383313f.js | 1 + pr/323/docs/search.index/75b83b34138f.js | 1 - pr/323/docs/search.index/7635a73c43aa.js | 1 + pr/323/docs/search.index/7694027599d3.js | 1 + pr/323/docs/search.index/7707a0af636d.js | 1 - pr/323/docs/search.index/77f373856497.js | 1 - pr/323/docs/search.index/78318d6c832f.js | 1 - pr/323/docs/search.index/7861667f4a43.js | 1 - pr/323/docs/search.index/78f080218498.js | 1 - pr/323/docs/search.index/79a2ddf51533.js | 1 + pr/323/docs/search.index/7b387f9a1467.js | 1 - pr/323/docs/search.index/7b519bf60e51.js | 1 + pr/323/docs/search.index/7dea95897c5e.js | 1 + pr/323/docs/search.index/7e247cb6de94.js | 1 - pr/323/docs/search.index/7e614ec002b9.js | 1 - pr/323/docs/search.index/7e738badf46e.js | 1 + pr/323/docs/search.index/7ebc0d37a846.js | 1 - pr/323/docs/search.index/7edd6678fc26.js | 1 + pr/323/docs/search.index/7eff5365274d.js | 1 + pr/323/docs/search.index/7f51efe5046e.js | 1 - pr/323/docs/search.index/7fc1930a76ba.js | 1 - .../docs/search.index/entry/23e89d8be7e8.js | 1 - .../docs/search.index/entry/c07d808995d5.js | 1 + .../search.index/function/0a18a7c168e3.js | 1 - .../search.index/function/3b5fc09aa528.js | 1 + .../generic_inverted_index/6ca95edf6c4a.js | 1 + .../generic_inverted_index/76f3213be5b5.js | 1 - pr/323/docs/search.index/name/06100f36a877.js | 1 + pr/323/docs/search.index/name/52ee5dc5acb9.js | 1 - .../normalizedName/9cdde8eea858.js | 1 - .../normalizedName/d40cd004cf8f.js | 1 + .../path/{3e0347ff70ce.js => f16d052caa1a.js} | 2 +- pr/323/docs/search.index/root.js | 2 +- pr/323/docs/search.index/type/21bdc4104a40.js | 1 + pr/323/docs/search.index/type/ccb5a8e6883b.js | 1 - pr/323/docs/settings.html | 2 +- .../connection/datagrams.rs.html | 100 +- .../iroh_quinn_proto/connection/mod.rs.html | 13399 ++++++++-------- .../connection/packet_builder.rs.html | 786 +- .../iroh_quinn_proto/connection/qlog.rs.html | 2239 +-- .../iroh_quinn_proto/connection/stats.rs.html | 278 +- .../connection/streams/mod.rs.html | 2 +- .../connection/streams/state.rs.html | 4051 +++-- .../docs/src/iroh_quinn_proto/frame.rs.html | 4804 +++--- .../docs/src/iroh_quinn_proto/packet.rs.html | 1023 +- .../docs/trait.impl/core/fmt/trait.Display.js | 4 +- 489 files changed, 13888 insertions(+), 13537 deletions(-) delete mode 100644 pr/323/docs/search.index/00037727d41b.js delete mode 100644 pr/323/docs/search.index/001277e366cd.js create mode 100644 pr/323/docs/search.index/008b39f12aaf.js create mode 100644 pr/323/docs/search.index/009511e34648.js delete mode 100644 pr/323/docs/search.index/0095923ba754.js delete mode 100644 pr/323/docs/search.index/00f09416a63d.js delete mode 100644 pr/323/docs/search.index/00f55497d47e.js create mode 100644 pr/323/docs/search.index/019bd84d4b2c.js create mode 100644 pr/323/docs/search.index/024f62593f95.js delete mode 100644 pr/323/docs/search.index/024f75b34b20.js create mode 100644 pr/323/docs/search.index/0259b3ee8225.js create mode 100644 pr/323/docs/search.index/0424910c4c21.js create mode 100644 pr/323/docs/search.index/059aa106437a.js delete mode 100644 pr/323/docs/search.index/05c72ebeb6e8.js delete mode 100644 pr/323/docs/search.index/05cf24e46b23.js delete mode 100644 pr/323/docs/search.index/05e417058650.js create mode 100644 pr/323/docs/search.index/068aba71b8f6.js delete mode 100644 pr/323/docs/search.index/06a55ef026d4.js create mode 100644 pr/323/docs/search.index/06cdfcefc624.js delete mode 100644 pr/323/docs/search.index/0773c651efc1.js delete mode 100644 pr/323/docs/search.index/082c33a78589.js delete mode 100644 pr/323/docs/search.index/0842ec51c54a.js create mode 100644 pr/323/docs/search.index/08695ab5ffcf.js delete mode 100644 pr/323/docs/search.index/08b434a15736.js create mode 100644 pr/323/docs/search.index/094547135a51.js create mode 100644 pr/323/docs/search.index/09751f9ed1f1.js create mode 100644 pr/323/docs/search.index/09d1f93a766a.js delete mode 100644 pr/323/docs/search.index/09f0b7b1cb10.js create mode 100644 pr/323/docs/search.index/0a5ddaea07dc.js create mode 100644 pr/323/docs/search.index/0a68956b7003.js delete mode 100644 pr/323/docs/search.index/0adb0f113264.js create mode 100644 pr/323/docs/search.index/0adc4fcce01e.js create mode 100644 pr/323/docs/search.index/0af4e11d16e4.js delete mode 100644 pr/323/docs/search.index/0b049d31f2d1.js delete mode 100644 pr/323/docs/search.index/0b8810d9f049.js delete mode 100644 pr/323/docs/search.index/0bfb65fd762c.js delete mode 100644 pr/323/docs/search.index/0c4100bc39d6.js create mode 100644 pr/323/docs/search.index/0c9fbf07a2ce.js delete mode 100644 pr/323/docs/search.index/0cad63531c89.js create mode 100644 pr/323/docs/search.index/0cc8efee7221.js delete mode 100644 pr/323/docs/search.index/0dbe969ddebd.js delete mode 100644 pr/323/docs/search.index/0e34ce7f7192.js create mode 100644 pr/323/docs/search.index/0f0fa73a3135.js delete mode 100644 pr/323/docs/search.index/0f103fffad43.js delete mode 100644 pr/323/docs/search.index/0f1e1d1cc48f.js delete mode 100644 pr/323/docs/search.index/0f434453b623.js delete mode 100644 pr/323/docs/search.index/0f835c2828ff.js delete mode 100644 pr/323/docs/search.index/1017c7914851.js create mode 100644 pr/323/docs/search.index/106c7a38daa5.js create mode 100644 pr/323/docs/search.index/10c44e4b991a.js delete mode 100644 pr/323/docs/search.index/1119c1193e11.js delete mode 100644 pr/323/docs/search.index/118928699d86.js delete mode 100644 pr/323/docs/search.index/11ac60f101c2.js create mode 100644 pr/323/docs/search.index/11e6700fadac.js create mode 100644 pr/323/docs/search.index/11fa761b7a02.js create mode 100644 pr/323/docs/search.index/122324c9163d.js create mode 100644 pr/323/docs/search.index/124a72a66be7.js delete mode 100644 pr/323/docs/search.index/125c69e2afa8.js delete mode 100644 pr/323/docs/search.index/12aab9d7ac8d.js create mode 100644 pr/323/docs/search.index/12b95dafd39c.js create mode 100644 pr/323/docs/search.index/12ecb21c67a4.js delete mode 100644 pr/323/docs/search.index/1323b5cdf876.js create mode 100644 pr/323/docs/search.index/1365e7b5bd0a.js delete mode 100644 pr/323/docs/search.index/138a5af4d179.js create mode 100644 pr/323/docs/search.index/13de217540ce.js delete mode 100644 pr/323/docs/search.index/1409f2140ee5.js create mode 100644 pr/323/docs/search.index/1446e2dc05e2.js delete mode 100644 pr/323/docs/search.index/1455b2c199cd.js create mode 100644 pr/323/docs/search.index/1487b03e5721.js create mode 100644 pr/323/docs/search.index/14e256e1d6e5.js delete mode 100644 pr/323/docs/search.index/1511e66727f1.js create mode 100644 pr/323/docs/search.index/15358d1515f8.js delete mode 100644 pr/323/docs/search.index/1552619586d7.js create mode 100644 pr/323/docs/search.index/159c51570c5d.js create mode 100644 pr/323/docs/search.index/15a27ab8af3a.js create mode 100644 pr/323/docs/search.index/15dba3e34fef.js create mode 100644 pr/323/docs/search.index/16033c7bb8f7.js delete mode 100644 pr/323/docs/search.index/16045633eb64.js delete mode 100644 pr/323/docs/search.index/16e43e96c3f0.js create mode 100644 pr/323/docs/search.index/17ad98e37001.js delete mode 100644 pr/323/docs/search.index/18c051295b43.js delete mode 100644 pr/323/docs/search.index/19476e380db8.js create mode 100644 pr/323/docs/search.index/1a238fdc3e6a.js delete mode 100644 pr/323/docs/search.index/1a59836e6e94.js delete mode 100644 pr/323/docs/search.index/1a6a4539ae89.js delete mode 100644 pr/323/docs/search.index/1a6b9bf2988f.js create mode 100644 pr/323/docs/search.index/1a6f5bcc1ce3.js create mode 100644 pr/323/docs/search.index/1a85850fa4c4.js create mode 100644 pr/323/docs/search.index/1b3b27c553dd.js delete mode 100644 pr/323/docs/search.index/1b5a76a5a4b0.js delete mode 100644 pr/323/docs/search.index/1bf5442726c7.js create mode 100644 pr/323/docs/search.index/1c336ea7d8f9.js delete mode 100644 pr/323/docs/search.index/1c84f4d721b0.js delete mode 100644 pr/323/docs/search.index/1cf369215e3f.js create mode 100644 pr/323/docs/search.index/1d3a063d9304.js create mode 100644 pr/323/docs/search.index/1d528d74dc33.js create mode 100644 pr/323/docs/search.index/1dd9eac90f78.js delete mode 100644 pr/323/docs/search.index/1e602721a639.js delete mode 100644 pr/323/docs/search.index/1f0367152a3f.js delete mode 100644 pr/323/docs/search.index/1f11eee293c6.js delete mode 100644 pr/323/docs/search.index/1f1d2b61eca6.js delete mode 100644 pr/323/docs/search.index/1f6d06e60df8.js delete mode 100644 pr/323/docs/search.index/1f850a139dfa.js delete mode 100644 pr/323/docs/search.index/1fb072ef9a66.js create mode 100644 pr/323/docs/search.index/200f734b64d0.js delete mode 100644 pr/323/docs/search.index/2025f39bb570.js create mode 100644 pr/323/docs/search.index/2033cb7c9d15.js create mode 100644 pr/323/docs/search.index/2050f119410b.js delete mode 100644 pr/323/docs/search.index/20b4057caa1a.js create mode 100644 pr/323/docs/search.index/20d3c333f469.js create mode 100644 pr/323/docs/search.index/210ec896a061.js delete mode 100644 pr/323/docs/search.index/21bd1e52c0f9.js create mode 100644 pr/323/docs/search.index/21cb922259a9.js delete mode 100644 pr/323/docs/search.index/21f7fe292e8a.js create mode 100644 pr/323/docs/search.index/2207279438e6.js delete mode 100644 pr/323/docs/search.index/225a0e027688.js create mode 100644 pr/323/docs/search.index/22a0b3614808.js create mode 100644 pr/323/docs/search.index/22a72c827db8.js create mode 100644 pr/323/docs/search.index/24a80ee69d19.js delete mode 100644 pr/323/docs/search.index/24eee7d39d3b.js delete mode 100644 pr/323/docs/search.index/252df03c9a7b.js delete mode 100644 pr/323/docs/search.index/254b4b1e845b.js create mode 100644 pr/323/docs/search.index/2560f8defded.js create mode 100644 pr/323/docs/search.index/256fee405b93.js create mode 100644 pr/323/docs/search.index/25a2665de949.js delete mode 100644 pr/323/docs/search.index/25b74d0e760c.js create mode 100644 pr/323/docs/search.index/25b9a7be65ac.js delete mode 100644 pr/323/docs/search.index/266afabfc690.js delete mode 100644 pr/323/docs/search.index/2670b688dca6.js delete mode 100644 pr/323/docs/search.index/26e84f065723.js create mode 100644 pr/323/docs/search.index/2713c22c8bb8.js delete mode 100644 pr/323/docs/search.index/27198464c776.js delete mode 100644 pr/323/docs/search.index/271cda0a044e.js create mode 100644 pr/323/docs/search.index/2737d083eea6.js delete mode 100644 pr/323/docs/search.index/27677e8fce37.js delete mode 100644 pr/323/docs/search.index/278470046fa3.js delete mode 100644 pr/323/docs/search.index/27bc89bd5eae.js create mode 100644 pr/323/docs/search.index/281e2c9c5d7a.js delete mode 100644 pr/323/docs/search.index/28d0ee3283f9.js create mode 100644 pr/323/docs/search.index/28e355da3bd0.js create mode 100644 pr/323/docs/search.index/2912fbd679c0.js create mode 100644 pr/323/docs/search.index/291d7178c4eb.js create mode 100644 pr/323/docs/search.index/29233aa98cf2.js delete mode 100644 pr/323/docs/search.index/29912ff817d4.js delete mode 100644 pr/323/docs/search.index/29ffaf5f711f.js create mode 100644 pr/323/docs/search.index/2a155524c2da.js delete mode 100644 pr/323/docs/search.index/2a2e9d6f638a.js create mode 100644 pr/323/docs/search.index/2a977e08489a.js delete mode 100644 pr/323/docs/search.index/2b09faf5f111.js create mode 100644 pr/323/docs/search.index/2b2e5db4b206.js delete mode 100644 pr/323/docs/search.index/2b35726b25dd.js create mode 100644 pr/323/docs/search.index/2b47a005bd3d.js create mode 100644 pr/323/docs/search.index/2b89b9636a42.js create mode 100644 pr/323/docs/search.index/2c835af2195f.js create mode 100644 pr/323/docs/search.index/2cd2c4b18c64.js delete mode 100644 pr/323/docs/search.index/2ce4b4057c53.js delete mode 100644 pr/323/docs/search.index/2d2960f9b8d4.js create mode 100644 pr/323/docs/search.index/2d73282266f1.js create mode 100644 pr/323/docs/search.index/2dcb4666ca26.js delete mode 100644 pr/323/docs/search.index/2eb9123a10fd.js create mode 100644 pr/323/docs/search.index/2f94b1f135cc.js delete mode 100644 pr/323/docs/search.index/2fac3831697f.js delete mode 100644 pr/323/docs/search.index/2fd891c7dddc.js create mode 100644 pr/323/docs/search.index/3010308ae38e.js create mode 100644 pr/323/docs/search.index/304f63ce8492.js create mode 100644 pr/323/docs/search.index/307a630e6173.js delete mode 100644 pr/323/docs/search.index/30d311112226.js create mode 100644 pr/323/docs/search.index/30e3052528a0.js create mode 100644 pr/323/docs/search.index/30f0116407be.js create mode 100644 pr/323/docs/search.index/31047b1ed472.js create mode 100644 pr/323/docs/search.index/310c7951c52b.js delete mode 100644 pr/323/docs/search.index/312e80bd916b.js create mode 100644 pr/323/docs/search.index/31a625c2b211.js create mode 100644 pr/323/docs/search.index/31f87a770c85.js create mode 100644 pr/323/docs/search.index/31fa229a9dc8.js create mode 100644 pr/323/docs/search.index/325b7d9a2242.js delete mode 100644 pr/323/docs/search.index/33aa643b366e.js delete mode 100644 pr/323/docs/search.index/33b38805b1c6.js create mode 100644 pr/323/docs/search.index/3450a1e0ecdc.js create mode 100644 pr/323/docs/search.index/346918baff2e.js delete mode 100644 pr/323/docs/search.index/348567575911.js delete mode 100644 pr/323/docs/search.index/34e0c30a19a6.js create mode 100644 pr/323/docs/search.index/3525cfddb103.js create mode 100644 pr/323/docs/search.index/35485c36125e.js create mode 100644 pr/323/docs/search.index/35baefe8b7a4.js create mode 100644 pr/323/docs/search.index/3766e569bd8e.js create mode 100644 pr/323/docs/search.index/378bf69c5e2d.js create mode 100644 pr/323/docs/search.index/382876485374.js create mode 100644 pr/323/docs/search.index/383ef2254913.js delete mode 100644 pr/323/docs/search.index/38cb6e24cf5d.js delete mode 100644 pr/323/docs/search.index/3922a16454e0.js delete mode 100644 pr/323/docs/search.index/392fb1700c95.js delete mode 100644 pr/323/docs/search.index/393da311e403.js create mode 100644 pr/323/docs/search.index/398e9b9f2f38.js delete mode 100644 pr/323/docs/search.index/3a529860f4ab.js create mode 100644 pr/323/docs/search.index/3a646e8890e2.js delete mode 100644 pr/323/docs/search.index/3a6bd631205f.js delete mode 100644 pr/323/docs/search.index/3a74061c4f9f.js delete mode 100644 pr/323/docs/search.index/3adb9518fd74.js delete mode 100644 pr/323/docs/search.index/3afe7bef2754.js delete mode 100644 pr/323/docs/search.index/3b1e455f5b16.js create mode 100644 pr/323/docs/search.index/3c6024526c76.js create mode 100644 pr/323/docs/search.index/3cc516400042.js delete mode 100644 pr/323/docs/search.index/3ce49910eea7.js create mode 100644 pr/323/docs/search.index/3d023b7ea367.js create mode 100644 pr/323/docs/search.index/3d1db5465a6b.js create mode 100644 pr/323/docs/search.index/3db858c2a458.js create mode 100644 pr/323/docs/search.index/3e94edaedbd9.js delete mode 100644 pr/323/docs/search.index/3f16c6444948.js create mode 100644 pr/323/docs/search.index/3f968c92ee8a.js create mode 100644 pr/323/docs/search.index/3fa1cc510dc9.js create mode 100644 pr/323/docs/search.index/3fd5ad3a998a.js create mode 100644 pr/323/docs/search.index/400291f3e805.js create mode 100644 pr/323/docs/search.index/402fdd03797d.js create mode 100644 pr/323/docs/search.index/40301c557124.js create mode 100644 pr/323/docs/search.index/40909919ad1e.js create mode 100644 pr/323/docs/search.index/4092c180885b.js delete mode 100644 pr/323/docs/search.index/409b3322db69.js create mode 100644 pr/323/docs/search.index/40c49d5174fc.js delete mode 100644 pr/323/docs/search.index/40d03efa707b.js delete mode 100644 pr/323/docs/search.index/40fcfec120bb.js delete mode 100644 pr/323/docs/search.index/4165ec97becb.js delete mode 100644 pr/323/docs/search.index/4179e32fae1c.js delete mode 100644 pr/323/docs/search.index/4204d4b478b6.js create mode 100644 pr/323/docs/search.index/4238388d0318.js create mode 100644 pr/323/docs/search.index/4265138dd576.js create mode 100644 pr/323/docs/search.index/427536da220b.js delete mode 100644 pr/323/docs/search.index/4296b91ec07a.js delete mode 100644 pr/323/docs/search.index/42b912b0d8d6.js delete mode 100644 pr/323/docs/search.index/43625da5722d.js create mode 100644 pr/323/docs/search.index/43be9d36d389.js create mode 100644 pr/323/docs/search.index/43e96dc71aca.js delete mode 100644 pr/323/docs/search.index/440ac285a659.js create mode 100644 pr/323/docs/search.index/442424c43e39.js delete mode 100644 pr/323/docs/search.index/44630589167e.js delete mode 100644 pr/323/docs/search.index/44a3f431018f.js create mode 100644 pr/323/docs/search.index/44bac3e74251.js create mode 100644 pr/323/docs/search.index/44bbaddcd3f2.js create mode 100644 pr/323/docs/search.index/44d998731581.js delete mode 100644 pr/323/docs/search.index/44f8510a4206.js delete mode 100644 pr/323/docs/search.index/4508fe205566.js create mode 100644 pr/323/docs/search.index/456883a708a4.js delete mode 100644 pr/323/docs/search.index/4599163a6a37.js delete mode 100644 pr/323/docs/search.index/45d12af4ff3b.js create mode 100644 pr/323/docs/search.index/464fd77205b4.js delete mode 100644 pr/323/docs/search.index/4682f9d07fab.js create mode 100644 pr/323/docs/search.index/46a11179261c.js create mode 100644 pr/323/docs/search.index/46a6871d7ff2.js delete mode 100644 pr/323/docs/search.index/46b30dba3752.js create mode 100644 pr/323/docs/search.index/46e6bf297811.js create mode 100644 pr/323/docs/search.index/46f8dda12533.js delete mode 100644 pr/323/docs/search.index/47b13875e45c.js create mode 100644 pr/323/docs/search.index/47b2eb85726d.js delete mode 100644 pr/323/docs/search.index/47cdb0491b20.js delete mode 100644 pr/323/docs/search.index/47d3233d790f.js delete mode 100644 pr/323/docs/search.index/48917fd6b226.js delete mode 100644 pr/323/docs/search.index/48b3ceed3817.js delete mode 100644 pr/323/docs/search.index/48d55f753fb7.js delete mode 100644 pr/323/docs/search.index/4909652ee282.js create mode 100644 pr/323/docs/search.index/491a1690ed91.js create mode 100644 pr/323/docs/search.index/49b3709bea71.js delete mode 100644 pr/323/docs/search.index/4a10d1b50063.js delete mode 100644 pr/323/docs/search.index/4a869b28035c.js create mode 100644 pr/323/docs/search.index/4b0aae8452bf.js delete mode 100644 pr/323/docs/search.index/4b88b05de3be.js create mode 100644 pr/323/docs/search.index/4bcec2741bca.js delete mode 100644 pr/323/docs/search.index/4bf42a7ae759.js create mode 100644 pr/323/docs/search.index/4c58caa77bbd.js create mode 100644 pr/323/docs/search.index/4d0ce69e1262.js delete mode 100644 pr/323/docs/search.index/4d1ba35b8882.js delete mode 100644 pr/323/docs/search.index/4d826694c35e.js create mode 100644 pr/323/docs/search.index/4e219bf4cf57.js delete mode 100644 pr/323/docs/search.index/4e3799776c96.js delete mode 100644 pr/323/docs/search.index/4e4aaeeb80ed.js delete mode 100644 pr/323/docs/search.index/4eb88f9761c0.js delete mode 100644 pr/323/docs/search.index/4f2436d925ad.js create mode 100644 pr/323/docs/search.index/4f6d913c8284.js delete mode 100644 pr/323/docs/search.index/4fa3a94ba9bc.js delete mode 100644 pr/323/docs/search.index/4fab115143ff.js create mode 100644 pr/323/docs/search.index/4fe84adc4549.js create mode 100644 pr/323/docs/search.index/50097845f0e4.js create mode 100644 pr/323/docs/search.index/504119401aae.js delete mode 100644 pr/323/docs/search.index/5259902f1872.js create mode 100644 pr/323/docs/search.index/52f2f408bfbd.js create mode 100644 pr/323/docs/search.index/5324e2ec6b58.js delete mode 100644 pr/323/docs/search.index/53369ac5a189.js delete mode 100644 pr/323/docs/search.index/5347b986d048.js create mode 100644 pr/323/docs/search.index/536138962c01.js create mode 100644 pr/323/docs/search.index/543c1d58de30.js create mode 100644 pr/323/docs/search.index/54422e1b039e.js create mode 100644 pr/323/docs/search.index/546c31e19f10.js delete mode 100644 pr/323/docs/search.index/547e81c6465c.js delete mode 100644 pr/323/docs/search.index/556039691422.js create mode 100644 pr/323/docs/search.index/55b84fdfac9a.js create mode 100644 pr/323/docs/search.index/56c46b53f55b.js create mode 100644 pr/323/docs/search.index/56c583f9554a.js delete mode 100644 pr/323/docs/search.index/56fbecce9190.js delete mode 100644 pr/323/docs/search.index/574d932e9c29.js create mode 100644 pr/323/docs/search.index/5765c75d05c7.js delete mode 100644 pr/323/docs/search.index/577580cd62c1.js create mode 100644 pr/323/docs/search.index/5785ea697f45.js delete mode 100644 pr/323/docs/search.index/57f124ccc65e.js delete mode 100644 pr/323/docs/search.index/57fa81511bf9.js create mode 100644 pr/323/docs/search.index/582e538ca031.js delete mode 100644 pr/323/docs/search.index/5833b407ef6d.js delete mode 100644 pr/323/docs/search.index/58539c8b579b.js delete mode 100644 pr/323/docs/search.index/588805b23add.js delete mode 100644 pr/323/docs/search.index/590f4c9d8e22.js create mode 100644 pr/323/docs/search.index/591de50f5474.js create mode 100644 pr/323/docs/search.index/596235b6df67.js create mode 100644 pr/323/docs/search.index/5a18d0d28110.js delete mode 100644 pr/323/docs/search.index/5a23f547788d.js delete mode 100644 pr/323/docs/search.index/5a45b3b18493.js delete mode 100644 pr/323/docs/search.index/5a6385e2310f.js delete mode 100644 pr/323/docs/search.index/5a813a4820e0.js delete mode 100644 pr/323/docs/search.index/5ae4a918d2e4.js create mode 100644 pr/323/docs/search.index/5b38d17093ab.js create mode 100644 pr/323/docs/search.index/5bb7576d5482.js create mode 100644 pr/323/docs/search.index/5beaadc1f163.js delete mode 100644 pr/323/docs/search.index/5c64021fc741.js delete mode 100644 pr/323/docs/search.index/5d359e55eab7.js create mode 100644 pr/323/docs/search.index/5d6375c5609f.js create mode 100644 pr/323/docs/search.index/5dd3ab5f1ef1.js create mode 100644 pr/323/docs/search.index/5dd8acc05993.js delete mode 100644 pr/323/docs/search.index/5dd964887ac3.js create mode 100644 pr/323/docs/search.index/5e6ac22b141e.js create mode 100644 pr/323/docs/search.index/5fd52e1e09cb.js create mode 100644 pr/323/docs/search.index/5ff8ca3dd389.js create mode 100644 pr/323/docs/search.index/601ea84f2bcd.js create mode 100644 pr/323/docs/search.index/602101d51cf9.js create mode 100644 pr/323/docs/search.index/604c5b6bbfc7.js create mode 100644 pr/323/docs/search.index/604fecc750c5.js create mode 100644 pr/323/docs/search.index/60d3db74dc82.js delete mode 100644 pr/323/docs/search.index/611bcb3d4014.js create mode 100644 pr/323/docs/search.index/617a2f2dcad5.js create mode 100644 pr/323/docs/search.index/618c79cfcc3c.js create mode 100644 pr/323/docs/search.index/61b15e853fbd.js create mode 100644 pr/323/docs/search.index/61d7e34f1682.js delete mode 100644 pr/323/docs/search.index/62030eb677af.js create mode 100644 pr/323/docs/search.index/621b17540369.js create mode 100644 pr/323/docs/search.index/628077ed8c99.js create mode 100644 pr/323/docs/search.index/62e9a3f54499.js create mode 100644 pr/323/docs/search.index/6303a0a8f283.js create mode 100644 pr/323/docs/search.index/6342e9f2829a.js create mode 100644 pr/323/docs/search.index/63ffb5fa209f.js delete mode 100644 pr/323/docs/search.index/642a5cb13021.js delete mode 100644 pr/323/docs/search.index/6465615c7c4e.js delete mode 100644 pr/323/docs/search.index/64816476bdd2.js delete mode 100644 pr/323/docs/search.index/648a36bbbcb1.js delete mode 100644 pr/323/docs/search.index/64a47a036ee9.js delete mode 100644 pr/323/docs/search.index/64d11c8fb329.js delete mode 100644 pr/323/docs/search.index/64e752cb7ef8.js delete mode 100644 pr/323/docs/search.index/64eb9b668729.js delete mode 100644 pr/323/docs/search.index/65694b9e3122.js delete mode 100644 pr/323/docs/search.index/65e587e4fe56.js delete mode 100644 pr/323/docs/search.index/65f055028fa9.js create mode 100644 pr/323/docs/search.index/663cc194c319.js create mode 100644 pr/323/docs/search.index/6642cdb55a94.js create mode 100644 pr/323/docs/search.index/674a3988b3e5.js create mode 100644 pr/323/docs/search.index/6757d6132188.js create mode 100644 pr/323/docs/search.index/675e04200622.js delete mode 100644 pr/323/docs/search.index/678443ae36ae.js delete mode 100644 pr/323/docs/search.index/679d38ece98b.js create mode 100644 pr/323/docs/search.index/67d6567c6683.js delete mode 100644 pr/323/docs/search.index/681f74d11d66.js create mode 100644 pr/323/docs/search.index/684759f8b36f.js delete mode 100644 pr/323/docs/search.index/6977b4c99668.js delete mode 100644 pr/323/docs/search.index/69bf077966ff.js create mode 100644 pr/323/docs/search.index/69eecfc94a5f.js delete mode 100644 pr/323/docs/search.index/6a91ed0cfbc4.js create mode 100644 pr/323/docs/search.index/6a9268da8c28.js create mode 100644 pr/323/docs/search.index/6a9ce3d21505.js delete mode 100644 pr/323/docs/search.index/6adf1d99a4d5.js delete mode 100644 pr/323/docs/search.index/6b465541ac5c.js delete mode 100644 pr/323/docs/search.index/6b51a243b1b9.js delete mode 100644 pr/323/docs/search.index/6b5ec36867cc.js create mode 100644 pr/323/docs/search.index/6b85aaae5278.js delete mode 100644 pr/323/docs/search.index/6bdb48f6e8fe.js create mode 100644 pr/323/docs/search.index/6bf8cc22533d.js create mode 100644 pr/323/docs/search.index/6c103fa720e9.js delete mode 100644 pr/323/docs/search.index/6c11604302a6.js delete mode 100644 pr/323/docs/search.index/6c2ea040f627.js delete mode 100644 pr/323/docs/search.index/6c6bec7a76cc.js create mode 100644 pr/323/docs/search.index/6c99d3e874b0.js create mode 100644 pr/323/docs/search.index/6c9fa969dbc1.js create mode 100644 pr/323/docs/search.index/6c9fb3b20c08.js create mode 100644 pr/323/docs/search.index/6ca02f986331.js delete mode 100644 pr/323/docs/search.index/6ca19f0a3313.js delete mode 100644 pr/323/docs/search.index/6ccad3bd4a7d.js delete mode 100644 pr/323/docs/search.index/6da9782962b9.js create mode 100644 pr/323/docs/search.index/6dc00ec2b861.js delete mode 100644 pr/323/docs/search.index/6e4b7a20ef6a.js delete mode 100644 pr/323/docs/search.index/6ee2f712ff40.js delete mode 100644 pr/323/docs/search.index/6fcb853616ae.js create mode 100644 pr/323/docs/search.index/6ff8f30623c6.js create mode 100644 pr/323/docs/search.index/704f43eab3eb.js create mode 100644 pr/323/docs/search.index/7092b04deb4e.js delete mode 100644 pr/323/docs/search.index/711db6f37c54.js delete mode 100644 pr/323/docs/search.index/713ea441eaa3.js delete mode 100644 pr/323/docs/search.index/72352df403a5.js create mode 100644 pr/323/docs/search.index/729cae89d66a.js delete mode 100644 pr/323/docs/search.index/731dde1dce82.js delete mode 100644 pr/323/docs/search.index/734aaab2a00b.js create mode 100644 pr/323/docs/search.index/7393667e232e.js delete mode 100644 pr/323/docs/search.index/7489318a49dc.js create mode 100644 pr/323/docs/search.index/7517f383313f.js delete mode 100644 pr/323/docs/search.index/75b83b34138f.js create mode 100644 pr/323/docs/search.index/7635a73c43aa.js create mode 100644 pr/323/docs/search.index/7694027599d3.js delete mode 100644 pr/323/docs/search.index/7707a0af636d.js delete mode 100644 pr/323/docs/search.index/77f373856497.js delete mode 100644 pr/323/docs/search.index/78318d6c832f.js delete mode 100644 pr/323/docs/search.index/7861667f4a43.js delete mode 100644 pr/323/docs/search.index/78f080218498.js create mode 100644 pr/323/docs/search.index/79a2ddf51533.js delete mode 100644 pr/323/docs/search.index/7b387f9a1467.js create mode 100644 pr/323/docs/search.index/7b519bf60e51.js create mode 100644 pr/323/docs/search.index/7dea95897c5e.js delete mode 100644 pr/323/docs/search.index/7e247cb6de94.js delete mode 100644 pr/323/docs/search.index/7e614ec002b9.js create mode 100644 pr/323/docs/search.index/7e738badf46e.js delete mode 100644 pr/323/docs/search.index/7ebc0d37a846.js create mode 100644 pr/323/docs/search.index/7edd6678fc26.js create mode 100644 pr/323/docs/search.index/7eff5365274d.js delete mode 100644 pr/323/docs/search.index/7f51efe5046e.js delete mode 100644 pr/323/docs/search.index/7fc1930a76ba.js delete mode 100644 pr/323/docs/search.index/entry/23e89d8be7e8.js create mode 100644 pr/323/docs/search.index/entry/c07d808995d5.js delete mode 100644 pr/323/docs/search.index/function/0a18a7c168e3.js create mode 100644 pr/323/docs/search.index/function/3b5fc09aa528.js create mode 100644 pr/323/docs/search.index/generic_inverted_index/6ca95edf6c4a.js delete mode 100644 pr/323/docs/search.index/generic_inverted_index/76f3213be5b5.js create mode 100644 pr/323/docs/search.index/name/06100f36a877.js delete mode 100644 pr/323/docs/search.index/name/52ee5dc5acb9.js delete mode 100644 pr/323/docs/search.index/normalizedName/9cdde8eea858.js create mode 100644 pr/323/docs/search.index/normalizedName/d40cd004cf8f.js rename pr/323/docs/search.index/path/{3e0347ff70ce.js => f16d052caa1a.js} (97%) create mode 100644 pr/323/docs/search.index/type/21bdc4104a40.js delete mode 100644 pr/323/docs/search.index/type/ccb5a8e6883b.js diff --git a/pr/323/docs/help.html b/pr/323/docs/help.html index 2565ad385..99d8cb979 100644 --- a/pr/323/docs/help.html +++ b/pr/323/docs/help.html @@ -1 +1 @@ -Help

All

Rustdoc help

Back
\ No newline at end of file +Help

All

Rustdoc help

Back
\ No newline at end of file diff --git a/pr/323/docs/iroh_quinn/enum.ConnectionError.html b/pr/323/docs/iroh_quinn/enum.ConnectionError.html index f5f28f5ae..14c0186c2 100644 --- a/pr/323/docs/iroh_quinn/enum.ConnectionError.html +++ b/pr/323/docs/iroh_quinn/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(Error),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

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 +

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 diff --git a/pr/323/docs/iroh_quinn/enum.FrameType.html b/pr/323/docs/iroh_quinn/enum.FrameType.html index c28f19a5c..007d96e36 100644 --- a/pr/323/docs/iroh_quinn/enum.FrameType.html +++ b/pr/323/docs/iroh_quinn/enum.FrameType.html @@ -1,4 +1,4 @@ -FrameType in iroh_quinn - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
+FrameType in iroh_quinn - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
 
Show 44 variants Padding, Ping, Ack, @@ -43,10 +43,10 @@ ReachOutAtIpv4, ReachOutAtIpv6, RemoveAddress, -
}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

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

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

impl Decodable for FrameType

Source§

fn decode<B>(buf: &mut B) -> Result<FrameType, UnexpectedEnd>
where - B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

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

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

impl Encodable for FrameType

Source§

fn encode<B>(&self, buf: &mut B)
where - B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

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

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

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

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

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

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

fn try_from(value: u64) -> Result<FrameType, <FrameType as TryFrom<u64>>::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

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

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

impl Decodable for FrameType

Source§

fn decode<B>(buf: &mut B) -> Result<FrameType, UnexpectedEnd>
where + B: Buf,

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

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

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

impl Encodable for FrameType

Source§

fn encode<B>(&self, buf: &mut B)
where + B: BufMut,

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

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

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

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

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

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

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

fn try_from(value: u64) -> Result<FrameType, <FrameType as TryFrom<u64>>::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn/struct.ApplicationClose.html b/pr/323/docs/iroh_quinn/struct.ApplicationClose.html index bb5b19c66..d9be55fa4 100644 --- a/pr/323/docs/iroh_quinn/struct.ApplicationClose.html +++ b/pr/323/docs/iroh_quinn/struct.ApplicationClose.html @@ -1,11 +1,11 @@ -ApplicationClose in iroh_quinn - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+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 +

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 diff --git a/pr/323/docs/iroh_quinn/struct.ConnectionClose.html b/pr/323/docs/iroh_quinn/struct.ConnectionClose.html index 853926ce5..7e905013f 100644 --- a/pr/323/docs/iroh_quinn/struct.ConnectionClose.html +++ b/pr/323/docs/iroh_quinn/struct.ConnectionClose.html @@ -1,4 +1,4 @@ -ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+ConnectionClose in iroh_quinn - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
     pub error_code: Code,
     pub frame_type: MaybeFrame,
     pub reason: Bytes,
@@ -6,8 +6,8 @@
 

Fields§

§error_code: Code

Class of error as encoded in the specification

§frame_type: MaybeFrame

Type of frame that caused the close

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: Error) -> ConnectionClose

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

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

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

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

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

impl Eq for ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

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 diff --git a/pr/323/docs/iroh_quinn/struct.ConnectionStats.html b/pr/323/docs/iroh_quinn/struct.ConnectionStats.html index fc80eb173..1e1861283 100644 --- a/pr/323/docs/iroh_quinn/struct.ConnectionStats.html +++ b/pr/323/docs/iroh_quinn/struct.ConnectionStats.html @@ -1,4 +1,4 @@ -ConnectionStats in iroh_quinn - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { +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, @@ -8,7 +8,7 @@
§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 +

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 diff --git a/pr/323/docs/iroh_quinn/struct.FrameStats.html b/pr/323/docs/iroh_quinn/struct.FrameStats.html index a3dea813e..408d19ae8 100644 --- a/pr/323/docs/iroh_quinn/struct.FrameStats.html +++ b/pr/323/docs/iroh_quinn/struct.FrameStats.html @@ -37,7 +37,7 @@ 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 +

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 diff --git a/pr/323/docs/iroh_quinn/struct.PathStats.html b/pr/323/docs/iroh_quinn/struct.PathStats.html index 3a25ec205..2726c623f 100644 --- a/pr/323/docs/iroh_quinn/struct.PathStats.html +++ b/pr/323/docs/iroh_quinn/struct.PathStats.html @@ -1,4 +1,4 @@ -PathStats in iroh_quinn - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { +PathStats in iroh_quinn - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { pub rtt: Duration, pub cwnd: u64, pub congestion_events: u64, @@ -21,8 +21,8 @@ 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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html b/pr/323/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html index a11ef9651..672327fe0 100644 --- a/pr/323/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html +++ b/pr/323/docs/iroh_quinn_proto/coding/struct.UnexpectedEnd.html @@ -1,5 +1,5 @@ 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, +

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 diff --git a/pr/323/docs/iroh_quinn_proto/coding/trait.Decodable.html b/pr/323/docs/iroh_quinn_proto/coding/trait.Decodable.html index 0549a2287..9e6885c2d 100644 --- a/pr/323/docs/iroh_quinn_proto/coding/trait.Decodable.html +++ b/pr/323/docs/iroh_quinn_proto/coding/trait.Decodable.html @@ -3,4 +3,4 @@ fn decode<B: Buf>(buf: &mut B) -> Result<Self>; }

Expand description

Decoding of QUIC primitives.

Required Methods§

Source

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough

-

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Decodable for u8

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u16

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u32

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u64

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv4Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv6Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Implementors§

\ No newline at end of file +

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Decodable for u8

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u16

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u32

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for u64

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv4Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Source§

impl Decodable for Ipv6Addr

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Implementors§

\ No newline at end of file diff --git a/pr/323/docs/iroh_quinn_proto/coding/trait.Encodable.html b/pr/323/docs/iroh_quinn_proto/coding/trait.Encodable.html index 63c896148..acb0150ee 100644 --- a/pr/323/docs/iroh_quinn_proto/coding/trait.Encodable.html +++ b/pr/323/docs/iroh_quinn_proto/coding/trait.Encodable.html @@ -3,4 +3,4 @@ fn encode<B: BufMut>(&self, buf: &mut B); }

Expand description

Infallible encoding of QUIC primitives.

Required Methods§

Source

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.

-

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Encodable for u8

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u16

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u32

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u64

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv4Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv6Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file +

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Encodable for u8

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u16

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u32

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for u64

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv4Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Source§

impl Encodable for Ipv6Addr

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Implementors§

\ No newline at end of file diff --git a/pr/323/docs/iroh_quinn_proto/enum.ClosePathError.html b/pr/323/docs/iroh_quinn_proto/enum.ClosePathError.html index 5093aa9f8..07d051047 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.ClosePathError.html +++ b/pr/323/docs/iroh_quinn_proto/enum.ClosePathError.html @@ -1,11 +1,11 @@ -ClosePathError in iroh_quinn_proto - Rust

ClosePathError

Enum ClosePathError 

Source
pub enum ClosePathError {
+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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.ConnectionError.html b/pr/323/docs/iroh_quinn_proto/enum.ConnectionError.html index f95e6db31..dfae5e329 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.ConnectionError.html +++ b/pr/323/docs/iroh_quinn_proto/enum.ConnectionError.html @@ -1,4 +1,4 @@ -ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
+ConnectionError in iroh_quinn_proto - Rust

ConnectionError

Enum ConnectionError 

Source
pub enum ConnectionError {
     VersionMismatch,
     TransportError(TransportError),
     ConnectionClosed(ConnectionClose),
@@ -20,8 +20,8 @@ and §

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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.Dir.html b/pr/323/docs/iroh_quinn_proto/enum.Dir.html index adb1c527a..d44638051 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.Dir.html +++ b/pr/323/docs/iroh_quinn_proto/enum.Dir.html @@ -9,7 +9,7 @@ unstructured data. 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 +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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.Event.html b/pr/323/docs/iroh_quinn_proto/enum.Event.html index 1b8cf8122..142009cd1 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.Event.html +++ b/pr/323/docs/iroh_quinn_proto/enum.Event.html @@ -1,4 +1,4 @@ -Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
+Event in iroh_quinn_proto - Rust

Event

Enum Event 

Source
pub enum Event {
     HandshakeDataReady,
     Connected,
     HandshakeConfirmed,
@@ -22,7 +22,7 @@
 
§

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 +

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.

diff --git a/pr/323/docs/iroh_quinn_proto/enum.FrameType.html b/pr/323/docs/iroh_quinn_proto/enum.FrameType.html index 2a31c0268..b8bd8a4ea 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.FrameType.html +++ b/pr/323/docs/iroh_quinn_proto/enum.FrameType.html @@ -1,4 +1,4 @@ -FrameType in iroh_quinn_proto - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
+FrameType in iroh_quinn_proto - Rust

FrameType

Enum FrameType 

Source
pub enum FrameType {
 
Show 44 variants Padding, Ping, Ack, @@ -43,8 +43,8 @@ ReachOutAtIpv4, ReachOutAtIpv6, RemoveAddress, -
}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

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

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

impl Decodable for FrameType

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

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

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

impl Encodable for FrameType

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

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

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

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

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

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

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

fn try_from(value: u64) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

}

Variants§

§

Padding

§

Ping

§

Ack

§

AckEcn

§

ResetStream

§

StopSending

§

Crypto

§

NewToken

§

Stream(StreamInfo)

§

MaxData

§

MaxStreamData

§

MaxStreamsBidi

§

MaxStreamsUni

§

DataBlocked

§

StreamDataBlocked

§

StreamsBlockedBidi

§

StreamsBlockedUni

§

NewConnectionId

§

RetireConnectionId

§

PathChallenge

§

PathResponse

§

ConnectionClose

§

ApplicationClose

§

HandshakeDone

§

AckFrequency

§

ImmediateAck

§

Datagram(DatagramInfo)

§

ObservedIpv4Addr

§

ObservedIpv6Addr

§

PathAck

§

PathAckEcn

§

PathAbandon

§

PathStatusBackup

§

PathStatusAvailable

§

PathNewConnectionId

§

PathRetireConnectionId

§

MaxPathId

§

PathsBlocked

§

PathCidsBlocked

§

AddIpv4Address

§

AddIpv6Address

§

ReachOutAtIpv4

§

ReachOutAtIpv6

§

RemoveAddress

Trait Implementations§

Source§

impl Clone for FrameType

Source§

fn clone(&self) -> FrameType

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for FrameType

Source§

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

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

impl Decodable for FrameType

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for FrameType

Source§

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

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

impl Encodable for FrameType

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for FrameType

Source§

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

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

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

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

impl TryFrom<u64> for FrameType

Source§

type Error = InvalidFrameId

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

fn try_from(value: u64) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl Copy for FrameType

Source§

impl Eq for FrameType

Source§

impl StructuralPartialEq for FrameType

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn_proto/enum.LongType.html b/pr/323/docs/iroh_quinn_proto/enum.LongType.html index 8f07318d2..2bbbe2383 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.LongType.html +++ b/pr/323/docs/iroh_quinn_proto/enum.LongType.html @@ -1,11 +1,11 @@ -LongType in iroh_quinn_proto - Rust

LongType

Enum LongType 

Source
pub enum LongType {
+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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.MaybeFrame.html b/pr/323/docs/iroh_quinn_proto/enum.MaybeFrame.html index 51cdba17e..0cfd197da 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.MaybeFrame.html +++ b/pr/323/docs/iroh_quinn_proto/enum.MaybeFrame.html @@ -1,4 +1,4 @@ -MaybeFrame in iroh_quinn_proto - Rust

MaybeFrame

Enum MaybeFrame 

Source
pub enum MaybeFrame {
+MaybeFrame in iroh_quinn_proto - Rust

MaybeFrame

Enum MaybeFrame 

Source
pub enum MaybeFrame {
     None,
     Unknown(u64),
     Known(FrameType),
@@ -6,8 +6,8 @@
 

Variants§

§

None

Not attributed to any particular FrameType.

§

Unknown(u64)

Attributed to some frame type this implementation does not recognize.

§

Known(FrameType)

Attributed to a specific FrameType, never FrameType::Padding.

-

Trait Implementations§

Source§

impl Clone for MaybeFrame

Source§

fn clone(&self) -> MaybeFrame

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MaybeFrame

Source§

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

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

impl Decodable for MaybeFrame

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for MaybeFrame

Source§

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

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

impl Encodable for MaybeFrame

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for MaybeFrame

Source§

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

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

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

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

impl Copy for MaybeFrame

Source§

impl Eq for MaybeFrame

Source§

impl StructuralPartialEq for MaybeFrame

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for MaybeFrame

Source§

fn clone(&self) -> MaybeFrame

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MaybeFrame

Source§

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

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

impl Decodable for MaybeFrame

Source§

fn decode<B: Buf>(buf: &mut B) -> Result<Self>

Decode a Self from the provided buffer, if the buffer is large enough
Source§

impl Display for MaybeFrame

Source§

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

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

impl Encodable for MaybeFrame

Source§

fn encode<B: BufMut>(&self, buf: &mut B)

Append the encoding of self to the provided buffer.
Source§

impl PartialEq for MaybeFrame

Source§

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

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

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

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

impl Copy for MaybeFrame

Source§

impl Eq for MaybeFrame

Source§

impl StructuralPartialEq for MaybeFrame

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn_proto/enum.PacketDecodeError.html b/pr/323/docs/iroh_quinn_proto/enum.PacketDecodeError.html index ae013578f..94703cccf 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.PacketDecodeError.html +++ b/pr/323/docs/iroh_quinn_proto/enum.PacketDecodeError.html @@ -1,4 +1,4 @@ -PacketDecodeError in iroh_quinn_proto - Rust

PacketDecodeError

Enum PacketDecodeError 

Source
pub enum PacketDecodeError {
+PacketDecodeError in iroh_quinn_proto - Rust

PacketDecodeError

Enum PacketDecodeError 

Source
pub enum PacketDecodeError {
     UnsupportedVersion {
         src_cid: ConnectionId,
         dst_cid: ConnectionId,
@@ -11,16 +11,16 @@
 
§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 +

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,

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 + 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 +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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.PathError.html b/pr/323/docs/iroh_quinn_proto/enum.PathError.html index 850d62bf5..233640273 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.PathError.html +++ b/pr/323/docs/iroh_quinn_proto/enum.PathError.html @@ -1,4 +1,4 @@ -PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
+PathError in iroh_quinn_proto - Rust

PathError

Enum PathError 

Source
pub enum PathError {
     MultipathNotNegotiated,
     ServerSideNotAllowed,
     MaxPathIdReached,
@@ -12,8 +12,8 @@
 
§

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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.PathEvent.html b/pr/323/docs/iroh_quinn_proto/enum.PathEvent.html index a7b572f91..354ae4aca 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.PathEvent.html +++ b/pr/323/docs/iroh_quinn_proto/enum.PathEvent.html @@ -47,7 +47,7 @@ changes the status.

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

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.ProtectedHeader.html b/pr/323/docs/iroh_quinn_proto/enum.ProtectedHeader.html index 35912ec51..e68afd7cd 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.ProtectedHeader.html +++ b/pr/323/docs/iroh_quinn_proto/enum.ProtectedHeader.html @@ -1,4 +1,4 @@ -ProtectedHeader in iroh_quinn_proto - Rust

ProtectedHeader

Enum ProtectedHeader 

Source
pub enum ProtectedHeader {
+ProtectedHeader in iroh_quinn_proto - Rust

ProtectedHeader

Enum ProtectedHeader 

Source
pub enum ProtectedHeader {
     Initial(ProtectedInitialHeader),
     Long {
         ty: LongType,
@@ -40,14 +40,14 @@
 

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

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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/enum.SendDatagramError.html b/pr/323/docs/iroh_quinn_proto/enum.SendDatagramError.html index 900d93edf..a848668b9 100644 --- a/pr/323/docs/iroh_quinn_proto/enum.SendDatagramError.html +++ b/pr/323/docs/iroh_quinn_proto/enum.SendDatagramError.html @@ -1,4 +1,4 @@ -SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
+SendDatagramError in iroh_quinn_proto - Rust

SendDatagramError

Enum SendDatagramError 

Source
pub enum SendDatagramError {
     UnsupportedByPeer,
     Disabled,
     TooLarge,
@@ -10,16 +10,16 @@
 

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 +

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,

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 + 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 +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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.ApplicationClose.html b/pr/323/docs/iroh_quinn_proto/struct.ApplicationClose.html index 380fe3169..486dff3a5 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.ApplicationClose.html +++ b/pr/323/docs/iroh_quinn_proto/struct.ApplicationClose.html @@ -1,11 +1,11 @@ -ApplicationClose in iroh_quinn_proto - Rust

ApplicationClose

Struct ApplicationClose 

Source
pub struct ApplicationClose {
+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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.Connection.html b/pr/323/docs/iroh_quinn_proto/struct.Connection.html index 906ebc6fa..88d8a55f9 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.Connection.html +++ b/pr/323/docs/iroh_quinn_proto/struct.Connection.html @@ -1,4 +1,4 @@ -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

+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 @@ -33,7 +33,7 @@ increasing time. Specifically, calling 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

+

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
  • @@ -41,17 +41,17 @@ events or timeouts with different instants must not be interleaved.

  • 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

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

Source

pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent>

Return endpoint-facing events

+
Source

pub fn streams(&mut self) -> Streams<'_>

Provide control over streams

+
Source

pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_>

Provide control over streams

+
Source

pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_>

Provide control over streams

+
Source

pub fn open_path_ensure( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -65,7 +65,7 @@ remote addresses.

remote, should it not be known yet. We assume that if we already have a path to the remote, the OS is likely to use the same interface to talk to said remote.

See also open_path. Returns (path_id, true) if the path already existed. (path_id, false) if was opened.

-

Source

pub fn open_path( +

Source

pub fn open_path( &mut self, network_path: FourTuple, initial_status: PathStatus, @@ -73,7 +73,7 @@ the OS is likely to use the same interface to talk to said remote.

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

Source

pub fn close_path( &mut self, now: Instant, path_id: PathId, @@ -82,32 +82,32 @@ When the path is opened it will be reported as an
Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

+

Source

pub fn paths(&self) -> Vec<PathId>

Returns all known paths.

There is no guarantee any of these paths are open or usable.

-
Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

-
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

-
Source

pub fn set_path_status( +

Source

pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath>

Gets the local PathStatus for a known PathId

+
Source

pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath>

Returns the path’s network path represented as a 4-tuple.

+
Source

pub fn set_path_status( &mut self, path_id: PathId, status: PathStatus, ) -> Result<PathStatus, SetPathStatusError>

Sets the PathStatus for a known PathId

Returns the previous path status on success.

-
Source

pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus>

Returns the remote path status

-
Source

pub fn set_path_max_idle_timeout( +

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

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

Source

pub fn poll_transmit( &mut self, now: Instant, max_datagrams: NonZeroUsize, @@ -121,60 +121,60 @@ for a path that was never opened locally.

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

+
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

+
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

+
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

+
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

+
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

+
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

+
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

+
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

+
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

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

Source

pub fn has_0rtt(&self) -> bool

Whether 0-RTT is/was possible during the handshake

+
Source

pub fn has_pending_retransmits(&self) -> bool

Whether there are any pending retransmits

+
Source

pub fn side(&self) -> Side

Look up whether we’re the client or server of this Connection

+
Source

pub fn path_observed_address( &self, path_id: PathId, ) -> Result<Option<SocketAddr>, ClosedPath>

Get the address observed by the remote over the given path

-
Source

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

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

-
Source

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

Current state of this connection’s congestion controller, for debugging purposes

-
Source

pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt)

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

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

Source

pub fn set_max_concurrent_paths( &mut self, now: Instant, count: NonZeroU32, @@ -185,36 +185,36 @@ actively reduce paths they must be closed using 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

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

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

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

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

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

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

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

Source

pub fn initiate_nat_traversal_round( &mut self, now: Instant, ) -> Result<Vec<SocketAddr>, Error>

Initiates a new nat traversal round

@@ -224,7 +224,7 @@ initiated, the previous one is cancelled, and paths that have not been opened ar

Returns the server addresses that are now being probed. If addresses fail due to spurious errors, these might succeed later and not be returned in this set.

-

Trait Implementations§

Source§

impl Debug for Connection

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

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.

diff --git a/pr/323/docs/iroh_quinn_proto/struct.ConnectionClose.html b/pr/323/docs/iroh_quinn_proto/struct.ConnectionClose.html index 1fd411a26..94c89dd15 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.ConnectionClose.html +++ b/pr/323/docs/iroh_quinn_proto/struct.ConnectionClose.html @@ -1,4 +1,4 @@ -ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
+ConnectionClose in iroh_quinn_proto - Rust

ConnectionClose

Struct ConnectionClose 

Source
pub struct ConnectionClose {
     pub error_code: TransportErrorCode,
     pub frame_type: MaybeFrame,
     pub reason: Bytes,
@@ -6,8 +6,8 @@
 

Fields§

§error_code: TransportErrorCode

Class of error as encoded in the specification

§frame_type: MaybeFrame

Type of frame that caused the close

§reason: Bytes

Human-readable reason for the close

-

Trait Implementations§

Source§

impl Clone for ConnectionClose

Source§

fn clone(&self) -> ConnectionClose

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for ConnectionClose

Source§

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

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

impl Display for ConnectionClose

Source§

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

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

impl From<Error> for ConnectionClose

Source§

fn from(x: TransportError) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for ConnectionClose

Source§

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

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

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

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

impl Eq for ConnectionClose

Source§

impl StructuralPartialEq for ConnectionClose

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.ConnectionStats.html b/pr/323/docs/iroh_quinn_proto/struct.ConnectionStats.html index 8059b91e7..26e65f83b 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.ConnectionStats.html +++ b/pr/323/docs/iroh_quinn_proto/struct.ConnectionStats.html @@ -1,4 +1,4 @@ -ConnectionStats in iroh_quinn_proto - Rust

ConnectionStats

Struct ConnectionStats 

Source
#[non_exhaustive]
pub struct ConnectionStats { +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, @@ -8,7 +8,7 @@
§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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.Datagram.html b/pr/323/docs/iroh_quinn_proto/struct.Datagram.html index 1ffdec640..60895c32c 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.Datagram.html +++ b/pr/323/docs/iroh_quinn_proto/struct.Datagram.html @@ -1,8 +1,8 @@ -Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
+Datagram in iroh_quinn_proto - Rust

Datagram

Struct Datagram 

Source
pub struct Datagram {
     pub data: Bytes,
 }
Expand description

An unreliable datagram

Fields§

§data: Bytes

Payload

-

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Datagram

Source§

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

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

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +

Trait Implementations§

Source§

impl Clone for Datagram

Source§

fn clone(&self) -> Datagram

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for Datagram

Source§

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

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

impl Display for Datagram

Source§

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

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

impl Encodable for Datagram

Source§

fn encode<B: BufMut>(&self, out: &mut B)

Append the encoding of self to the provided buffer.

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where @@ -21,7 +21,8 @@ otherwise. Right variant of Either<Self, Self> otherwise. Read more

Source§

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

fn to_string(&self) -> String

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

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
§

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

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where diff --git a/pr/323/docs/iroh_quinn_proto/struct.DatagramInfo.html b/pr/323/docs/iroh_quinn_proto/struct.DatagramInfo.html index 4f9a0d6e9..49f57593d 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.DatagramInfo.html +++ b/pr/323/docs/iroh_quinn_proto/struct.DatagramInfo.html @@ -1,5 +1,5 @@ -DatagramInfo in iroh_quinn_proto - Rust

DatagramInfo

Struct DatagramInfo 

Source
pub struct DatagramInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for DatagramInfo

Source§

fn clone(&self) -> DatagramInfo

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for DatagramInfo

Source§

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

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

impl Display for DatagramInfo

Source§

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

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

impl PartialEq for DatagramInfo

Source§

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

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

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

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

impl Copy for DatagramInfo

Source§

impl Eq for DatagramInfo

Source§

impl StructuralPartialEq for DatagramInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +DatagramInfo in iroh_quinn_proto - Rust

DatagramInfo

Struct DatagramInfo 

Source
pub struct DatagramInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for DatagramInfo

Source§

fn clone(&self) -> DatagramInfo

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for DatagramInfo

Source§

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

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

impl Display for DatagramInfo

Source§

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

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

impl PartialEq for DatagramInfo

Source§

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

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

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

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

impl Copy for DatagramInfo

Source§

impl Eq for DatagramInfo

Source§

impl StructuralPartialEq for DatagramInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html b/pr/323/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html index 3a71a6b61..40e8449b3 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html +++ b/pr/323/docs/iroh_quinn_proto/struct.FixedLengthConnectionIdParser.html @@ -1,6 +1,6 @@ -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 +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.

diff --git a/pr/323/docs/iroh_quinn_proto/struct.FrameStats.html b/pr/323/docs/iroh_quinn_proto/struct.FrameStats.html index fcbc9fe92..dea14a19d 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.FrameStats.html +++ b/pr/323/docs/iroh_quinn_proto/struct.FrameStats.html @@ -37,7 +37,7 @@ 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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.InvalidFrameId.html b/pr/323/docs/iroh_quinn_proto/struct.InvalidFrameId.html index 80b456be4..4784b11cb 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.InvalidFrameId.html +++ b/pr/323/docs/iroh_quinn_proto/struct.InvalidFrameId.html @@ -1,6 +1,6 @@ -InvalidFrameId in iroh_quinn_proto - Rust

InvalidFrameId

Struct InvalidFrameId 

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

Encounter a frame ID that was not valid.

-

Trait Implementations§

Source§

impl Debug for InvalidFrameId

Source§

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

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

impl PartialEq for InvalidFrameId

Source§

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

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

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

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

impl Eq for InvalidFrameId

Source§

impl StructuralPartialEq for InvalidFrameId

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +InvalidFrameId in iroh_quinn_proto - Rust

InvalidFrameId

Struct InvalidFrameId 

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

Encounter a frame ID that was not valid.

+

Trait Implementations§

Source§

impl Debug for InvalidFrameId

Source§

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

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

impl PartialEq for InvalidFrameId

Source§

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

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

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

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

impl Eq for InvalidFrameId

Source§

impl StructuralPartialEq for InvalidFrameId

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
§

impl<Q, K> Equivalent<K> for Q
where diff --git a/pr/323/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html b/pr/323/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html index e277dc7bf..70fa3c85f 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html +++ b/pr/323/docs/iroh_quinn_proto/struct.MultipathNotNegotiated.html @@ -1,5 +1,5 @@ -MultipathNotNegotiated in iroh_quinn_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

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

Error when the multipath extension was not negotiated, but attempted to be used.

-

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

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

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

impl Display for MultipathNotNegotiated

Source§

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

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

impl Error for MultipathNotNegotiated

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +MultipathNotNegotiated in iroh_quinn_proto - Rust

MultipathNotNegotiated

Struct MultipathNotNegotiated 

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

Error when the multipath extension was not negotiated, but attempted to be used.

+

Trait Implementations§

Source§

impl Clone for MultipathNotNegotiated

Source§

fn clone(&self) -> MultipathNotNegotiated

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for MultipathNotNegotiated

Source§

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

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

impl Display for MultipathNotNegotiated

Source§

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

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

impl Error for MultipathNotNegotiated

1.30.0 · Source§

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

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

fn description(&self) -> &str

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

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

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

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

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

impl Copy for MultipathNotNegotiated

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn_proto/struct.PathStats.html b/pr/323/docs/iroh_quinn_proto/struct.PathStats.html index 76d16c997..f6e358156 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.PathStats.html +++ b/pr/323/docs/iroh_quinn_proto/struct.PathStats.html @@ -1,4 +1,4 @@ -PathStats in iroh_quinn_proto - Rust

PathStats

Struct PathStats 

Source
#[non_exhaustive]
pub struct PathStats { +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, @@ -21,8 +21,8 @@ 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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html b/pr/323/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html index 6db7877fc..f367ad43f 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html +++ b/pr/323/docs/iroh_quinn_proto/struct.ProtectedInitialHeader.html @@ -1,4 +1,4 @@ -ProtectedInitialHeader in iroh_quinn_proto - Rust

ProtectedInitialHeader

Struct ProtectedInitialHeader 

Source
pub struct ProtectedInitialHeader {
+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>,
@@ -10,7 +10,7 @@
 
§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 +

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 diff --git a/pr/323/docs/iroh_quinn_proto/struct.StreamInfo.html b/pr/323/docs/iroh_quinn_proto/struct.StreamInfo.html index 149c06098..6d6a3cfbb 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.StreamInfo.html +++ b/pr/323/docs/iroh_quinn_proto/struct.StreamInfo.html @@ -1,5 +1,5 @@ -StreamInfo in iroh_quinn_proto - Rust

StreamInfo

Struct StreamInfo 

Source
pub struct StreamInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for StreamInfo

Source§

fn clone(&self) -> StreamInfo

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamInfo

Source§

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

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

impl Display for StreamInfo

Source§

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

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

impl PartialEq for StreamInfo

Source§

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

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

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

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

impl Copy for StreamInfo

Source§

impl Eq for StreamInfo

Source§

impl StructuralPartialEq for StreamInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where +StreamInfo in iroh_quinn_proto - Rust

StreamInfo

Struct StreamInfo 

Source
pub struct StreamInfo(/* private fields */);

Trait Implementations§

Source§

impl Clone for StreamInfo

Source§

fn clone(&self) -> StreamInfo

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

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

Performs copy-assignment from source. Read more
Source§

impl Debug for StreamInfo

Source§

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

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

impl Display for StreamInfo

Source§

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

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

impl PartialEq for StreamInfo

Source§

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

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

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

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

impl Copy for StreamInfo

Source§

impl Eq for StreamInfo

Source§

impl StructuralPartialEq for StreamInfo

Auto Trait Implementations§

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where diff --git a/pr/323/docs/iroh_quinn_proto/struct.TransportError.html b/pr/323/docs/iroh_quinn_proto/struct.TransportError.html index 37d14cf76..a3e4c6b38 100644 --- a/pr/323/docs/iroh_quinn_proto/struct.TransportError.html +++ b/pr/323/docs/iroh_quinn_proto/struct.TransportError.html @@ -11,7 +11,7 @@
§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, +

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 diff --git a/pr/323/docs/iroh_quinn_proto/trait.ConnectionIdParser.html b/pr/323/docs/iroh_quinn_proto/trait.ConnectionIdParser.html index e3357d299..1d64febb0 100644 --- a/pr/323/docs/iroh_quinn_proto/trait.ConnectionIdParser.html +++ b/pr/323/docs/iroh_quinn_proto/trait.ConnectionIdParser.html @@ -1,9 +1,9 @@ -ConnectionIdParser in iroh_quinn_proto - Rust

ConnectionIdParser

Trait ConnectionIdParser 

Source
pub trait ConnectionIdParser {
+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 +

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/323/docs/search.index/00037727d41b.js b/pr/323/docs/search.index/00037727d41b.js deleted file mode 100644 index 8837e0256..000000000 --- a/pr/323/docs/search.index/00037727d41b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkA8+YTAAABADUAAQ0BoKAAABQqd/IaFAAAAQABgQKwgBP0AAGgYAAAFBhkczcBAIWFoEAAABPKsGATngBLApAK") \ No newline at end of file diff --git a/pr/323/docs/search.index/001277e366cd.js b/pr/323/docs/search.index/001277e366cd.js deleted file mode 100644 index 6d710e4f0..000000000 --- a/pr/323/docs/search.index/001277e366cd.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQsBoEAAABOnZfLREwAAAQABBQD0nxEAAAEAAQABAAGDArAAEJEAAbBQEvMAAWR3E4ACoKAAABNjZXYBAgD0rBAAAAEAewEBAAEAAPSgDwAAAQDoAAQBI4ICsFAS0AABZXMBAgD0ohIAAAEAiQABAAEDAPTWEgAAAQCOAAEA84UCYnUBAgD0GBEAAAEAYAEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAANMHXwhgCGEIYgjHD4AQIxOeE+YT5xPpE/QT9RMYFBwUAQwA84UTAAABAAEAAYACsAAJbQABoCAAAA0GZG4BBgD0oBIAAAEAiQABAPMDAWL0qhAAAAEAewEBAJcGAACHsEAPngABoHAAABIysJASzgABOAkDOzAAAAEAACIAEQA4BgMAmAcBAPsMAgABDQIAlw8BAOkPAQDuDwEACxAGAE4QAAB7EAEAAxMAACcTAABLEwAA3RMAAOgTAAAGFAAAFRQAAAEDAPS0EgAAAQA6AQEA84ACY213AQCAxbGAFCUAAUCBBAg=") \ No newline at end of file diff --git a/pr/323/docs/search.index/008b39f12aaf.js b/pr/323/docs/search.index/008b39f12aaf.js new file mode 100644 index 000000000..74615fac7 --- /dev/null +++ b/pr/323/docs/search.index/008b39f12aaf.js @@ -0,0 +1 @@ +rn_("AQgBoIAAABQbZgEQEwAAE4ACoOAAABPkYnABAwGwABOgAEtz9ZkPAAABAOMAAQDPAgGAArBAEkcAAaAQAAAQvHByAYEDoAAAABEusAAPhAEaoBAAAA98ZXN184ADYW9yAQIA9X8QAAABAAEAOQIBAAEEADswAAABAAAJAAEA8xAJAAGBAqCQAAATZrBQEjEAAWJ3o4CEsFATgQABsJASsgABQCADVwMAAIagcAAAE12gQAAAE8kwQQw6MAAAAQAAAAAAOwAQAAAATQVOBQAGAQYCBgMGBAYFBgYGBwYIBuEHegl7CYAJQQxCDGMMZAxlDOsM7Ax2Dm4Pbw9wD4QPog+jD+cPXRBrEGwQgxCEEIsQjBCeEMQQxRAzEYERihGLEY8ReBKoEqkSURNSE1gTgROCE54TnxO2ExgUGRQjFCQU") \ No newline at end of file diff --git a/pr/323/docs/search.index/009511e34648.js b/pr/323/docs/search.index/009511e34648.js new file mode 100644 index 000000000..1af0a51c6 --- /dev/null +++ b/pr/323/docs/search.index/009511e34648.js @@ -0,0 +1 @@ +rn_("AYACsEAS7AABoMAAABOeZW8BAgD0dxEAAAEAAQABABMAA7AgEXsAAbCAE68AAWFzdAHjDAAAAQQA868PAAABAAEAs4CEsEAS+wABQQAG") \ No newline at end of file diff --git a/pr/323/docs/search.index/0095923ba754.js b/pr/323/docs/search.index/0095923ba754.js deleted file mode 100644 index 55192160c..000000000 --- a/pr/323/docs/search.index/0095923ba754.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9KISAAABAIkAAQABAgD0oBAAAAEAnAABAAEDAPTWEgAAAQCOAAEAAQIBoEAAABLBcjswAAABAAAHAAIAnhABADcRBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAArAetB64Hiw+MDy0QLhBMEE0Q3xDgEI4RjxGgEqESrRKuEr0S5xLoEioTKxM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/00f09416a63d.js b/pr/323/docs/search.index/00f09416a63d.js deleted file mode 100644 index 7215dbc7c..000000000 --- a/pr/323/docs/search.index/00f09416a63d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9YQSAAABAN8AdgABABOAArAwEq8BNGlwAQYA8yQTAAABAAEA8wICYnMB7Q8AAPMBAWbyyAcAAB4JAQIA9NYSAAABAI4AAQAzgAOwQBKkAAFpdXk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/00f55497d47e.js b/pr/323/docs/search.index/00f55497d47e.js deleted file mode 100644 index 461b51cd7..000000000 --- a/pr/323/docs/search.index/00f55497d47e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAAHwAAgDUBwAAwQl7AAGEArBwE6UAAaBAAAAQumlzAQcA85MQAAABAA8BI4ACoLAAABJeYXQBAAOwMAl0AAGwMBMPAAGgIAAAB9VhZG8B6gEAADcDAICGoMAAABOdsAATdAABAJgV") \ No newline at end of file diff --git a/pr/323/docs/search.index/019bd84d4b2c.js b/pr/323/docs/search.index/019bd84d4b2c.js new file mode 100644 index 000000000..b89e65a0c --- /dev/null +++ b/pr/323/docs/search.index/019bd84d4b2c.js @@ -0,0 +1 @@ +rn_("AYQCsCARKQEKoJAAABOYbXABgAKgMAAAELSgUAAAE0FjcpcAAACFsCATEQABsMATxwABoGAAABGAEEAOOzAAAAEAABIABgBTBQgAfhEBAIESAQBeEwEArxMBAB8UAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/024f62593f95.js b/pr/323/docs/search.index/024f62593f95.js new file mode 100644 index 000000000..a269510aa --- /dev/null +++ b/pr/323/docs/search.index/024f62593f95.js @@ -0,0 +1 @@ +rn_("AQACoDAAABDAoJAAABMJMGj1oBEAAHMBAQABAAEAE4ACoDAAAA+BZHIjAISwEAlLAv+wIAwdAPegMAAAD6USABj1EgYAAC8AAQABAAEAAQIA9BgUAAABAAoAAQABAgDzHRAAAAEAAQDzgAJpcgEBArBAEQYAAaBQAAAS82Vz9ScMAABKAqsCAQBgAgELAPYYEwAAAQCfAFMAFgABAPOAAmx004CEoJAAABLNAUQE") \ No newline at end of file diff --git a/pr/323/docs/search.index/024f75b34b20.js b/pr/323/docs/search.index/024f75b34b20.js deleted file mode 100644 index fc0587fc7..000000000 --- a/pr/323/docs/search.index/024f75b34b20.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9AkNAAABAOsCAQABAAD0cQwAAAEAAQBlAAECADowAAABAAAAAAAJABAAAACOD48PeBA2EXcSeBJiE+ETERQSFMcBAACGsDAJbwABoEAAABGVsIARfAABGEAHOjAAAAEAAAAAABcAEAAAAMoHywdXCHsJqgmrCQgNew98D5sPPhBFEEYQRxBIEIMQhBC7ELwQIBJgE2ETixOMEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0259b3ee8225.js b/pr/323/docs/search.index/0259b3ee8225.js new file mode 100644 index 000000000..4c126e0cb --- /dev/null +++ b/pr/323/docs/search.index/0259b3ee8225.js @@ -0,0 +1 @@ +rn_("AQQA9FUQAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAAlw+YD3sQfBAIEZQRlRGWEpcSERQSFPMBAXnyfxMAAAEAAQADoKAAABPEoCAAABMIsDARigABZWl08n0SAAABAAEDADswAAABAAAGAAEACREGAAEIAPRvEwAAAQB+AAUAEwEDsJATzgABsFASvgFOZ2xwOzAAAAEAABIACQAXEAIAjREBAK0SAQDOEgEA/xIBAGATAQCUEwEAoxMBAOATAQABgAKwMBGGAAGwMBGIAAFydgECAbAAEkMAAWQ7MAAAAQAABQACAIIRAwBtEgEAI4ECoJAAABPDaW8BBwD03xMAAAsAHgAPAHcWAAGJsFATfwABsFASRQD9sGASvAABtQkDOjAAAAEAAAAAAA0AEAAAAOQM5QzmDHcOeA50EHUQsBDTEDQRNRE2ETcRtBJDgAOgIAAADNqgEAAADONtbm8BAwD0FBAAAAEAAQARAQGAAqCwAAATlrAwEu4AAW50QwADoCAAABC/sFARkgABZWhpOzAAAAEAAAYAAwApDAAAoREDAEUTAQDzgAJkcocLAAAKgAHLABABgAHOABABoFAAABExoFAAABPJoDAAABBBoDAAABBMMDFjZWlvc3R1d/X8BwAAAQB+B7ICRAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0424910c4c21.js b/pr/323/docs/search.index/0424910c4c21.js new file mode 100644 index 000000000..0da170de0 --- /dev/null +++ b/pr/323/docs/search.index/0424910c4c21.js @@ -0,0 +1 @@ +rn_("AQABoDAAABEudPRrEAAAAQAXAAEAAYACoAAAABA9oAAAABA+NDbzAwF2AQcNAAAXAQAAh7CgEp4AAbAgDDoAAbBgEpoAAbCAEoEAAaAgAAAQpAlERjowAAABAAAAAAAJABAAAABvB3AHcQdIDKgPBBE2EjcSOBLJEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/059aa106437a.js b/pr/323/docs/search.index/059aa106437a.js new file mode 100644 index 000000000..ed812088e --- /dev/null +++ b/pr/323/docs/search.index/059aa106437a.js @@ -0,0 +1 @@ +rn_("AYICsEATzgABsGAStgABZXIBBQD0RxAAAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAAJAPkQ84EXkSehJkE+MTExQUFAEAAaFAAAAUDGf03QcAAAEAAQABAMMAhKCwAAATTbBAEF4AARQgBPVZBwAAfgUnA0cC/QBjgAOwMA/KAAFhZWk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/05c72ebeb6e8.js b/pr/323/docs/search.index/05c72ebeb6e8.js deleted file mode 100644 index 75f1b10fb..000000000 --- a/pr/323/docs/search.index/05c72ebeb6e8.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABoDAAAAzVaQHyEgAAAQABsDAQ7QABbvK6BwAAAQABAQDzNQgAAC4HAQATAAKgUAAAEE5lc/ULBAAAAQABAAEAAQABhAKwoBOLAAGwoBONAAFjcvMBAXTyihEAANUAAQMBsGAT0wABZjswAAABAAAGAAMAIAwCAP8SAQCDEwEA84ACYWXzgIQAKAU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/05cf24e46b23.js b/pr/323/docs/search.index/05cf24e46b23.js deleted file mode 100644 index e63c08949..000000000 --- a/pr/323/docs/search.index/05cf24e46b23.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAOIAAgDnDAEAdw7gAAEBAaAAAAASyHP1TgUAADMKuAMBAAIAAYACoCAAAA92oDAAABJ1ZXITAQKgIAAAEU9wdQFiDwAAAQECoGAAABM/oOAAABOWZXQ7MAAAAQAACAABAFIFCAABgAOwIBIhAAGwsBJYAAGwsBJaAAFiY3PzgMUAAQoK") \ No newline at end of file diff --git a/pr/323/docs/search.index/05e417058650.js b/pr/323/docs/search.index/05e417058650.js deleted file mode 100644 index 4a3d3a6b8..000000000 --- a/pr/323/docs/search.index/05e417058650.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAAHwAAgCZEAAApBF7AAEAAbBwEz0AAWXykQ8AAAEA8wABc/T6DwAAAQAFBAEAM4ADoGAAABFOZGlv") \ No newline at end of file diff --git a/pr/323/docs/search.index/068aba71b8f6.js b/pr/323/docs/search.index/068aba71b8f6.js new file mode 100644 index 000000000..d6c8eea26 --- /dev/null +++ b/pr/323/docs/search.index/068aba71b8f6.js @@ -0,0 +1 @@ +rn_("AQQA9g8NAAABAOkCAQAkAQEAAQoA814SAAABAJoAAQEA93MJAAABALUGAQBrAAEADwEjgAKwYBNiAAFpbwECAPTRDAAAAQDIBQEAAQUA9HgTAAABAEYAAQD3BAAAh6AgAAAM07CQEZgAAQScAzowAAABAAAAAAAIABAAAAALBmMJZAl1DHYMpRCmEI0SjhI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/06a55ef026d4.js b/pr/323/docs/search.index/06a55ef026d4.js deleted file mode 100644 index c595ee0f1..000000000 --- a/pr/323/docs/search.index/06a55ef026d4.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA9eEQAAABAAEAAQABACOBArCQE3gAAWF0Q4ADsRAT7QABoIAAABM7Y290EwACsAATxQABbnL24AEAAAEAVwIBAGEDbAs=") \ No newline at end of file diff --git a/pr/323/docs/search.index/06cdfcefc624.js b/pr/323/docs/search.index/06cdfcefc624.js new file mode 100644 index 000000000..e99c1f79e --- /dev/null +++ b/pr/323/docs/search.index/06cdfcefc624.js @@ -0,0 +1 @@ +rn_("AQoA9BoTAAABAA8AAQABgwKgIAAAD/ugMAAAEIxpdAEEAbAAEpQAAWT0PhIAAAEAAQABACOGhLCgE8EAAbBAEpIAAbBwE3IAARWAAAEAArAwD+0AAbAQEzIAAXN08g0GAAAiBgGAArDAE+wAAaAAAAAHs3R584CFEcEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/0773c651efc1.js b/pr/323/docs/search.index/0773c651efc1.js deleted file mode 100644 index 3c2f369c1..000000000 --- a/pr/323/docs/search.index/0773c651efc1.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsHASIwABsCAUEwABaW8BAAGw8BQTAAFr9YoTAAAFAAEAAQAkAPMFAWTyYBIAAAEAAQIA830QAAABAAEA8wABaTowAAABAAAAAAAHABAAAABbEHYSTxNQE38TgBOcE50TI4MCsLAT8gABYXMBgQKgoAAAEsKgQAAAECBvdvOAhAFQAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/082c33a78589.js b/pr/323/docs/search.index/082c33a78589.js deleted file mode 100644 index af46271c3..000000000 --- a/pr/323/docs/search.index/082c33a78589.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsFATiAABoAAAAAlcY3QBAQA7MAAAAQAABQABAAgTBQABAwD08wwAAEwGlgABAAGAAqAQAAAMhKBQAAASLWNkAQIA81EJAAAPAwEA8wABbfMvBQAAtAABAAGBAqAQAAASwqBwAAATA2Fj8wABcvO8BwAAkgkZAGcPAICIsDAPgwDGoDAAABP8KTAj") \ No newline at end of file diff --git a/pr/323/docs/search.index/0842ec51c54a.js b/pr/323/docs/search.index/0842ec51c54a.js deleted file mode 100644 index fdbed2683..000000000 --- a/pr/323/docs/search.index/0842ec51c54a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYAOzAAAAEAAAkAAQDxEAkAAQIAOzAAAAEAABMAAQDRDxMAE4GEoFAAABCyoBAAAAw7oCAAAAz0HEAAE4ACsFAS+QABYW4zgAOwMA97AAFpc3Q=") \ No newline at end of file diff --git a/pr/323/docs/search.index/08695ab5ffcf.js b/pr/323/docs/search.index/08695ab5ffcf.js new file mode 100644 index 000000000..9388920da --- /dev/null +++ b/pr/323/docs/search.index/08695ab5ffcf.js @@ -0,0 +1 @@ +rn_("AQEA9VkIAAABAAEAAQABAAEAAqAwAAAQwKCQAAATCTBo9aARAABzAQEAAQABABOAAqAwAAAPgWRyIwKEsBAJSwL/sCAMHQD3oDAAAA+lEgAY9RIGAAAvAAEAAQABADOAhLCgEloAAbBAD4sAARBJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/08b434a15736.js b/pr/323/docs/search.index/08b434a15736.js deleted file mode 100644 index 7f551bd42..000000000 --- a/pr/323/docs/search.index/08b434a15736.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCsQAUHwABsNAUEQABa20BBAGgoAAAFCp38hoUAAABAAELAPOPEwAAAQABACOAAqCAAAATtWVvYwQDoGAAABNOZW5wATgMAAABCgDzchMAAAEAbQATgAKiEAAAFCpsb3cAAACFoGAAABIgoQAAABPiBIEDOzAAAAEAADsABAAvBgUAsQcAAAEIMwAoEQAAQ4ADsIARJwEKoAAAAAHrZGt0") \ No newline at end of file diff --git a/pr/323/docs/search.index/094547135a51.js b/pr/323/docs/search.index/094547135a51.js new file mode 100644 index 000000000..ab4a6ed74 --- /dev/null +++ b/pr/323/docs/search.index/094547135a51.js @@ -0,0 +1 @@ +rn_("AQABoCAAAAzwbQH8BQAAAQQBsAASlAABZPQ+EgAAAQABAAEAI4SEsKATwQABsEASkgABsHATcgABFYAAM4ADsNATrwABYWVp") \ No newline at end of file diff --git a/pr/323/docs/search.index/09751f9ed1f1.js b/pr/323/docs/search.index/09751f9ed1f1.js new file mode 100644 index 000000000..b5bb167e7 --- /dev/null +++ b/pr/323/docs/search.index/09751f9ed1f1.js @@ -0,0 +1 @@ +rn_("AYACsHASYgABoLAAABOMZW8jAYSwcBGeAAGgYAAAEv6AEvcAEOwAMAM7MAAAAQAAGgAGAM0HAQBFCQUADQ0BAPMQCQAyEQAA3BIFAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/09d1f93a766a.js b/pr/323/docs/search.index/09d1f93a766a.js new file mode 100644 index 000000000..513a8ee2e --- /dev/null +++ b/pr/323/docs/search.index/09d1f93a766a.js @@ -0,0 +1 @@ +rn_("AQMBgBL3ABDsczswAAABAAAFAAEA3BIFACOCAqCQAAATzWlzBwEAhMWwUBKmAAGgoAAAEz6wcBMGAAGg0AAAE9mAARgI") \ No newline at end of file diff --git a/pr/323/docs/search.index/09f0b7b1cb10.js b/pr/323/docs/search.index/09f0b7b1cb10.js deleted file mode 100644 index 5c6f17131..000000000 --- a/pr/323/docs/search.index/09f0b7b1cb10.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQ0A9VETAAABAAEAcAABAAGAAqEQAAATirBwE58AAW5yAQcA9FsRAAABAAEAAQAzAASgIAAACXuw4BN/AAExZWly9BAEAACgA6wBIAMBAgD0rBAAAAEAewEBAAEDAPQYEQAAAQBgAQEA8wACYnUBxw8AAAEDAPSqEAAAAQB7AQEA84QCZHMBAgDzXgkAAAEAVADzgAJpdAEYAPMnFAAAAQABAAGAArCwE08AAaDAAAAS/GV184ECY20BDQGwQBPPAAFl9ZcTAAABAAEAAQABABOCArAAEMIAAWtzAQQA9FMQAAABAAEAAQABgQKgIAAAEL2wQBLsAAFlafOCAmV0AQQBoGAAABJOcvQ2DAAAAQCOAwEA84ADYWVvAQkA9ncSAAABAOoAfwAwAAEAE4ACsGAQawABZG4BBgD1lRAAAAEAAQABAGkCAQMBoHAAABKqZ/S9BwAAAQDzAQEAAQkA8+YTAAABADUAAYECsIAT9AABoGAAABQYZHMXAQCIhbDQFBoAAaBAAAATyrBgE54ASwKQCgEEAPQ9EwAAAQBKAAEAU4ADsJAUEwABZWhvAQABoEAAABElZQF5DwAAI4ECsFAQzwABa3R3/AEAjaGQAAAUCrCwErgAAaCgAAASdv2BDzswAAABAAAZAAgAcQAAAHcAAwDpBQEAmwcDAIYJAwBUDAcA/w8BACUSAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0a5ddaea07dc.js b/pr/323/docs/search.index/0a5ddaea07dc.js new file mode 100644 index 000000000..7cc72d94a --- /dev/null +++ b/pr/323/docs/search.index/0a5ddaea07dc.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABDLoDAAABEzaXQBAQA7MAAAAQAAfAACANUHAADCCXsAYwADsAAB7gABZGVsOjAAAAEAAAAAAAcAEAAAAEAF/QX+Bf8F2QfpEBETEhM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0a68956b7003.js b/pr/323/docs/search.index/0a68956b7003.js new file mode 100644 index 000000000..957ad6d24 --- /dev/null +++ b/pr/323/docs/search.index/0a68956b7003.js @@ -0,0 +1 @@ +rn_("AYACsHARHAABsCATxwABYWQBBQD0xxIAAAEAjgABAPMAAWMBPRIAACOAArCgE60AAXN5AQYA9LoQAAABALoAAQCTgISgAAAAEQOxIBQJAAEJIAHzgAJlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0adb0f113264.js b/pr/323/docs/search.index/0adb0f113264.js deleted file mode 100644 index 9194f6887..000000000 --- a/pr/323/docs/search.index/0adb0f113264.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAAAkABACqCQEARRADAIMQAQBgEwEAAQIBsEAT5AABdDswAAABAAANAAYAghIDAOUSAQBMEwEAYxMBANoTAQD+EwEAE4ACsDASrwE0aXABiAKwABNXAAGwABNZAAE0NhOBAqAwAAARKGRzAYACoCAAAAyEsGATiAABbngBBQDzJBMAAAEAAQATgAKgMAAAE3plefMAhBIAA/LtDwAA2wDzgAJydA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0adc4fcce01e.js b/pr/323/docs/search.index/0adc4fcce01e.js new file mode 100644 index 000000000..05cb176fd --- /dev/null +++ b/pr/323/docs/search.index/0adc4fcce01e.js @@ -0,0 +1 @@ +rn_("AQQBsAASqAABczswAAABAAALAAIAHBMHAAQUAwBHAAAFhqCAAAAS/bEAFBEAAbBAEmsAAbCAFB8AAbBQEqoAARUAFvRyDgAAAQABAAEAQwCEoCAAAAl/oNAAABMIoDAAAAleFQAE89YBAAABAP8F") \ No newline at end of file diff --git a/pr/323/docs/search.index/0af4e11d16e4.js b/pr/323/docs/search.index/0af4e11d16e4.js new file mode 100644 index 000000000..2f205d36d --- /dev/null +++ b/pr/323/docs/search.index/0af4e11d16e4.js @@ -0,0 +1 @@ +rn_("AQABsIATqwABcPKnEwAAAQBDBQOgIAAAEQWgQAAAEkJkZXPzqQ8AABwDAQABAgA7MAAAAQAAEwABANMPEwATgYSgUAAAELSgEAAADDygIAAADPYcQAAzgAOwUBL7AAFhZm4=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0b049d31f2d1.js b/pr/323/docs/search.index/0b049d31f2d1.js deleted file mode 100644 index 4eb697192..000000000 --- a/pr/323/docs/search.index/0b049d31f2d1.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQADsCAQ7wABoFAAABGUoEAAABOobHN28qMTAAABAAECAPR1EQAAAQABAAEAE4QCsCAReQABYXPzgAJhZROAAmzK071KfXJzAQEAOzAAAAEAAAYAAQAjFAYA8wYBZfTYEwAAAQAuAAEAEwcDsEAUAAABoGAAABOBYXJ1ASUQAAAjgAKgEAAADC1lcgEAADswAAABAAAeAA4ATAUBAP8FCAB5CQEAQAwBAOkMAQB0DgAAbA8CAIIPAACgDwEAiRABAJwQAAAxEQAAiBEBAI0RAAABAQA6MAAAAQAAAAAACgAQAAAAZQxmDDIQMxDEEMUQxhBtE24T7BPxEwGBArAAESEAAbAAESMAATQ2AQUA9HYTAAABAEYAAQDzBAFj8osSAAABAAEDAaBAAAATp2Xy0RMAAAEAAQQA85ASAAABAEYBAQQBsAASkgABZPQ8EgAAAQABAAEAZxIAAYmwoBO/AAGgUAAAEl+gMAAAEMqwYBPyAAGwcBNwAAG1yAQ6MAAAAQAAAAAACgAQAAAAVAmCCW8McAytD64Prw9+EckSyhKVExGBAhCwIBF5AAFhcwECAPNVEgAAAQABAPcAAIDFoBAAABBLgAA4CAEFAPRTEgAAAQCaAAEAAQgA9P8SAAABAIMAAQAzAQOwQBLUAAFhYmX19wwAAAEA7gJIAsMAAQAA9UAMAAABACsDAQABAPOCAnN1AQYA9C0QAAABALEAAQAzAAOgoAAAFCpidHf18QUAAAEA2A1QAAEAAQ8A9JITAAABAEsAAQABBAD0KxAAAAEAsQABACOAArEAE+8AAW9w84ECc3QBBAGgoAAAFCp38hoUAAABAAELAPOPEwAAAQABAAEMAPT9EgAAAQCjAAEAI4ACoHAAABO1Z3fzgAJlbwENAPPhEwAAMAABAAEQAPMJFAAAFgABAAEBAaBgAAASTnL3LQYAAAEAJwLhAwEAjgMBAAEDAaAAAAAP5XPzPQwAAAEAAQABAwDzPAwAAMgEAQABgAKgQAAAE06wMBJ9AAFhc/OAA25ydgEJAPSOEQAAAQAeAQEAAQcA9LMQAAABAKsAAQDzgAJoaQEBArAgDPUAAbAADHkAAWVz8sUHAAABAAEFAPSTDwAAAQDiAAEA84ACYmPX7wAAjaAgAAAHx6DgAAATgrDQE3QAARf1CzowAAABAAAAAAAaABAAAADYAdkB2gEUBBUEZQkmDDgMdwx4DHsM3AzdDN4M3wxvDiQQcBAaERsRYRFiEXsTvxPAE9MT1BMBgAKgsAAAE9ygMAAAEMdsbhMAArCAE6kAAWVw84ETAAAkAAEAAQMBsEATzwABZfWXEwAAAQABAAEAAQABBgD0MhMAAAEAAQABAAEPAPQLFAAAAQABAAEAAQIAOzAAAAEAAAUAAQD2EwUA8w0BbTswAAABAAAFAAEAtxMFAAEEAPVAEgAADAABAIMAAQABAAKgEAAAEQOw8BQTAAFla/eKEwAABQABAAEAJAATAAEAAQIA9FcQAAABAAEAAQABAgD0IRAAAAEAqQABAPeTAQCNoFAAABC2oQAAABP8sQAUAAABoKAAABLLvbwDOzAAAAEAACIADAAWDAMApw8AAM8PAQDVEAcALBEAAGASAQBpEgEAbRIBAMMSAQDgEgQArxMEABkUAAABAgA7MAAAAQAACwACAEUTBQD2EwUA8wEBbTswAAABAAANAAQA1wwAABARAABvEgUAtxMFABMBAqAwAAAM22Vz8pwRAAABAAGJArAgE9oAAaAwAAAT42lwEwADoCAAABKqoIAAABK9ZWl19SoMAAAyAAEAAQABAAGGArCQE/4AAaAAAAASRGJkAYACsDASngABoDAAABCacnbXAAADhaAwAAAQGKAwAAAS8hABEzowAAABAAAAAAAJABAAAABOCU8JUAmACYEJLxA0EOsQ7BCBEgEGADowAAABAAAAAAAHABAAAACLD4wPTBBNEKASoRIqEysTAQcA9BETAAABAAEAAQB3AAAAhqBwAAATJ6BAAAASwaBwAAAQyENABTswAAABAAAQAAcA7wEAAL0HAQCxCQEAnhABAKoQAQA3EQUAJhIBAHd3AICLsLAT6gABoGAAABDItUEP") \ No newline at end of file diff --git a/pr/323/docs/search.index/0b8810d9f049.js b/pr/323/docs/search.index/0b8810d9f049.js deleted file mode 100644 index 23997d348..000000000 --- a/pr/323/docs/search.index/0b8810d9f049.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBoEAAABPOZfLtEwAAAQABgAOgMAAAEiyw4BOtAAGwkBPvAAFhY22HAQCAhrAQEM8AAbAQEO8AAaCgAAAT3KDwAAAT/DFJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0bfb65fd762c.js b/pr/323/docs/search.index/0bfb65fd762c.js deleted file mode 100644 index a9bf99a9a..000000000 --- a/pr/323/docs/search.index/0bfb65fd762c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsGATfwABZQG0EwAAAQYA9LQSAAABADoBAQABBAD0zhIAAAEA/QABAPMAA2VyczowAAABAAAAAAAJABAAAAB2CXcJeAnoECcRIxIkEjESeBN5Ew==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0c4100bc39d6.js b/pr/323/docs/search.index/0c4100bc39d6.js deleted file mode 100644 index 2a7ad97e5..000000000 --- a/pr/323/docs/search.index/0c4100bc39d6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsEAS6gABoMAAABOcZW8BAgD0dREAAAEAAQABABMAA7AgEXkAAbCAE60AAWFzdAHhDAAAAQQA860PAAABAAEAs4CEsEAS+QABQQAG") \ No newline at end of file diff --git a/pr/323/docs/search.index/0c9fbf07a2ce.js b/pr/323/docs/search.index/0c9fbf07a2ce.js new file mode 100644 index 000000000..8cda75e41 --- /dev/null +++ b/pr/323/docs/search.index/0c9fbf07a2ce.js @@ -0,0 +1 @@ +rn_("AQUA9HgTAAABAEYAAQDzBAFj8o0SAAABACOCAqBQAAASYWZ0AQABsAATygABZPQXDAAAAQABAAEA84ACZWkTgAKw4BPVAAF0dg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0cad63531c89.js b/pr/323/docs/search.index/0cad63531c89.js deleted file mode 100644 index 73c79daf9..000000000 --- a/pr/323/docs/search.index/0cad63531c89.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAUAAQAIEwUAAQAA9YcMAAABAAEAAQABAPOAAmdrE4ACsLATYAABYW8BgAKgIAAADBWwYBD7AAFlaQGFArBwE4gAAbAwEuUAAWVzEwADsEAPhwABoGAAABEwYmVzOzAAAAEAABIABAAgBgEALwcKAP0PAQBPEAMAAQMA9BkQAAABAAQAAQABCwGgQAAAE85l8u0TAAABAPOAAmRy8wACZW87MAAAAQAABwACAP0HAwA+CAMAAQIAOzAAAAEAAAkAAQALDAkA84ACb3QBAwA7MAAAAQAA3gACANcHAAAtC90AAQIBoHAAABGbaTswAAABAADwAAIA1gcAAD0K7wATgAKgQAAADEVydQEEAPTFEgAAAQCOAAEAAQMA81wSAAABAJoA84ACYW/XBwAAiLBADP8AAbEwE+oAATSBFjswAAABAAAeAA0ARwUBAPkFAQAdDAAASgwHAG0MAQCVDwEAeRABAAYRAABVEQMAkhEBADsSAACUEgEADxQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0cc8efee7221.js b/pr/323/docs/search.index/0cc8efee7221.js new file mode 100644 index 000000000..eec7dfff1 --- /dev/null +++ b/pr/323/docs/search.index/0cc8efee7221.js @@ -0,0 +1 @@ +rn_("AQEBsFAPiwABY/QsBQAAAQABALUAAQABoIAAABQbZgEQEwAA8wYBc/IJDQAAOAQzAAOgYAAAE/9jaXPyexEAAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/0dbe969ddebd.js b/pr/323/docs/search.index/0dbe969ddebd.js deleted file mode 100644 index 4465f2500..000000000 --- a/pr/323/docs/search.index/0dbe969ddebd.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkA8+YTAAABADUAAQ0BoKAAABQqd/IaFAAAAQABgQKwgBP0AAGgYAAAFBhkczcBAIaFoEAAABPKsGATngBLApAK") \ No newline at end of file diff --git a/pr/323/docs/search.index/0e34ce7f7192.js b/pr/323/docs/search.index/0e34ce7f7192.js deleted file mode 100644 index 89cc8858c..000000000 --- a/pr/323/docs/search.index/0e34ce7f7192.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQAOjAAAAEAAAAAAAcAEAAAAIsPjA9MEE0QoBKhEioTKxMBAAD0UgkAAAEA+wncABcAAACHoEAAABC3sGAT0wABoBAAAAySsFATdAABsDARGgABsSAT8gABOMECOjAAAAEAAAAAAA4AEAAAAGcJaAl8CX0JIAwhDCIMKwwsDOAMLBL/EgATgxOEEzOAA6CAAAATe2VveQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/0f0fa73a3135.js b/pr/323/docs/search.index/0f0fa73a3135.js new file mode 100644 index 000000000..26124c5c4 --- /dev/null +++ b/pr/323/docs/search.index/0f0fa73a3135.js @@ -0,0 +1 @@ +rn_("AQAA9K4QAAABAHsBAQAjgQKgYAAAEr9iaQEAAPSiDwAAAQDoAAQBI4ECsFAS0gABZXMTgAKwAAe0AdxhcgGJArAgE9wAAaAwAAAT5WlwAQIA9KQSAAABAIkAAQABAwD02BIAAAEAjgABAPOFAmJ1AQIA9BoRAAABAGABAQDzgAJpbLcBAACFoCAAABKsEgEMOjAAAAEAAAAAABYAEAAAANQHYAhhCGIIYwgrDF0MXgxfDGAMyQ+CEO4S7xIlE6AT6BPpE+sT9hP3ExoUHhQ=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0f103fffad43.js b/pr/323/docs/search.index/0f103fffad43.js deleted file mode 100644 index bbfdee40a..000000000 --- a/pr/323/docs/search.index/0f103fffad43.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAAN4AAgAlDAAAFQ3dAPMAAW07MAAAAQAA3gACABcGAABEBt0AAQMAOzAAAAEAAAcAAgBYDwUA2BIBAPMAAWM7MAAAAQAAVwAmADAFCwDlBQEAYAkBADwMAABCDAIA8wwAAP4MAgAQDQEAfw8AAIQPAQCHDwEAjQ8AABIQAgAZEAEAHhABAD0QAACxEAAAtxAAAAQRAQAREQEAJREBACkRAgCEEQEAlRECAJsRAABAEgAARxIBAEwSAQCQEgEAnBIDAL4SAQDOEgMA1BIBAC4TAQA/EwAApxMAAMwTBADVEwIA84ACcncBCQDz5hMAAAEANQABDAGgoAAAFCp38hoUAAABAAEBAPOoDwAAAQABAPOAAnR1AYECsIAT9AABoGAAABQYZHM3AgCBhqBAAAATyrBgE54AS7AwE4sAAQKQDgGIArAAE1cAAbAAE1kAATQ2M4ADoDAAABEoZG5zYwADoBAAAAVQYWVvAXYAAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0f1e1d1cc48f.js b/pr/323/docs/search.index/0f1e1d1cc48f.js deleted file mode 100644 index cfb291d06..000000000 --- a/pr/323/docs/search.index/0f1e1d1cc48f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMA9gQNAACWAlkAAQCmAgEAAQQAOzAAAAEAAAoABAA1EAUAWhIBALASAQDCEwAAAYYCoAAAABLSoAAAABLTZHLzAgF2AfgSAADzgAJlbycBAADHoDAAAA+ZoHAAABItoIAAABOVoNAAABM7oLAAABOVQEUICTswAAABAAAJAAMANQYCAIQHBABrDAEAI4ACaXe0yZZoZHIBAAA6MAAAAQAAAAAACgAQAAAAnwmgCc4P6hLrEhYTFxO2EwkUHxQgFBOAAqAwAAAQS2x084ACYWU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/0f434453b623.js b/pr/323/docs/search.index/0f434453b623.js deleted file mode 100644 index 5ab61309b..000000000 --- a/pr/323/docs/search.index/0f434453b623.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9sIQAAABAFMDAQAKAAEAAQAAOzAAAAEAAAYAAgACEwAAZxMFACOIArBAE9UAAWVzAQ0BsEATzwABZfWXEwAAAQABAAEAAQABBQA7MAAAAQAACAADAJgRAgCGEgQAlhMAANcBAACFa9tI9uj+FIQC89IMAAABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/0f835c2828ff.js b/pr/323/docs/search.index/0f835c2828ff.js deleted file mode 100644 index fa7063a06..000000000 --- a/pr/323/docs/search.index/0f835c2828ff.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAAAUAAQD2EwUA8w0BbTswAAABAAAFAAEAtxMFACMDhLBAEkwAAaEAAAAT/KAAAAATgTAIAjswAAABAAAUAAUA1RAHAG0SAQDgEgQArxMEABkUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1017c7914851.js b/pr/323/docs/search.index/1017c7914851.js deleted file mode 100644 index 98c62887e..000000000 --- a/pr/323/docs/search.index/1017c7914851.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAEoAAAABA/sCAQ7wABoFAAABGUoEAAABOoZGxzdjswAAABAAATAAYAIwYAAEUMAADrDAAAtw8NABMRAACjEwEAAQIA9HURAAABAAEAAQAThAKwIBF5AAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/323/docs/search.index/106c7a38daa5.js b/pr/323/docs/search.index/106c7a38daa5.js new file mode 100644 index 000000000..61514def8 --- /dev/null +++ b/pr/323/docs/search.index/106c7a38daa5.js @@ -0,0 +1 @@ +rn_("AQYA9DQTAAABAAEAAQABDwD0DRQAAAEAAQABAPOHAmhtAQQBsAASlAABZPQ+EgAAAQABAAEAI4eEsKATwQABsEASkgABsHATcgABFYAAAQABoEAAABA/ZTswAAABAAAGAAIAzwcEAF4IAQATgAKwUA/8AAFkcgEJAPVTEwAAAQABAHAAAQC3AgCAhrAgEkcAAQnR+Tp2ahFJAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/10c44e4b991a.js b/pr/323/docs/search.index/10c44e4b991a.js new file mode 100644 index 000000000..1b0d6b750 --- /dev/null +++ b/pr/323/docs/search.index/10c44e4b991a.js @@ -0,0 +1 @@ +rn_("AQUA9FUSAAABAJoAAQABCAD0ARMAAAEAgwABADMBA7BAEtYAAWFiZfX5DAAAAQDuAkgCwwABAAD1QQwAAAEALAMBAAEA84ACc3UjgQKwgBL7AAFydAGAArBwEo8AAaAQAAAM3W56AQYA9C8QAAABALEAAQBzAISgoAAAFCwCART18gUAAAEA2Q1QAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1119c1193e11.js b/pr/323/docs/search.index/1119c1193e11.js deleted file mode 100644 index 0801cce6b..000000000 --- a/pr/323/docs/search.index/1119c1193e11.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9KwQAAABAHsBAQAjgQKgYAAAEr1iaQEAAPSgDwAAAQDoAAQBI4ICsFAS0AABZXMBiQKwIBPaAAGgMAAAE+NpcAECAPSiEgAAAQCJAAEAAQMA9NYSAAABAI4AAQDzhQJidQECAPQYEQAAAQBgAQEA84ACaWy3AQAAhaAgAAASqhIBDDowAAABAAAAAAAUABAAAADTB18IYAhhCGIIKgxcDF0MXgxfDMcPgBAjE54T5hPnE+kT9BP1ExgUHBQBBQDzhRMAAAEAAQBDhgOwkBP+AAGgAAAAEkRiZHcBgAKwAAltAAGwIA0GAxJkbgEDAPOeDwAAAQD7AAECAPYvDAAAAQAuAwEApQMBAAEAADswAAABAAAGAAIAAhMAAGcTBQAjgQKwQBPVAAFlc1OAA7AACYgAAWV4eQGAA6BAAAAT/LAgEp4AAbAACYYAAWVyeAGAAqBAAAAM8bBAD8gAAWdsFw0AAMiwwBMuAAGgEAAAERSgMAAAE3uwQAzpAAEARqALOjAAAAEAAAAAAAkAEAAAAOcF6AXvDPAMBA2aD/MP9A+aEpsSAQYA9KASAAABAIkAAQDzAwFi9KoQAAABAHsBAQC3DAAAiaBwAAASMrCQEs4AAbBgD+sAAaAwAAAS8jiJEzowAAABAAAAAAAtABAAAAA4BjkGOgY7BpgHmQdOCU8JUAmACYEJNAz7DPwM/QwBDQINAw2XD5gP6Q/qD+4P7w8LEAwQDRAOEA8QEBAREC8QNBBOEHsQfBDrEOwQgRIDEycTSxPdE+gTBhQVFA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/118928699d86.js b/pr/323/docs/search.index/118928699d86.js deleted file mode 100644 index 68e961e0e..000000000 --- a/pr/323/docs/search.index/118928699d86.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9JQSAAABAHoBAQATAgKwYBKWAAFmbfLlDAAAAQAjggKwQBK+AAFlbAEFAPR2EwAAAQBGAAEA8wMBY/KLEgAAAQABgQKgAAAACX+gMAAAD4ZzdBOAAqAQAAAHsG5404CEsHAUHQABkEAB") \ No newline at end of file diff --git a/pr/323/docs/search.index/11ac60f101c2.js b/pr/323/docs/search.index/11ac60f101c2.js deleted file mode 100644 index 30f5e9f51..000000000 --- a/pr/323/docs/search.index/11ac60f101c2.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9KYQAAABAAEAAQATgAKgsAAAE29jcwEEAPNyDwAAAQABAAGAAqBQAAAQeKBgAAAQ6GlyAYACoCAAAAyBoGAAABDnZWgBAQGgIAAAEOZi830MAAABAAEAE4ECoBAAAAyAYWMBgAKgEAAAEzuwMA0TAAFlcyMAAqBAAAATzmV38u0TAAABAAEGAPSUEgAAAQB6AQEAEwACsGASlgABZm3y5QwAAAEAAQEA9LMQAAABAKsAAQAjgAKgsAAAE1ZkZwGCAqCgAAASwqBAAAAQIG9284ADZ25yAYACoEAAABKPoDAAAA9vaGw3PwAAirBAD3AAAaAwAAAMfDRgVzswAAABAAAaAAQA3QEAABsEDgALDAkA1gwAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/11e6700fadac.js b/pr/323/docs/search.index/11e6700fadac.js new file mode 100644 index 000000000..056497270 --- /dev/null +++ b/pr/323/docs/search.index/11e6700fadac.js @@ -0,0 +1 @@ +rn_("AYACsRAUAgABsBAMZgABZW8BBQA7MAAAAQAADQABAEIRDQABAgDztg8AAAEAAQABAgD0sg8AAAEAAQABAAEDADswAAABAAAIAAEAYBAIAIcHAACHsKATOwABsNATxwABoFAAABOWjBQR9bkHAADUCAEAAQABAPOBAmx08wABaTswAAABAAB9AAMATAQAAJsQAACmEXsAAQUA8+gTAAABADUAAQABoEAAABCfZfNZCQAAAQDaCBOAAqAwAAAPj2Ry8wABZfNWBwAAAQABAPOAAmh0AQEAOzAAAAEAAHwAAgCbEAAAphF7ABOAAqBQAAAP0Gl0AQgA9G8TAAABAH4ABQATAISw8BPqAC2wkBPOAAGwUBK+AU5AiQA7MAAAAQAAEgAJABcQAgCNEQEArRIBAM4SAQD/EgEAYBMBAJQTAQCjEwEA4BMBANcBAACGsIARWwABoEAAAAl8CDAmOjAAAAEAAAAAAA0AEAAAAIEAggDDB7cJHQwUDa4QrxAqEisSpBKlEi4TLxM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/11fa761b7a02.js b/pr/323/docs/search.index/11fa761b7a02.js new file mode 100644 index 000000000..5cf1cd974 --- /dev/null +++ b/pr/323/docs/search.index/11fa761b7a02.js @@ -0,0 +1 @@ +rn_("AQYA9F4TAAABAFAAAQAjgAKhEAAAFAxjchOAAqAgAAAJXmh5AQEBoGAAABP/c/J7EQAAAQAjgAKwIAm+AAFldQGAArAgD9EAAaCQAAATtmFlAQcBsEAT0QABZfWZEwAAAQABAAEAAQABBAGwABKoAAFzOzAAAAEAAAsAAgAcEwcABBQDAEcAAASGoIAAABL9sQAUEQABsEASawABsIAUHwABsFASqgABFQAW9HIOAAABAAEAAQCDAISgAAAAE36wQBQVAAGwMBMwAAFIAAP3QwUAAHILAQClAAEABQABAAEDAbBAE+YAAXTyThMAAAEAI4UCoEAAABN8YnMBAQD0dxEAAAEAAQABABOAAqAQAAAJf2Rst+gAAIyg0AAAEwigUAAAERWgkAAAEySwUBKPAAGgMAAADHy3mAc7MAAAAQAAXwATANYBAQDcAQEAGAQDAD0GAAAsBwEAxAcBANYHAABbCQEAZwkAAHIJAACfCQAAlAw8AOMMAACeDwEAbRABAIkQAQDPEAEAUhEEAEcSAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/122324c9163d.js b/pr/323/docs/search.index/122324c9163d.js new file mode 100644 index 000000000..cb530d73b --- /dev/null +++ b/pr/323/docs/search.index/122324c9163d.js @@ -0,0 +1 @@ +rn_("AQEA9eMQAAABAAEAAQABACOBArCQE3oAAWF0Q4ADsRAT7wABoIAAABM9Y290EwACsAATxwABbnL24QEAAAEAVwIBAGEDbQs=") \ No newline at end of file diff --git a/pr/323/docs/search.index/124a72a66be7.js b/pr/323/docs/search.index/124a72a66be7.js new file mode 100644 index 000000000..18e6f63ca --- /dev/null +++ b/pr/323/docs/search.index/124a72a66be7.js @@ -0,0 +1 @@ +rn_("AYACsHAUAAABoAAAAAUvZmcBAQA7MAAAAQAABwABAFUMBwABAQGgcAAAEi1hAXkHAAAjgAKhAAAAFAhkbnOAhKBgAAASLABBSA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/125c69e2afa8.js b/pr/323/docs/search.index/125c69e2afa8.js deleted file mode 100644 index 35fc3c1d3..000000000 --- a/pr/323/docs/search.index/125c69e2afa8.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYQCsAARIQABsAARIwABNDbzAAFh8oIMAAABACOAArCQEScBCmx0AYACsHATDwABsIATQwABY3MBAAA6MAAAAQAAAAAACQAQAAAAfBF9EX8SgBJcE10TrROuEx0UHhTzgAJkZScBAICFoNAAABOCsBAFSQABoDAAABAjAggL") \ No newline at end of file diff --git a/pr/323/docs/search.index/12aab9d7ac8d.js b/pr/323/docs/search.index/12aab9d7ac8d.js deleted file mode 100644 index e7ca80d91..000000000 --- a/pr/323/docs/search.index/12aab9d7ac8d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBoEAAABEsc/VHDAAAuwQyAQEAAQATggKgIAAADQ9lciOAA6BAAAAMRrAwCXkAAWVvdAECAPQYEwAAAQAPAAEAE4ACsEATxQABYnYTgAKwAAebAAFveAEKAPVREwAAAQABAHAAAQABgQKgIAAAEJywUBJFAAFhbwEEAaAAAAATVnMBmxAAAGMDA7BQEQ4AAWNlc/ZVCQAAAQABACwAAQCpCAEBAPTcDAAAAQABAAEAI4ACsDASvAFOcnT3AQAAhrAQB50AAQHAB/LGAQAAiQ8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/12b95dafd39c.js b/pr/323/docs/search.index/12b95dafd39c.js new file mode 100644 index 000000000..78d292fbd --- /dev/null +++ b/pr/323/docs/search.index/12b95dafd39c.js @@ -0,0 +1 @@ +rn_("AQMBsEARBgABZfInDAAASgIBBQA7MAAAAQAABgABAA0QBgABAwD03gwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAAB4GHwYgBisHZQlrDHcMqBCpEKoQqxBDEkQSlBKVEjMAA7BQD4UAxmRwcjswAAABAAAPAAYAKwUAAN8FAwBxDwAAghEDAD4SAwBtEgEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/12ecb21c67a4.js b/pr/323/docs/search.index/12ecb21c67a4.js new file mode 100644 index 000000000..cbc4c1409 --- /dev/null +++ b/pr/323/docs/search.index/12ecb21c67a4.js @@ -0,0 +1 @@ +rn_("AQABsIATXgABdAFnCQAAAQkA81cSAAABAAEA84ACY2cBgwKgkAAAE6WgkAAAE6YyMwGAAqBQAAATzaCAAAAS+mNl8wACc3TyoQcAAAEAAQABoLAAABNBYvPVEgAAFwEBACOAAqAAAAAS1GRyI4ACoFAAABOXY2XzgAJpeQEGAPXjEAAAAQABAAEAAQABAQGwQBPRAAFl9ZkTAAABAAEAAQABAPcAAICFoBAAAAwqKWAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1323b5cdf876.js b/pr/323/docs/search.index/1323b5cdf876.js deleted file mode 100644 index 78f586d58..000000000 --- a/pr/323/docs/search.index/1323b5cdf876.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEAOzAAAAEAAAcAAQChCQcAAQIA9/kPAADQAIgCAQABAHAAAQATgAKwMBCKAKdpdPMAAXPy8hMAAAEAM4ADsRATcAABZG5w") \ No newline at end of file diff --git a/pr/323/docs/search.index/1365e7b5bd0a.js b/pr/323/docs/search.index/1365e7b5bd0a.js new file mode 100644 index 000000000..7034743ad --- /dev/null +++ b/pr/323/docs/search.index/1365e7b5bd0a.js @@ -0,0 +1 @@ +rn_("AQMA9gYNAACWAlkAAQCmAgEAAQQAOzAAAAEAAAoABAA3EAUAXBIBALISAQDEEwAAAYYCoAAAABLUoAAAABLVZHLzAgF2AfoSAADzgAJlbycBAADHoDAAAA+boHAAABIvoIAAABOXoNAAABM9oLAAABOXQEUICTswAAABAAAJAAMANgYCAIUHBABsDAEAI4ACIgcnlDjmZHIBAAA6MAAAAQAAAAAACgAQAAAAoAmhCdAP7BLtEhgTGRO4EwsUIRQiFBOAAqAwAAAQTWx0w4AEoAAAABA9oAAAABA+NDZhZQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/138a5af4d179.js b/pr/323/docs/search.index/138a5af4d179.js deleted file mode 100644 index e1d80a36f..000000000 --- a/pr/323/docs/search.index/138a5af4d179.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABoDAAABEsdPRpEAAAAQAXAAEAAYACoAAAABA7oAAAABA8NDbzAwF2AQUNAAAXAQAAh7CgEpwAAbAgDDkAAbBgEpgAAbCAEn8AAaAgAAAQoglERjowAAABAAAAAAAJABAAAABuB28HcAdHDKYPAhE0EjUSNhLHEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/13de217540ce.js b/pr/323/docs/search.index/13de217540ce.js new file mode 100644 index 000000000..a01f34c60 --- /dev/null +++ b/pr/323/docs/search.index/13de217540ce.js @@ -0,0 +1 @@ +rn_("AQADsCAQ8QABoFAAABGWoEAAABOqbHN28qUTAAABAAECAPR3EQAAAQABAAEAE4QCsCARewABYXPzgAJhZROAAldlx10Fx3JzAQEAOzAAAAEAAAYAAQAlFAYA8wYBZfTaEwAAAQAuAAEAEwcDsEAUAgABoGAAABODYXJ1AScQAAAjgAKgEAAADC5lcgEAADswAAABAAAeAA4ATQUBAAAGCAB6CQEAQQwBAOsMAQB2DgAAbg8CAIQPAACiDwEAixABAJ4QAAAzEQAAihEBAI8RAAABAQA6MAAAAQAAAAAACgAQAAAAZgxnDDQQNRDGEMcQyBBvE3AT7hPzEwGBArAAESMAAbAAESUAATQ2AQUA9HgTAAABAEYAAQDzBAFj8o0SAAABAAEDAaBAAAATqWXy0xMAAAEAAQQA85ISAAABAEYBAQQBsAASlAABZPQ+EgAAAQABAAEAZxIAAYmwoBPBAAGgUAAAEmGgMAAAEMywYBP0AAGwcBNyAAG1yAQ6MAAAAQAAAAAACgAQAAAAVQmDCXAMcQyvD7APsQ+AEcsSzBKXExGBAhCwIBF7AAFhcwECAPNXEgAAAQABAPcAAIDFoBAAABBNgAA4CAEFAPRVEgAAAQCaAAEAAQgA9AETAAABAIMAAQAzAQOwQBLWAAFhYmX1+QwAAAEA7gJIAsMAAQAA9UEMAAABACwDAQABAPOCAnN1AQYA9C8QAAABALEAAQAzAAOgoAAAFCxidHf18gUAAAEA2Q1QAAEAAQ8A9JQTAAABAEsAAQABBAD0LRAAAAEAsQABACOAArEAE/EAAW9w84ECc3QBBAGgoAAAFCx38hwUAAABAAELAPOREwAAAQABAAEMAPT/EgAAAQCjAAEAI4ACoHAAABO3Z3fzgAJlbwENAPPjEwAAMAABAAEQAPMLFAAAFgABAAEBAaBgAAASUHL3LgYAAAEAJwLhAwEAjwMBAAEDAaAAAAAP53PzPgwAAAEAAQABAwDzPQwAAMkEAQABgAKgQAAAE1CwMBJ/AAFhc/OAA25ydgEJAPSQEQAAAQAeAQEAAQcA9LUQAAABAKsAAQDzgAJoaQEBArAgDPcAAbAADHoAAWVz8sYHAAABAAEFAPSVDwAAAQDiAAEA84ACYmPX7wAAjaAgAAAHyKDgAAAThLDQE3YAARf1CzowAAABAAAAAAAaABAAAADZAdoB2wEVBBYEZgknDDkMeAx5DHwM3gzfDOAM4QxxDiYQchAcER0RYxFkEX0TwRPCE9UT1hMBgAKgsAAAE96gMAAAEMlsbhMAArCAE6sAAWVw84MTAAAkAAEAAQMBsEAT0QABZfWZEwAAAQABAAEAAQABBgD0NBMAAAEAAQABAAEPAPQNFAAAAQABAAEAAQIAOzAAAAEAAAUAAQD4EwUA8w0BbTswAAABAAAFAAEAuRMFAAEEAPVCEgAADAABAIMAAQABAAKgEAAAEQWw8BQVAAFla/eMEwAABQABAAEAJAATAAEAAQIA9FkQAAABAAEAAQABAgD0IxAAAAEAqQABAPeTAQCNoFAAABC4oQAAABP+sQAUAgABoKAAABLNvbwDOzAAAAEAACIADAAXDAMAqQ8AANEPAQDXEAcALhEAAGISAQBrEgEAbxIBAMUSAQDiEgQAsRMEABsUAAABAgA7MAAAAQAACwACAEcTBQD4EwUA8wEBbTswAAABAAANAAQA2QwAABIRAABxEgUAuRMFABMBAqAwAAAM3WVz8p4RAAABAAGJArAgE9wAAaAwAAAT5WlwEwADoCAAABKsoIAAABK/ZWl19SsMAAAyAAEAAQABAAGGArCQFAAAAaAAAAASRmJkAYACsDASoAABoDAAABCccnbXAAADhaAwAAAQGqAwAAAS9BABEzowAAABAAAAAAAJABAAAABPCVAJUQmBCYIJMRA2EO0Q7hCDEgEGADowAAABAAAAAAAHABAAAACND44PThBPEKISoxIsEy0TAQcA9BMTAAABAAEAAQB3AAAAhqBwAAATKaBAAAASw6BwAAAQykNABTswAAABAAAQAAcA8AEAAL4HAQCyCQEAoBABAKwQAQA5EQUAKBIBAHd3AICLsLAT7AABoGAAABDKtUEP") \ No newline at end of file diff --git a/pr/323/docs/search.index/1409f2140ee5.js b/pr/323/docs/search.index/1409f2140ee5.js deleted file mode 100644 index f33056f42..000000000 --- a/pr/323/docs/search.index/1409f2140ee5.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkBoEAAABPOZfLtEwAAAQABgAKwAAzsAAGggAAAEzxkcAEDAaAAAAAP53Pydw8AAAEAAQIDsGASwwABsNAT0QABoIAAABNbaXJzAdwTAAD3AACAhaEwAAAT7JBJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1446e2dc05e2.js b/pr/323/docs/search.index/1446e2dc05e2.js new file mode 100644 index 000000000..728f0cd94 --- /dev/null +++ b/pr/323/docs/search.index/1446e2dc05e2.js @@ -0,0 +1 @@ +rn_("AQUA9LgSAAABAIoAAQDzAAFj9QQQAAABALwAAQABAAEJAPPoEwAAAQA1AAENAaCgAAAULHfyHBQAAAEAAYECsIAT9gABoGAAABQaZHM3AQCEhaBAAAATzLBgE6AASwKQCiOAAqBwAAARBGRyAQsBoEAAABPQZfLvEwAAAQATgAKwMBAgAAFkcvMCAWX0PwgAAAEAAQABACOAArAgDB8AAWV5AQoA80sSAAABAAEAAQsAOzAAAAEAAAcAAQAcEwcAAQIA9HgTAAABAEYAAQC3AACAhbBwEn0AAbDQFBEAAWAgBgEGAPNXEgAAAQABAAEDAbBAE+YAAXTyThMAAAEAI4UCoEAAABN8YnMTAgKgUAAAERViZvdbCQAAAQBDAM4GAQBhAAEA84ACaXABBADzHRAAAAEAAQDzAAFwOzAAAAEAAAwAAQBJBwwAI4ADoDAAAAwtsHATMAABYWVp84ADZG1uAQAA9mUJAAASA8wFAQBQAAEA8wIBZDswAAABAAAJAAMAghEDAD4SAwBtEgEAI4ACoJAAABPDaW8BgAOwMBLpAAGAEyYAEAGwUBDEAAFjc3jzAAFlAVIFAADnFQAAiaAAAAAMhaBAAAAM2rBADNsAAZBIXQHjAQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1455b2c199cd.js b/pr/323/docs/search.index/1455b2c199cd.js deleted file mode 100644 index 6e7ac3768..000000000 --- a/pr/323/docs/search.index/1455b2c199cd.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9FMQAAABAAEAAQABAAA6MAAAAQAAAAAACgAQAAAAlQ+WD3kQehAGEZIRkxGUEpUSDxQQFPMBAXnyfRMAAAEAAQADoKAAABPCoCAAABMGsDARiAABZWl08nsSAAABAAEDADswAAABAAAGAAEABxEGAAEIAPRtEwAAAQB+AAUAEwEDsJATzAABsFASvAFOZ2xwOzAAAAEAABIACQAVEAIAixEBAKsSAQDMEgEA/RIBAF4TAQCSEwEAoRMBAN4TAQABgAKwMBGEAAGwMBGGAAFydgECAbAAEkEAAWQ7MAAAAQAABQACAIARAwBrEgEAI4ECoJAAABPBaW8BBwD03RMAAAsAHgAPAHcWAAGJsFATfQABsFASQwD9sGASugABtQkDOjAAAAEAAAAAAA0AEAAAAOIM4wzkDHUOdg5yEHMQrhDREDIRMxE0ETURshJDgAOgIAAADNigEAAADOFtbm8BAwD0EhAAAAEAAQARAQGAAqCwAAATlLAwEuwAAW50QwADoCAAABC9sFARkAABZWhpOzAAAAEAAAYAAwAoDAAAnxEDAEMTAQDzgAJkcocLAAAKgAHKABABgAHNABABoFAAABEvoFAAABPHoDAAABA/oDAAABBKMDFjZWlvc3R1d/X7BwAAAQB9B7ICRAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1487b03e5721.js b/pr/323/docs/search.index/1487b03e5721.js new file mode 100644 index 000000000..038a5c8cd --- /dev/null +++ b/pr/323/docs/search.index/1487b03e5721.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABPQZfLvEwAAAQABgAOgMAAAEi6w4BOvAAGwkBPxAAFhY22HAQCAhrAQENEAAbAQEPEAAaCgAAAT3qDwAAAT/jFJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/14e256e1d6e5.js b/pr/323/docs/search.index/14e256e1d6e5.js new file mode 100644 index 000000000..576e407de --- /dev/null +++ b/pr/323/docs/search.index/14e256e1d6e5.js @@ -0,0 +1 @@ +rn_("AQUBsEASTgABZfJvEgAAAQABggKgkAAAE6WgkAAAE6YyMwEAAqAgAAASLKAgAAAJTGltOzAAAAEAAAwABgAnBQAAXAUFALYJAABpDwEAfBMAAI8TAQDzAAFzAdYQAAABBQA7MAAAAQAABQABAAoTBQABAAD1iAwAAAEAAQABAAEA84ACZ2tzgISwsBNiAAERSAABBgGgQAAAEZdl8woNAACzAwEAAYACoJAAABNxsFAQ/QABZ3oBgAOwUBOeAAGgEAAADBagcAAAEvpnbnLHAQAAhaBgAAATfaBQAAATzRUBBDswAAABAAARAAQA1AEBAGAQCABdEQMASxICAAGFArBwE4oAAbAwEucAAWVzEwADsEAPiQABoGAAABEyYmVzOzAAAAEAABIABAAhBgEAMAcKAP8PAQBREAMAAQMA9BsQAAABAAQAAQABCwGgQAAAE9Bl8u8TAAABAPOAAmRy8wACZW87MAAAAQAABwACAP4HAwA/CAMAAQIAOzAAAAEAAAkAAQAMDAkAAQAA9FUQAAABAAEAAQABAQA7MAAAAQAA3gACAD4GAABmCN0AYwADsBASpgABZWlzOjAAAAEAAAAAAAwAEAAAAF4JlQmWCZcJmAkwDDEMYA9hD4gPpBAGEwcT44CEsCAHuwABEUAEAQGEsDAMHwABoEAAAAzaoIAAABEEsEAM2wABAAAdAeMBAAABBQD1UxMAAAEAAQBwAAEAE4ACoAAAAAdkYWcBAwD0JRIAAAEAVAEBAPOAAmlvAQEAOzAAAAEAAN4AAgDYBwAALgvdAAEIAbBAE9EAAWX1mRMAAAEAAQABAAEA84ECZ3QBAQGgcAAAEZ1pOzAAAAEAAPAAAgDXBwAAPgrvAAEFAPNvEAAAAQABAPOAAmFvE4ACoEAAAAxGcnUBgAKgAAAAB72gEAAACV1keBMAArAgDQEAAWVy8soPAAABABOAArCwFBUAAXJ4AQoA9BMTAAABAAEAAQATgAKwEBC4AhVhZQEEAPRREAAAAQABAAEAAQQAOjAAAAEAAAAAAAcAEAAAALgSuRLHEsgSQxNEE1YTVxMBBAD0qBAAAAEAAQABAAEDAPNeEgAAAQCaAPOAhAFIAce/AwCPoIAAABJgsGAM8QABsEAMbgABsTAT7AABP6kfOzAAAAEAAE0AIwBoAAAAbAAAAEgFAQBRBQAA+gUBAKEHAQCrBwEAswcAALcHAQCPCQAAogkHALgJAQAeDAAAMwwBADoMAQBLDAcAbgwBAIYMAQDRDAEAlw8BAAQQAQB7EAEAhxABAJ8QAADBEAIA0RABAOMQBAAIEQAAVxEDAJQRAQA9EgAAlhIBAJoSAQDDEgAAERQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1511e66727f1.js b/pr/323/docs/search.index/1511e66727f1.js deleted file mode 100644 index 7fd289146..000000000 --- a/pr/323/docs/search.index/1511e66727f1.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBgBL1ABDsczswAAABAAAFAAEA2hIFACOCAqCQAAATy2lzAQEA9KISAAABAIkAAQAjgAKwoBN9AAFjbgGEArCgE4sAAbCgE40AAWNyAQIAOzAAAAEAAAYAAQALEAYAE4ICoCAAABNvZXUBAgDzaxEAAAEAAQABgAKwIBOrAAGgYAAAEwNvdmOBA6AQAAASwmFjcwEDAPTcDAAAAQABAAEAAQEA9KAQAAABAJwAAQATgAOgwAAAE9ewEAlvAAFucHUBgAKwMA/IAAGhYAAAFAphZQGBArBAEk8AAbBAElEAATQ2AQUA9CEQAAABAKkAAQBTgISgsAAAE5SwMBLsAAEBoAQBCADz6RIAAAcAFwATgAKwQBGQAAFhZQEAAaAgAAAQPXIBAxEAAAEAAPQ9EwAAAQBKAAEApwAAgIWgMAAAEJ2gEAAAEL2woBOfAAEJICEBCQGgQAAAE85l8u0TAAABAEOAhKAgAAAT3KCQAAATPKEwAAAT7BBJAAECAPTWEgAAAQCOAAEAE4ACsEASpAABaXkBAQKgMAAAEoGgQAAAEsFncjswAAABAAAHAAIAnhABADcRBQABgAKwABNXAAGwABNZAAE0NgEGAPQWFAAAAQAKAAEA8wICcnYBghMAAPMCAWX0KRAAAAEAqAABAPOAAmR0560/ABKgMAAAEL6gQAAADPGw8BQTAAGwcBMEAAEwYWJjZWdoaWtsbm9wcnN0dXY7MAAAAQAA9wBFAEsFAAAdBgIAJwYDACoHAACJBwAArAcCALwHAADOBwQA1AcAADUIAABdCAEAUgkBAFgJAQBkCQAAvQkBAMEJewAaDAEAKAwAADUMAABqDAAAdgwAAGMPAQCLDwEAog8AAAQQAAAtEAEAPxAAAEwQAQCREAEAlRADAKYQAwDfEAEAAREAAB4RAgBOEQAAZxEAAIoRAACOEQEAnhEEADISAABBEgEARBIAAE4SAABfEgAAkhIBAKASAQCtEgEAtBIBALoSAQC9EgAAwBIAANISAADnEgEA/BIAAAETAAAREwQAKhMBADsTAAA9EwEAQxMBAE4TAAB8EwAAiBMCAI8TAgCpEwEAtRMAAMgTAQDvEwEAKhQAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/15358d1515f8.js b/pr/323/docs/search.index/15358d1515f8.js new file mode 100644 index 000000000..51f113979 --- /dev/null +++ b/pr/323/docs/search.index/15358d1515f8.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAAUAAQAKEwUA8wUBdDswAAABAAAIAAIAHgwAAEsMBwABBQDzJhMAAAEAAQA3AACAhbAwD/wAAaCgAAATQaAwAAATfBBBSA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1552619586d7.js b/pr/323/docs/search.index/1552619586d7.js deleted file mode 100644 index 1630d8ae6..000000000 --- a/pr/323/docs/search.index/1552619586d7.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAABYACQAFEAUADhEBAJgRAgBYEgEAhhIEAHITAQCWEwAAwRMAAOATAAAjgAOh8AAAFCqw8BQdAAFjZmcjgAKwUBMwAAFtbgEFAPNyEwAAAQBtAPMEAWMBLxEAAAEBAbBwE8UAAXA7MAAAAQAACQACADYIBwB0EAEAAQIAOzAAAAEAAAsAAgBFEwUA9hMFAPMBAW07MAAAAQAADQAEANcMAAAQEQAAbxIFALcTBQATAAKgMAAADNtlc/KcEQAAAQABggKwcBOlAAGgQAAAELppc/cBAICGsJATiwABkEkI") \ No newline at end of file diff --git a/pr/323/docs/search.index/159c51570c5d.js b/pr/323/docs/search.index/159c51570c5d.js new file mode 100644 index 000000000..5819f7940 --- /dev/null +++ b/pr/323/docs/search.index/159c51570c5d.js @@ -0,0 +1 @@ +rn_("AQkA8+gTAAABADUAAQ0BoKAAABQsd/IcFAAAAQABgQKwgBP2AAGgYAAAFBpkczcBAIWFoEAAABPMsGAToABLApAK") \ No newline at end of file diff --git a/pr/323/docs/search.index/15a27ab8af3a.js b/pr/323/docs/search.index/15a27ab8af3a.js new file mode 100644 index 000000000..8855da02c --- /dev/null +++ b/pr/323/docs/search.index/15a27ab8af3a.js @@ -0,0 +1 @@ +rn_("AQIAOjAAAAEAAAAAAA4AEAAAABINEw25EJcRmBGZEUISSRJKEk4STxKeEp8S0hLTEgEJAPMgEQAAAQABAAEKAPNXEgAAAQABAAEGAPXjEAAAAQABAAEAAQDzgAJkbnMAhLEgE60AAQARA/KmEgAAAQABDQD2GBMAAAEAnwBTABYAAQABgAKwoBMwAAGgEAAADEljdQEBAPQ/EwAAAQBKAAEA8wABZfK+CQAAAQD3AACAxbAwEF4AAQACqAg=") \ No newline at end of file diff --git a/pr/323/docs/search.index/15dba3e34fef.js b/pr/323/docs/search.index/15dba3e34fef.js new file mode 100644 index 000000000..fcca10c0e --- /dev/null +++ b/pr/323/docs/search.index/15dba3e34fef.js @@ -0,0 +1 @@ +rn_("AQcA9hgTAAABAJ8AUwAWAAEA8wEBaQGqCQAAAQAA9a0JAAABAAEAAQABAEcBAIDGNSXP3bEDoKAAABP+sGATegABoBAAAAmGAAogig==") \ No newline at end of file diff --git a/pr/323/docs/search.index/16033c7bb8f7.js b/pr/323/docs/search.index/16033c7bb8f7.js new file mode 100644 index 000000000..e894d5552 --- /dev/null +++ b/pr/323/docs/search.index/16033c7bb8f7.js @@ -0,0 +1 @@ +rn_("AQoBoIAAABQbZgEQEwAAAQgA9bETAAABAAEAAQABAPMDAWb1QhAAAAEAAQABAAEA84MCYXMBAgKgIAAAEX2gMAAAEjViaAF/DwAAI4ACoIAAABLJYXDzAAJobQEvBQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/16045633eb64.js b/pr/323/docs/search.index/16045633eb64.js deleted file mode 100644 index 1fddcf8e3..000000000 --- a/pr/323/docs/search.index/16045633eb64.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9FMSAAABAJoAAQABCAD0/xIAAAEAgwABADMBA7BAEtQAAWFiZfX3DAAAAQDuAkgCwwABAAD1QAwAAAEAKwMBAAEA84ACc3UjgQKwgBL5AAFydAEGAPQtEAAAAQCxAAEAUwCEsIASjQABoKAAABQqAgEU9fEFAAABANgNUAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/16e43e96c3f0.js b/pr/323/docs/search.index/16e43e96c3f0.js deleted file mode 100644 index 0aaba5b88..000000000 --- a/pr/323/docs/search.index/16e43e96c3f0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYICoFAAABJooQAAABP9Y3QjAAKwQA+EAAFldTswAAABAAA3ABYALQcBACMMAQD5DAEAiQ8BAAUQBQA1EAUArxABAA4RAQCYEQIAWBIDAIYSBACwEgEAthIBAMUSAQBBEwEAVBMBAHITAQB2EwEAlhMAAL0TAQDBEwEA4BMAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/17ad98e37001.js b/pr/323/docs/search.index/17ad98e37001.js new file mode 100644 index 000000000..b809440ea --- /dev/null +++ b/pr/323/docs/search.index/17ad98e37001.js @@ -0,0 +1 @@ +rn_("AQEBoGAAABP/c/J7EQAAAQABCAD1sRMAAAEAAQABAAEAowCEsCAJvgABoIAAABJ4MEAIOzAAAAEAAAUAAgDIAQAAQhAEAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/18c051295b43.js b/pr/323/docs/search.index/18c051295b43.js deleted file mode 100644 index 3129c7ab0..000000000 --- a/pr/323/docs/search.index/18c051295b43.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBsIAS5wABdfKSCQAAAQAjgAKwEAmQAAEzNgECAPSMCQAAAQBcBwEAAQIA82IMAAABAAEAE4ACsBAJigABaXUBAgA7MAAAAQAACwACAEUTBQD2EwUA8wkBbTswAAABAAAMAAMAEBEAAG8SBQC3EwUAdyAAAIqgMAAAE1uwMBEtAAGgcAAAEZugIAAACY6wMBFhAAGwIAezAdyzhQo7MAAAAQAAFAIEANABAAD1ASIB1gcAAD0K7wA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/19476e380db8.js b/pr/323/docs/search.index/19476e380db8.js deleted file mode 100644 index 02a49233c..000000000 --- a/pr/323/docs/search.index/19476e380db8.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAADwAAgBxCQAAkww7AAGAArBQEGkAAbCwExgAAWFsYwAEsEAPZwABoBAAABAjYnBydjswAAABAAAGAAEABxEGAAGAArCAELUB/rDgE30AAWlyAQMBsEARBAABZfImDAAASQIBBQA7MAAAAQAABgABAAsQBgABAwD03AwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAAB0GHgYfBioHZAlqDHYMphCnEKgQqRBBEkISkhKTElcAAACFsFAQWwIbsFAPgwDGoEAAABEwSIADOzAAAAEAAA8ABgAqBQAA3gUDAG8PAACAEQMAPBIDAGsSAQDTAISgYAAADO4QCAbzhQEAAAEASws=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a238fdc3e6a.js b/pr/323/docs/search.index/1a238fdc3e6a.js new file mode 100644 index 000000000..2a6068f61 --- /dev/null +++ b/pr/323/docs/search.index/1a238fdc3e6a.js @@ -0,0 +1 @@ +rn_("ARMA9ScUAAABAAEAAQABABOAAqDAAAASyWFvAQICoLAAABPDoLAAABPEY3PzdBAAAAEAXgABgQKwUBGSAAGgwAAAE5ZoafOAAmVpI4ACoLAAABMDaXQBBgGwgBPKAAF2OzAAAAEAAAcAAgBwEQQAOBMCAHcAAACGoKAAABMloJAAABLzsFAQMgABBeAC9HwHAAABAAEAgAw=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a59836e6e94.js b/pr/323/docs/search.index/1a59836e6e94.js deleted file mode 100644 index 6813a4a57..000000000 --- a/pr/323/docs/search.index/1a59836e6e94.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYQCsCARJwEKoJAAABOWbXABgAKgMAAAELKgUAAAEz9jcpcAAACFsCATDwABsMATxQABoGAAABF+EEAOOzAAAAEAABIABgBSBQgAfBEBAH8SAQBcEwEArRMBAB0UAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a6a4539ae89.js b/pr/323/docs/search.index/1a6a4539ae89.js deleted file mode 100644 index 1c0568ed1..000000000 --- a/pr/323/docs/search.index/1a6a4539ae89.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBsEATzwABZfWXEwAAAQABAAEAAQABAgA7MAAAAQAACwACAEUTBQD2EwUA8wEBbTswAAABAAANAAQA1wwAABARAABvEgUAtxMFAPMBAWXynBEAAAEAM4CEsBAQMgABoCAAAAwtFEAE") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a6b9bf2988f.js b/pr/323/docs/search.index/1a6b9bf2988f.js deleted file mode 100644 index 69faa8d94..000000000 --- a/pr/323/docs/search.index/1a6b9bf2988f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9KISAAABAIkAAQABAwD01hIAAAEAjgABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAAC0QLhDfEOAQjhGPEa0SrhIBAgD0GBEAAAEAYAEBAAEFAPMeEQAAAQABAAEEAPRTEgAAAQCaAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a6f5bcc1ce3.js b/pr/323/docs/search.index/1a6f5bcc1ce3.js new file mode 100644 index 000000000..a15f37e3a --- /dev/null +++ b/pr/323/docs/search.index/1a6f5bcc1ce3.js @@ -0,0 +1 @@ +rn_("AQYA9LoQAAABALoAAQATgQOgAAAAEQOxIBQJAAFhZG4BFAD1JxQAAAEAAQABAAEAAQYBsIATygABdjswAAABAAAHAAIAcBEEADgTAgBXAAABhqDAAAATA6CgAAATJaCQAAAS87BQEDIAAQXgAvR8BwAAAQABAIAMAQYA814SAAABAJoAAQABoGAAAA+dcgFABAAA1wEAgMWgMAAADCgAAIoK") \ No newline at end of file diff --git a/pr/323/docs/search.index/1a85850fa4c4.js b/pr/323/docs/search.index/1a85850fa4c4.js new file mode 100644 index 000000000..fed05c4dc --- /dev/null +++ b/pr/323/docs/search.index/1a85850fa4c4.js @@ -0,0 +1 @@ +rn_("AQABoLAAABNBYvPVEgAAFwEBAAGAAqAgAAAPhKAwAAATTWFy8wABdAHhBwAAAQQBoEAAABPQZfLvEwAAAQABBQA7MAAAAQAACwAFAAcQBQBaEgEAdBMBAMMTAADiEwAA4wGEoDAAAA+ChQACOzAAAAEAAAsABQAbBgIAJgcEAGkMAQAxEQAAahIAAGOAA7AAD6QDMGRucg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1b3b27c553dd.js b/pr/323/docs/search.index/1b3b27c553dd.js new file mode 100644 index 000000000..23da7f0a0 --- /dev/null +++ b/pr/323/docs/search.index/1b3b27c553dd.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAD0AAgByCQAAlAw8AAGAArBQEGsAAbCwExoAAWFsYwAEsEAPaQABoBAAABAlYnBydjswAAABAAAGAAEACREGAAGAArCAELcB/rDgE38AAWlyAQMBsEARBgABZfInDAAASgIBBQA7MAAAAQAABgABAA0QBgABAwD03gwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAAB4GHwYgBisHZQlrDHcMqBCpEKoQqxBDEkQSlBKVElcAAACFsFAQXQIbsFAPhQDGoEAAABEySIADOzAAAAEAAA8ABgArBQAA3wUDAHEPAACCEQMAPhIDAG0SAQDTAISgYAAADPAQCAbzhgEAAAEATAs=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1b5a76a5a4b0.js b/pr/323/docs/search.index/1b5a76a5a4b0.js deleted file mode 100644 index 285b14ced..000000000 --- a/pr/323/docs/search.index/1b5a76a5a4b0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoAAAAA/lc/M9DAAAAQABAAECAPM8DAAAyAQBACMAAqBAAAAM82Vy8kAFAAABAAGAAqBAAAATTrAwEn0AAWFzAQIBoEAAABPOZfLtEwAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/323/docs/search.index/1bf5442726c7.js b/pr/323/docs/search.index/1bf5442726c7.js deleted file mode 100644 index fc035aa46..000000000 --- a/pr/323/docs/search.index/1bf5442726c7.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA83IPAAABAAEAAYACoFAAABB4oGAAABDoaXIBgAKgIAAADIGgYAAAEOdlaAEBAaAgAAAQ5mLzfQwAAAEAAQATgQKgEAAADIBhYwEGAPSUEgAAAQB6AQEAEwACsGASlgABZm3y5QwAAAEAI4ACoMAAABNWbnInDwAAiKBAAAAPb7BAD3AAAaAwAAAMfDQgFzswAAABAAAZAAMA3QEAABsEDgALDAkAAYADsHATDwABsDASvgABsIATQwABY3JzAQABsCATDwABbzowAAABAAAAAAAJABAAAAB8EX0RfxKAElwTXROtE64THRQeFAGAArAgCXQAAbAwEskAAXJ09wAAAMegIAAAB9WgYAAAD5uwgBLJAAFABpII9uoBAABVAnoFAQC2CQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1c336ea7d8f9.js b/pr/323/docs/search.index/1c336ea7d8f9.js new file mode 100644 index 000000000..201fc051b --- /dev/null +++ b/pr/323/docs/search.index/1c336ea7d8f9.js @@ -0,0 +1 @@ +rn_("AYECsDAP7QABsBATMgABc3QBAwDzVxIAAAEAAQAjgAKwABFjAAFoaQEAADswAAABAAATAAEA6AcTACOAAqAwAAAM4mFl84ACcnTzAAJhbDswAAABAAAoAAMAhQATAIUBAADTDxMA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1c84f4d721b0.js b/pr/323/docs/search.index/1c84f4d721b0.js deleted file mode 100644 index c83e01cc7..000000000 --- a/pr/323/docs/search.index/1c84f4d721b0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBsEATLgABZfIrBwAAAQABBADzGxAAAAEAAQBDAAOwQBKcAAGxABNyAAFlZnA7MAAAAQAAIwAGAOwBAAA6BwwASAcMAFgPBQCyEAAA2BIBAGOAA7AwDCsAAWFlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1cf369215e3f.js b/pr/323/docs/search.index/1cf369215e3f.js deleted file mode 100644 index 2e5273b3b..000000000 --- a/pr/323/docs/search.index/1cf369215e3f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYkCsCAT2gABoDAAABPjaXATAAOgIAAAEqqggAAAEr1laXX1KgwAADIAAQABAAEAAYYCsJAT/gABoAAAABJEYmQBgAKwMBKeAAGgMAAAEJpydtcAAACFoDAAABAYoDAAABLyEAETOjAAAAEAAAAAAAkAEAAAAE4JTwlQCYAJgQkvEDQQ6xDsEIES") \ No newline at end of file diff --git a/pr/323/docs/search.index/1d3a063d9304.js b/pr/323/docs/search.index/1d3a063d9304.js new file mode 100644 index 000000000..36aa6302e --- /dev/null +++ b/pr/323/docs/search.index/1d3a063d9304.js @@ -0,0 +1 @@ +rn_("AQYA9DQRAAABAAEAAQABAgA7MAAAAQAADAAFACQMAQA3EAUAXBIBALISAQDEEwAA84ACZm4BAQA7MAAAAQAABwACAFoPBQDaEgEA84ACZG4BAQDzlAcAAAEAAQABhgKgAAAAEtSgAAAAEtVkcvMBAXYB+hIAAEMAA7CAEm8AAbCwE/EAAWtscjswAAABAAAMAAMAiwcIAAsNAQD3DwEAAQIA9VkIAAABAAEAAQABAPMAhBRBAAFtAAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1d528d74dc33.js b/pr/323/docs/search.index/1d528d74dc33.js new file mode 100644 index 000000000..a391a9973 --- /dev/null +++ b/pr/323/docs/search.index/1d528d74dc33.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAN4AAgAmDAAAFw3dAPMAAW07MAAAAQAA3gACABgGAABFBt0AAQMAOzAAAAEAAAcAAgBaDwUA2hIBAPMAAWM7MAAAAQAAVwAmADEFCwDmBQEAYQkBAD0MAABDDAIA9QwAAAANAgASDQEAgQ8AAIYPAQCJDwEAjw8AABQQAgAbEAEAIBABAD8QAACzEAAAuRAAAAYRAQATEQEAJxEBACsRAgCGEQEAlxECAJ0RAABCEgAASRIBAE4SAQCSEgEAnhIDAMASAQDQEgMA1hIBADATAQBBEwAAqRMAAM4TBADXEwIA84ACcncBCQDz6BMAAAEANQABDAGgoAAAFCx38hwUAAABAAEBAPOqDwAAAQABAPOAAnR1AYECsIAT9gABoGAAABQaZHM3AgCBhqBAAAATzLBgE6AAS7AwE40AAQKQDgGIArAAE1kAAbAAE1sAATQ2M4ADoDAAABEqZG5zYwADoBAAAAVRYWVvAXYAAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1dd9eac90f78.js b/pr/323/docs/search.index/1dd9eac90f78.js new file mode 100644 index 000000000..e29d59e79 --- /dev/null +++ b/pr/323/docs/search.index/1dd9eac90f78.js @@ -0,0 +1 @@ +rn_("AQUA9HgTAAABAEYAAQDzAwFj8o0SAAABACOAArBwFB8AAWhvAYICsAASawABsAARigABZXNDAAOgoAAAE8SgIAAAEwhlaXT1fRIAAAEAeQABAOwAAYECoEAAABL+sEASvAABYWYBgAKwcBJiAAGgsAAAE4xlbwECADswAAABAAAGAAEACREGAAECAaBAAAATqWXy0xMAAAEA84ACaW8BCAD0bxMAAAEAfgAFABMBA7CQE84AAbBQEr4BTmdscDswAAABAAASAAkAFxACAI0RAQCtEgEAzhIBAP8SAQBgEwEAlBMBAKMTAQDgEwEAAYACsEASRQD9oCAAABDMYWkBgQKwMBN/AAGgMAAAEmFjaQEDAbAAEqgAAXM7MAAAAQAADQADAIgRAQAcEwcABBQDAAEDAPWGEQAAAQALAQEARgHzgAJydgEAAPRDEgAAAQBQAAEA8wIBZDswAAABAAAJAAMAghEDAD4SAwBtEgEAI4ACoJAAABPDaW8TgAKw8BQRAAFsbwEFAPTfEwAACwAeAA8AI4ACoGAAABL9Z3MjgAKwkBPBAAFibPeXAwCPsHARngABsGAT9AABsHATcgABsFASqgABtfkXOzAAAAEAADsAFwAsBgAAzQcBAEUJBQBVCQAAgwkAACoMAABwDAEA5AwCAA0NAQByDgMAdw4BAK0PBAB0EAEAsBAAANMQAADzEAkAMhEAADQRAwCAEQAAtBIAAMsSAQDcEgUAlxMAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1e602721a639.js b/pr/323/docs/search.index/1e602721a639.js deleted file mode 100644 index 9bf417261..000000000 --- a/pr/323/docs/search.index/1e602721a639.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMA9gQNAACWAlkAAQCmAgEAAQQAOzAAAAEAAAoABAA1EAUAWhIBALASAQDCEwAAAYYCoAAAABLSoAAAABLTZHLzAgF2AfgSAADzgAJlbycBAADHoDAAAA+ZoHAAABItoIAAABOVoNAAABM7oLAAABOVQEUICTswAAABAAAJAAMANQYCAIQHBABrDAEAI4ACaXe0yZZoZHIBAAA6MAAAAQAAAAAACgAQAAAAnwmgCc4P6hLrEhYTFxO2EwkUHxQgFBOAAqAwAAAQS2x0w4AEoAAAABA7oAAAABA8NDZhZQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1f0367152a3f.js b/pr/323/docs/search.index/1f0367152a3f.js deleted file mode 100644 index 4c2a42d7b..000000000 --- a/pr/323/docs/search.index/1f0367152a3f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYECoCAAABCcsFASRQABYW8BAwGgAAAAE1ZzAZsQAAAjgAKgAAAACFZydmMAA7BQEQ4AAWNlczswAAABAAAIAAQAVQkCAIMJAQAbEAIALRIAAAcBAICFsFAQzQABoKAAABMisPATOQABsCAPYAABRCEE") \ No newline at end of file diff --git a/pr/323/docs/search.index/1f11eee293c6.js b/pr/323/docs/search.index/1f11eee293c6.js deleted file mode 100644 index cf4b780f5..000000000 --- a/pr/323/docs/search.index/1f11eee293c6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYECoCAAABP9sEAPqwABYnABBQD0xRIAAAEAjgABAPMDAWMBOxIAAAEMAPQCFAAAAQABAAEA84ACbnYBgAKwMAxoAAGgMAAADGppbwECAaAAAAATy3MBlBEAACOAAqBAAAATfGFl5wEAgIWwAAXrAAGQSAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/1f1d2b61eca6.js b/pr/323/docs/search.index/1f1d2b61eca6.js deleted file mode 100644 index d38c8f3de..000000000 --- a/pr/323/docs/search.index/1f1d2b61eca6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYYCoAAAABLSoAAAABLTZHIBgAKgcAAAE1ugMAAAELpmdAELAaBAAAATp2Xy0RMAAAEAI4ECsFAQkQABY3YBAAGwgBOpAAFw8qUTAAABAEMGA6AgAAARA6BAAAASQGRlc/OnDwAAHAMBAPMABGlyc3Y7MAAAAQAADwAFAF0HBQBiEgUAjRIBAPgSAADcEwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/1f6d06e60df8.js b/pr/323/docs/search.index/1f6d06e60df8.js deleted file mode 100644 index ac1a22a0c..000000000 --- a/pr/323/docs/search.index/1f6d06e60df8.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAAOzAAAAEAAAYAAgBbEQMASRICAAEKAbBAE88AAWX1lxMAAAEAAQABAAEA8wACaXP0OQwAAAEASwABACOBAqDgAAATtmV5") \ No newline at end of file diff --git a/pr/323/docs/search.index/1f850a139dfa.js b/pr/323/docs/search.index/1f850a139dfa.js deleted file mode 100644 index 50cc94896..000000000 --- a/pr/323/docs/search.index/1f850a139dfa.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYAOzAAAAEAAAUAAQDaEgUAg4YEsFASpAABsHATBAABoNAAABPXYm5vdhOAArAQEn0AAXByAQABoDAAABGNdAEjEwAA8wIBc/JOEAAA2QITgAKgYAAAED9ldQECAPYNDQAAAQDpAgEAJAEBAAGAArAAEmkAAYAOdDFAAWVzAQEA87kHAABACwEAY4ADsIATdAABYm5084ADYW9yARQA9AIUAAABAAEAAQABAAA7MAAAAQAABgACAAITAABnEwUAI4gCsEAT1QABZXMBBAGwMBLYAAFj8ykRAAABAAEAAQUAOzAAAAEAAAgAAwCYEQIAhhIEAJYTAADzAgNjZXDz0gwAAAEAAQBjgAOgYAAAD6Jlb3UBAAGwYBN/AAFlAbQTAADzAgFz9nYJAAABAAEAcAc/AAoBAQgA80kSAAABAAEAQ4ADoBAAABMGoOAAABOKZG9zAQIBoFAAABEocgHIEAAAAYACoEAAABBLoHAAABPCYWUBAAA7MAAAAQAACwACAF0HBQBiEgUA84ACdHYBAQD0UAgAAAEAAQABAPMAAm5y9mwJAABQAG8GAQCxAAEA8wCHMJEM9XsSAAABAHkAAQDsAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/1fb072ef9a66.js b/pr/323/docs/search.index/1fb072ef9a66.js deleted file mode 100644 index b247e00d2..000000000 --- a/pr/323/docs/search.index/1fb072ef9a66.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEAOzAAAAEAAAYAAQAjFAYA8wYBZfTYEwAAAQAuAAEAEwMDsEAUAAABoGAAABOBYXJ1ASUQAAAjgAKAER4AEAFkcg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/200f734b64d0.js b/pr/323/docs/search.index/200f734b64d0.js new file mode 100644 index 000000000..41c77f312 --- /dev/null +++ b/pr/323/docs/search.index/200f734b64d0.js @@ -0,0 +1 @@ +rn_("AQsA9BoTAAABAA8AAQABBAGwQBLWAAFlATASAADzAAF0AcwTAAABDQDz4xMAADAAAQABEADzCxQAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAAQTAABpEwUAI4YCsEAT1wABZXMBBAD0axAAAAEAFwABAPOAA2NydDOAhLBgEIcAAaAQAAAGLQEICQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2025f39bb570.js b/pr/323/docs/search.index/2025f39bb570.js deleted file mode 100644 index 3070be59c..000000000 --- a/pr/323/docs/search.index/2025f39bb570.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYICsCAQ6QABsDARYQABZXABBQD0lQ8AAAEA4wABAFOAA7CQEZIAAWFpcgGBArCgE4sAAbCgE40AAWNyxwAAAIWwUAz5AAGgIAAACbugIAAADDQ8AAE6MAAAAQAAAAAACgAQAAAAiwGMASoEYwdyCXMJJxAoEJMQlBCjEQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2033cb7c9d15.js b/pr/323/docs/search.index/2033cb7c9d15.js new file mode 100644 index 000000000..6447f697a --- /dev/null +++ b/pr/323/docs/search.index/2033cb7c9d15.js @@ -0,0 +1 @@ +rn_("AQgA9G8TAAABAH4ABQATAAOwkBPOAAGwUBK+AU5nbHA7MAAAAQAAEgAJABcQAgCNEQEArRIBAM4SAQD/EgEAYBMBAJQTAQCjEwEA4BMBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2050f119410b.js b/pr/323/docs/search.index/2050f119410b.js new file mode 100644 index 000000000..38977057c --- /dev/null +++ b/pr/323/docs/search.index/2050f119410b.js @@ -0,0 +1 @@ +rn_("AQMBgBL3ABDsczswAAABAAAFAAEA3BIFACOCAqCQAAATzWlzAQEA9KQSAAABAIkAAQAjgAKwoBN/AAFjbgGEArCgE40AAbCgE48AAWNyAQIAOzAAAAEAAAYAAQANEAYAE4ICoCAAABNxZXUBAgDzbREAAAEAAQABgAKwIBOtAAGgYAAAEwVvdmOBA6AQAAASxGFjcwEDAPTeDAAAAQABAAEAAQEA9KIQAAABAJwAAQATgAOgwAAAE9mwEAlwAAFucHUBgAKwMA/KAAGhYAAAFAxhZQGBArBAElEAAbBAElMAATQ2AQUA9CMQAAABAKkAAQBTgISgsAAAE5awMBLuAAEBoAQBCADz6xIAAAcAFwATgAKwQBGSAAFhZQEAAaAgAAAQP3IBBREAAAEAAPQ/EwAAAQBKAAEApwAAgIWgMAAAEJ+gEAAAEL+woBOhAAEJICEBCQGgQAAAE9Bl8u8TAAABAEOAhKAgAAAT3qCQAAATPqEwAAAT7hBJAAECAPTYEgAAAQCOAAEAE4ACsEASpgABaXkBAQKgMAAAEoOgQAAAEsNncjswAAABAAAHAAIAoBABADkRBQABgAKwABNZAAGwABNbAAE0NgEGAPQYFAAAAQAKAAEA8wICcnYBhBMAAPMCAWX0KxAAAAEAqAABAPOAAmR0560/ABKgMAAAEMCgQAAADPOw8BQVAAGwcBMGAAEwYWJjZWdoaWtsbm9wcnN0dXY7MAAAAQAA9wBFAEwFAAAeBgIAKAYDACsHAACKBwAArQcCAL0HAADPBwQA1QcAADYIAABeCAEAUwkBAFkJAQBlCQAAvgkBAMIJewAbDAEAKQwAADYMAABrDAAAdwwAAGUPAQCNDwEApA8AAAYQAAAvEAEAQRAAAE4QAQCTEAEAlxADAKgQAwDhEAEAAxEAACARAgBQEQAAaREAAIwRAACQEQEAoBEEADQSAABDEgEARhIAAFASAABhEgAAlBIBAKISAQCvEgEAthIBALwSAQC/EgAAwhIAANQSAADpEgEA/hIAAAMTAAATEwQALBMBAD0TAAA/EwEARRMBAFATAAB+EwAAihMCAJETAgCrEwEAtxMAAMoTAQDxEwEALBQAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/20b4057caa1a.js b/pr/323/docs/search.index/20b4057caa1a.js deleted file mode 100644 index 487635417..000000000 --- a/pr/323/docs/search.index/20b4057caa1a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9FURAAABAAEAAQABAQDzaxEAAAEAAQATgAJBeeMvrhxlcwEKAPM2EwAAAQABAAGAArAgE6sAAaBgAAATA292AYACoDAAABJOoAAAABLCY2TzgIUFEAY=") \ No newline at end of file diff --git a/pr/323/docs/search.index/20d3c333f469.js b/pr/323/docs/search.index/20d3c333f469.js new file mode 100644 index 000000000..9ac9425ed --- /dev/null +++ b/pr/323/docs/search.index/20d3c333f469.js @@ -0,0 +1 @@ +rn_("AQsBoEAAABOpZfLTEwAAAQABBQD0oREAAAEAAQABAAGDArAAEJMAAbBQEvUAAWR3E4ACoKAAABNlZXYBAgD0rhAAAAEAewEBAAEAAPSiDwAAAQDoAAQBI4ICsFAS0gABZXMBAgD0pBIAAAEAiQABAAEDAPTYEgAAAQCOAAEA84UCYnUBAgD0GhEAAAEAYAEBAPOAAmls8wADYnR1OjAAAAEAAAAAAA8AEAAAANQHYAhhCGIIYwjJD4IQJROgE+gT6RPrE/YT9xMaFB4UAQwA84cTAAABAAEAAYACsAAJbgABoCAAAA0IZG4BBgD0ohIAAAEAiQABAPMDAWL0rBAAAAEAewEBAJcGAACHsEAPoAABoHAAABI0sJAS0AABOAkDOzAAAAEAACIAEQA5BgMAmQcBAP0MAgADDQIAmQ8BAOsPAQDwDwEADRAGAFAQAAB9EAEABRMAACkTAABNEwAA3xMAAOoTAAAIFAAAFxQAAAEDAPS2EgAAAQA6AQEA84ACY213AQCAxbGAFCcAAUCBBAg=") \ No newline at end of file diff --git a/pr/323/docs/search.index/210ec896a061.js b/pr/323/docs/search.index/210ec896a061.js new file mode 100644 index 000000000..30a6ab376 --- /dev/null +++ b/pr/323/docs/search.index/210ec896a061.js @@ -0,0 +1 @@ +rn_("AYACoAAAABA9oAAAABA+NDYBgAOwABNZAAGwABNbAAGwcBPKAAE0NmEBBAD0GBQAAAEACgABACOBAqBAAAARA21wAQEAOzAAAAEAAAgAAwBgEgAAyRIAACUUBgCHAwACxqBAAAAQBqBgAAATfqCAAAASPUAHgAg7MAAAAQAAFgAIAKYPAQC6EAEA/xADAHARBgA4EwIAhBMAANoTAQAJFAEA8wACZXY6MAAAAQAAAAAAEQAQAAAA6QHqASYGBw1nD2gPgg+bDysQLBDUENUQURJSElMSVBKhE6ITAYECsHAQ/wABsHARAQABNDYjgAKwEAfEAAFucAEDArBAEqAAAaAwAAAS9GV39DEQAAAFALcAAQATgQKgcAAAEylnbyOAArBQD6YAAWR0VwEAAIWgEAAAEAaxIBPaAAERIQH0dxEAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/21bd1e52c0f9.js b/pr/323/docs/search.index/21bd1e52c0f9.js deleted file mode 100644 index fac8935a2..000000000 --- a/pr/323/docs/search.index/21bd1e52c0f9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA9MkHAADHB9cAAQABBQD0thIAAAEAigABAPMCAWP1AhAAAAEAvAABAAEAYwAEoDAAAAxIoHAAABF/ZW5zdvMgEAAAlQD+AQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/21cb922259a9.js b/pr/323/docs/search.index/21cb922259a9.js new file mode 100644 index 000000000..86a44d648 --- /dev/null +++ b/pr/323/docs/search.index/21cb922259a9.js @@ -0,0 +1 @@ +rn_("AYACoFAAABDqoFAAABIiYXUBgwKxABQhAAGw0BQTAAFrbQEEAaCgAAAULHfyHBQAAAEAAQsA85ETAAABAAEAI4ACoIAAABO3ZW9jAwOgYAAAE1BlbnABOQwAACOAAqBAAAAQem1wAQoA83QTAAABAG0AE4ACohAAABQsbG/3AAAAhaEAAAAT5ASBAzswAAABAAA7AAQAMAYFALIHAAACCDMAKhEAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/21f7fe292e8a.js b/pr/323/docs/search.index/21f7fe292e8a.js deleted file mode 100644 index 963ec6d10..000000000 --- a/pr/323/docs/search.index/21f7fe292e8a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9FMSAAABAJoAAQABCAD0/xIAAAEAgwABADMBA7BAEtQAAWFiZTowAAABAAAAAAAJABAAAACWB5cH9wz4DHoP5g8wEDEQLhLxEgEAADswAAABAAAeAA4ATAUBAP8FCAB5CQEAQAwBAOkMAQB0DgAAbA8CAIIPAACgDwEAiRABAJwQAAAxEQAAiBEBAI0RAAABgQKwABEhAAGwABEjAAE0NiOAArCAE/IAAW9wAQABsEAS0AABZfMsEQAAPQEBALcBAICFoGAAABDIEAEL") \ No newline at end of file diff --git a/pr/323/docs/search.index/2207279438e6.js b/pr/323/docs/search.index/2207279438e6.js new file mode 100644 index 000000000..d3218137b --- /dev/null +++ b/pr/323/docs/search.index/2207279438e6.js @@ -0,0 +1 @@ +rn_("AYICsEASUQABsEASUwABNDYBBgD0GBQAAAEACgABAPMCAXIBhBMAAPMDAWX0KxAAAAEAqAABAFOAA7CwE6EAAWFlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/225a0e027688.js b/pr/323/docs/search.index/225a0e027688.js deleted file mode 100644 index a9dd0c6e5..000000000 --- a/pr/323/docs/search.index/225a0e027688.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQACoDAAABC+oJAAABMHMGj1nhEAAHMBAQABAAEAE4ACoDAAAA9/ZHIjAISwEAlKAv+wIAwcAPagMAAAD6MSABj1EQYAAC8AAQABAAEAAQIA9BYUAAABAAoAAQABAgDzGxAAAAEAAQDzgAJpcgEBArBAEQQAAaBQAAAS8WVz9SYMAABJAqsCAQBgAgELAPYWEwAAAQCfAFMAFgABAPOAAmx004CEoJAAABLLAUQE") \ No newline at end of file diff --git a/pr/323/docs/search.index/22a0b3614808.js b/pr/323/docs/search.index/22a0b3614808.js new file mode 100644 index 000000000..ba6bc5fb9 --- /dev/null +++ b/pr/323/docs/search.index/22a0b3614808.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABEABQAkBgAARgwAAO0MAAC5Dw0AFREAAAGEArAAESMAAbAAESUAATQ2FwgAAIigIAAAEX2gMAAAEOmg4AAAEySgMAAAEjWwcA+TAAGgIAAABi3LwAg7MAAAAQAAAQEVAI8BAAAZBgAALQYAAEwJAABSCQAAcAkBACYMAABhDAEAgwwBAPAMAAAXDd0Afw8AAJAPAQB6EAAAOBEAAHkSAQBHEwUAZBMAAOMTAAD4EwUAExQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/22a72c827db8.js b/pr/323/docs/search.index/22a72c827db8.js new file mode 100644 index 000000000..0dc21dfff --- /dev/null +++ b/pr/323/docs/search.index/22a72c827db8.js @@ -0,0 +1 @@ +rn_("AQ0A9VMTAAABAAEAcAABAAGAAqEQAAATjLBwE6EAAW5yAQcA9F0RAAABAAEAAQAzAASgIAAACXyw4BOBAAExZWly9BEEAACgA6wBIAMBAgD0rhAAAAEAewEBAAEDAPQaEQAAAQBgAQEA8wACYnUByQ8AAAEDAPSsEAAAAQB7AQEA84QCZHMBAgDzXwkAAAEAVADzgAJpdAEYAPMpFAAAAQABAAGAArCwE1EAAaDAAAAS/mV184ECY20BDQGwQBPRAAFl9ZkTAAABAAEAAQABABOCArAAEMQAAWtzAQQA9FUQAAABAAEAAQABgQKgIAAAEL+wQBLuAAFlafOCAmV0AQQBoGAAABJQcvQ3DAAAAQCPAwEA84ADYWVvAQkA9nkSAAABAOoAfwAwAAEAE4ACsGAQbQABZG4BBgD1lxAAAAEAAQABAGkCAQMBoHAAABKsZ/S+BwAAAQDzAQEAAQkA8+gTAAABADUAAYECsIAT9gABoGAAABQaZHMXAQCIhbDQFBwAAaBAAAATzLBgE6AASwKQCgEEAPQ/EwAAAQBKAAEAU4ADsJAUFQABZWhvAQABoEAAABEnZQF7DwAAI4ECsFAQ0QABa3R3/AEAjaGQAAAUDLCwEroAAaCgAAASeP2BDzswAAABAAAZAAgAcQAAAHcAAwDqBQEAnAcDAIcJAwBVDAcAARABACcSAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/24a80ee69d19.js b/pr/323/docs/search.index/24a80ee69d19.js new file mode 100644 index 000000000..da6ad633a --- /dev/null +++ b/pr/323/docs/search.index/24a80ee69d19.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABNYcwGdEAAAAYACsDAQMgABsFAQNAABYWkBgAKgsAAAE96gMAAAEMlsbhcBAACFoCAAABIsoCAAAAlMsIATqwABEJEEOjAAAAEAAAAAABAAEAAAACcFXAVdBV4FXwVgBWEFtglpD2oPyhJ8E4MTjxOQE6cTqBMBAgA6MAAAAQAAAAAACgAQAAAAoAmhCdAP7BLtEhgTGRO4EwsUIRQiFAEAADswAAABAAAGAAIABBMAAGkTBQABAgGwMBLaAAFjOjAAAAEAAAAAABMAEAAAAAANgQ8UEBUQFhAbEBwQIBAhELMQJxErESwRLRGdEdAS0RKpE84TzxMBggKwMBOhAAGwQBETAAFhZQEBAbBAE9EAAWX1mRMAAAEAAQABAAEAE4ACoBAAAAwqYW/HBwAAx7BAE9cAAaCAAAASJwBMiAnyVwgAANcDAYACoIAAABLzoJAAABLNYWsBAwDzSxIAAAEAAQAjgISxIBQfAAGgAAAACYCgMAAAD4gEBAYBCAD1JxQAAAEAAQABAAEAE4oCsEAUAgABYXKTAISgwAAAEsmhAAAAE4wBQAby2gcAAAEAAQMBoEAAABGXZfMKDQAAswMBAAEFAPQ0EwAAAQABAAEAU4ADsAAS7gABZXN0AQYA9A0UAAABAAEAAQDzCAFt9DkSAAABAAEAAQABAgA7MAAAAQAABQABAPgTBQDzDQFtOzAAAAEAAAUAAQC5EwUAAQQA9xINAAABAC8FDAABAIMAAQABAwGAEvcAEOxzOzAAAAEAAAUAAQDcEgUAI4ICoJAAABPNaXMBgAKgsAAAE5awMBLuAAFudAGAAqCAAAATCbBAEZIAAWFlAYACoBAAABC/oAAAABEFbnIBCQGgQAAAE9Bl8u8TAAABACOAAqCQAAATPmlsxyMAAAqgMAAAEMCwUBKmAAGw8BQVAAGwcBMGAAGg0AAAE9kwYmNlaGlrbm92OzAAAAEAABkACwApDAAAlxADAKARBAADEwAAExMDAEUTAQCMEwAAkRMCAKsTAQC3EwAAyhMBAAECAqCwAAATw6CwAAATxGNz83QQAAABAF4AAQECsFARkgABoMAAABOWaGnyRRMAAAEA84ACZWkBAQGgkAAAEwNz9FkQAAABAAEAAQABAwGwABCRAAFzAQMQAADzgANoaXQBAQGgUAAAEpFjAdgMAAABBQGwgBPKAAF2OzAAAAEAAAcAAgBwEQQAOBMCAAEBAPQjEAAAAQCpAAEA84ADY2Rs92UPANGwkBIlAAGgUAAAELihAAAAE/6goAAAEyWgEAAAB7FAP78pOzAAAAEAADcAFgBPBQEADwYCAHwHAgAXDAMA2wwBAHQPAgCDDwAAqQ8AANEPAQDWEAgALhEAAGISAQBrEgEAbxIBAMUSAQDiEgQAMhMBADsTAQA+EwAAsRMEAP4TAAAbFAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/24eee7d39d3b.js b/pr/323/docs/search.index/24eee7d39d3b.js deleted file mode 100644 index f7a5b128e..000000000 --- a/pr/323/docs/search.index/24eee7d39d3b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBsAASpgABczswAAABAAALAAIAGhMHAAIUAwBHAAAFhqCAAAAS+7EAFA8AAbBAEmkAAbCAFB0AAbBQEqgAARUAFvRwDgAAAQABAAEAQwCEoCAAAAl+oNAAABMGoDAAAAldFQAE89UBAAABAP8F") \ No newline at end of file diff --git a/pr/323/docs/search.index/252df03c9a7b.js b/pr/323/docs/search.index/252df03c9a7b.js deleted file mode 100644 index 75508c4b3..000000000 --- a/pr/323/docs/search.index/252df03c9a7b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9IwJAAABAFwHAQABCQGggAAAFBlmAQ4TAAABAgD1fRAAAAEAAQA5AgEA8wACaXU6MAAAAQAAAAAABwAQAAAAWxB2Ek8TUBN/E4ATnBOdEwEIAPWvEwAAAQABAAEAAQDzAgFm9UAQAAABAAEAAQABABOAArCgE/IAAXJ08wACYXPzbRAAAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/254b4b1e845b.js b/pr/323/docs/search.index/254b4b1e845b.js deleted file mode 100644 index 07da323bd..000000000 --- a/pr/323/docs/search.index/254b4b1e845b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsIATqQABcPKlEwAAAQBDBQOgIAAAEQOgQAAAEkBkZXPzpw8AABwDAQABAgA7MAAAAQAAEwABANEPEwATgYSgUAAAELKgEAAADDugIAAADPQcQAAzgAOwUBL5AAFhZm4=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2560f8defded.js b/pr/323/docs/search.index/2560f8defded.js new file mode 100644 index 000000000..a53003154 --- /dev/null +++ b/pr/323/docs/search.index/2560f8defded.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAJcPmA97EHwQCBGUEZURlhKXEhEUEhTzAQF58n8TAAABACMAA6BAAAARJ6BQAAATyWVvc/J7DwAAsgI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/256fee405b93.js b/pr/323/docs/search.index/256fee405b93.js new file mode 100644 index 000000000..03ded17ec --- /dev/null +++ b/pr/323/docs/search.index/256fee405b93.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABLKc/SDDwAAuAMBAAIAAQECsGATrQABsEAT5gABZHQ7MAAAAQAADQAGAIQSAwDnEgEAThMBAGUTAQDcEwEAABQBAAEBhLBwEZ4AAbCAEmIAAaBgAAAS/oAS9wAQ7AAwAzswAAABAAALAAIARQkFANwSBQCjgISgsAAAEz2xcBQfAAEIQIIBAISgcAAAEiygcAAAELOxABOtAAGwABK+AU4CIQHy8AUAAAEAAQEAOzAAAAEAAAYAAQAlFAYA8wYBZfTaEwAAAQAuAAEAEwEDsEAUAgABoGAAABODYXJ1AScQAAB3BAAAh6AQAAAGI7AwErEBNKBAAAAQ1hHBDDswAAABAAAGAAIABBMAAGkTBQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/25a2665de949.js b/pr/323/docs/search.index/25a2665de949.js new file mode 100644 index 000000000..3b11a1dc9 --- /dev/null +++ b/pr/323/docs/search.index/25a2665de949.js @@ -0,0 +1 @@ +rn_("AQAA9K4QAAABAHsBAQAjgQKgYAAAEr9iaQEAAPSiDwAAAQDoAAQBI4ICsFAS0gABZXMBiQKwIBPcAAGgMAAAE+VpcAECAPSkEgAAAQCJAAEAAQMA9NgSAAABAI4AAQDzhQJidQECAPQaEQAAAQBgAQEA84ACaWy3AQAAhaAgAAASrBIBDDowAAABAAAAAAAUABAAAADUB2AIYQhiCGMIKwxdDF4MXwxgDMkPghAlE6AT6BPpE+sT9hP3ExoUHhQBBQDzhxMAAAEAAQBDhgOwkBQAAAGgAAAAEkZiZHcBgAKwAAluAAGwIA0IAxJkbgEDAPOgDwAAAQD7AAECAPYwDAAAAQAvAwEApQMBAAEAADswAAABAAAGAAIABBMAAGkTBQAjgQKwQBPXAAFlc1OAA7AACYkAAWV4eQGAA6BAAAAT/rAgEqAAAbAACYcAAWVyeAGAAqBAAAAM87BAD8oAAWdsFw0AAMiwwBMwAAGgEAAAERagMAAAE32wQAzrAAEARqALOjAAAAEAAAAAAAkAEAAAAOgF6QXxDPIMBg2cD/UP9g+cEp0SAQYA9KISAAABAIkAAQDzAwFi9KwQAAABAHsBAQC3DAAAiaBwAAASNLCQEtAAAbBgD+0AAaAwAAAS9DiJEzowAAABAAAAAAAtABAAAAA5BjoGOwY8BpkHmgdPCVAJUQmBCYIJNQz9DP4M/wwDDQQNBQ2ZD5oP6w/sD/AP8Q8NEA4QDxAQEBEQEhATEDEQNhBQEH0QfhDtEO4QgxIFEykTTRPfE+oTCBQXFA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/25b74d0e760c.js b/pr/323/docs/search.index/25b74d0e760c.js deleted file mode 100644 index 35f1aad82..000000000 --- a/pr/323/docs/search.index/25b74d0e760c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcAOzAAAAEAAHwAAgCZEAAApBF7AAEDADswAAABAAB7AAEA8w17AAECADswAAABAAAHAAEAVAwHAPMAA2JpdDswAAABAAB8AAIAKQUAAGIFewA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/25b9a7be65ac.js b/pr/323/docs/search.index/25b9a7be65ac.js new file mode 100644 index 000000000..c3210d0f7 --- /dev/null +++ b/pr/323/docs/search.index/25b9a7be65ac.js @@ -0,0 +1 @@ +rn_("AYADsAATWQABsAATWwABsHATygABNDZhAQQA9BgUAAABAAoAAQAjgQKgQAAAEQNtcAEBADswAAABAAAIAAMAYBIAAMkSAAAlFAYAhwMAAMagQAAAEAagYAAAE36ggAAAEj1AB4AIOzAAAAEAABYACACmDwEAuhABAP8QAwBwEQYAOBMCAIQTAADaEwEACRQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/266afabfc690.js b/pr/323/docs/search.index/266afabfc690.js deleted file mode 100644 index 95677b044..000000000 --- a/pr/323/docs/search.index/266afabfc690.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkBoIAAABQZZgEOEwAAAQIA9X0QAAABAAEAOQIBAPMAAml1OjAAAAEAAAAAAAcAEAAAAFsQdhJPE1ATfxOAE5wTnRMBCAD1rxMAAAEAAQABAAEA8wIBZvVAEAAAAQABAAEAAQATgAKwoBPyAAFydPMCAmFz820QAAABAAEAAQQBsEATzwABZfWXEwAAAQABAAEAAQABAgA7MAAAAQAACwACAEUTBQD2EwUA8wMBbTswAAABAAANAAQA1wwAABARAABvEgUAtxMFAPOBAmNl84ACYWU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2670b688dca6.js b/pr/323/docs/search.index/2670b688dca6.js deleted file mode 100644 index a23cd92a2..000000000 --- a/pr/323/docs/search.index/2670b688dca6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsAARIQABsAARIwABNDYBBAGgoAAAFCp38hoUAAABAAELAPOPEwAAAQABACOAAqCAAAATtWVvYwIDoGAAABNOZW5wATgMAADzAAJhdgFHBwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/26e84f065723.js b/pr/323/docs/search.index/26e84f065723.js deleted file mode 100644 index e8386cae1..000000000 --- a/pr/323/docs/search.index/26e84f065723.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYECoBAAABLCoHAAABMDYWMBhwKwABNXAAGwABNZAAE0NlMAhKCQAAAS6bCwE30AAYEACfWJBwAAMwCDCA8BGQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2713c22c8bb8.js b/pr/323/docs/search.index/2713c22c8bb8.js new file mode 100644 index 000000000..d52a6dc98 --- /dev/null +++ b/pr/323/docs/search.index/2713c22c8bb8.js @@ -0,0 +1 @@ +rn_("AQQA9ugTAAABAA0AAQAjAAQAAQMA80sSAAABAAEAI4ECsSAUHwABY2sTgAKgMAAAE6luclMAA6BAAAAMNWVvcvTMEAAAaQGeAQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/27198464c776.js b/pr/323/docs/search.index/27198464c776.js deleted file mode 100644 index eee3c6b9c..000000000 --- a/pr/323/docs/search.index/27198464c776.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9VARAAABAAEAAQABAAGAArBgEpgAAaAgAAAQom95AQABoMAAABOoafTpEgAABwAXABsAAQYA9AsUAAABAAEAAQABAgD0oBAAAAEAnAABAAECAaBAAAASwXI7MAAAAQAABwACAJ4QAQA3EQUA84ACYW9nAwAAhrCAEsAAVbCgEzAAAQwwBjswAAABAAAIAAMAMQwAAGMRAwA3EgMA84ICZWkjAAKggAAAFBlmaPJNCQAAwQkBAAKwEAwaAAGwMBO/AAFlcPSTDwAAAQDiAAEAAQADsEATjQABoUAAABPxoFAAABGKYmNmAXEHAADzgAJtbgEBA6CwAAATS7BAEFwAAaBgAAARNmNldPRYBwAApAhHAv0AAQUAOzAAAAEAAAUAAQBiEgUAAQcA9eASAAABAAEAAQABACOAAqBQAAARBmFv8wCFIEkCOzAAAAEAAAgABACvBwAA4QcFAAcNAAA/EQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/271cda0a044e.js b/pr/323/docs/search.index/271cda0a044e.js deleted file mode 100644 index d2a6641da..000000000 --- a/pr/323/docs/search.index/271cda0a044e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9DIRAAABAAEAAQABAgA7MAAAAQAADAAFACMMAQA1EAUAWhIBALASAQDCEwAA84ACZm4BAQA7MAAAAQAABwACAFgPBQDYEgEA84ACZG4BAQDzkwcAAAEAAQABhgKgAAAAEtKgAAAAEtNkcvMBAXYB+BIAAEMAA7CAEm0AAbCwE+8AAWtscjswAAABAAAMAAMAigcIAAkNAQD1DwEAAQIA9VgIAAABAAEAAQABAPMAhBRBAAFtAAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/2737d083eea6.js b/pr/323/docs/search.index/2737d083eea6.js new file mode 100644 index 000000000..e160508b5 --- /dev/null +++ b/pr/323/docs/search.index/2737d083eea6.js @@ -0,0 +1 @@ +rn_("AQUA9AsNAAABAOsCAQABAAD0cgwAAAEAAQBmAAECADowAAABAAAAAAAJABAAAACQD5EPehA4EXkSehJkE+MTExQUFMcBAACGsDAJcAABoEAAABGXsIARfgABGEAHOjAAAAEAAAAAABcAEAAAAMsHzAdYCHwJqwmsCQoNfQ9+D50PQBBHEEgQSRBKEIUQhhC9EL4QIhJiE2MTjROOEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/27677e8fce37.js b/pr/323/docs/search.index/27677e8fce37.js deleted file mode 100644 index b754ef56e..000000000 --- a/pr/323/docs/search.index/27677e8fce37.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsIASyQABdvS5CQAAAQC2CQEAE4ACsDAQrwABbG4BgAKgAAAAEDugAAAAEDw0NvMDAXYBBQ0AABMAAqBAAAARLGFz90cMAABfA1wBMgEBAAEAkQEBAgD0ohIAAAEAiQABAAEDAPTWEgAAAQCOAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAArAetB64Hiw+MDy0QLhBMEE0Q3xDgEI4RjxGgEqESrRKuEr0SKhMrE/OAAmR0E4ACoCAAAA0PZXIBBwGgQAAAE6dl8tETAAABAAEHAPSmEAAAAQABAAEAAQAA9awJAAABAAEAAQABACOAAqCgAAAT/GZwAQMBoJAAABN8ZfJlDwAAAQDXAAAAhaBQAAAQI7FAFCMAAYEpAPLtBQAAAQDzgAJsdJcBAICFsTATqQABsRAT0wABEUUA") \ No newline at end of file diff --git a/pr/323/docs/search.index/278470046fa3.js b/pr/323/docs/search.index/278470046fa3.js deleted file mode 100644 index 1d6d8b5c0..000000000 --- a/pr/323/docs/search.index/278470046fa3.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9FIJAAABAPsJ3AAXAAAAyaBAAAAQt7BgE9MAAaAQAAAMkrBQE3QAAbAwERoAAaAAAAAMU7EgE/IAAbBAELUB/gBOsAk6MAAAAQAAAAAAEQAQAAAAZwloCXwJfQkgDCEMIgwrDCwM4AwsElUSVhJXEv8SABODE4QT") \ No newline at end of file diff --git a/pr/323/docs/search.index/27bc89bd5eae.js b/pr/323/docs/search.index/27bc89bd5eae.js deleted file mode 100644 index 2150f3df1..000000000 --- a/pr/323/docs/search.index/27bc89bd5eae.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA98QQAAABAAEApwIBAH4ABQABAQKgQAAAE9egMAAAEMplaPVUCQAALgDtAgEADgUBAgD0dREAAAEAAQABABOBArAgEXkAAWFz84ACbm8BAgD0VxAAAAEAAQABABMAAqDQAAAT3GNz8s8PAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/281e2c9c5d7a.js b/pr/323/docs/search.index/281e2c9c5d7a.js new file mode 100644 index 000000000..4bfdcf689 --- /dev/null +++ b/pr/323/docs/search.index/281e2c9c5d7a.js @@ -0,0 +1 @@ +rn_("AQQBoFAAABPJcwEtEgAAAYICsEASUQABsEASUwABNDYBBgD0GBQAAAEACgABAPMCAXIBhBMAAPMDAWX0KxAAAAEAqAABAFOEA7CwE6EAAWFlaWOAA6BQAAAT/2NldA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/28d0ee3283f9.js b/pr/323/docs/search.index/28d0ee3283f9.js deleted file mode 100644 index 6ab5c018c..000000000 --- a/pr/323/docs/search.index/28d0ee3283f9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQADoKAAABPCoCAAABMGsDARiAABZWl08nsSAAABAAEDADswAAABAAAGAAEABxEGAAEIAPRtEwAAAQB+AAUAEwEDsJATzAABsFASvAFOZ2xwOzAAAAEAABIACQAVEAIAixEBAKsSAQDMEgEA/RIBAF4TAQCSEwEAoRMBAN4TAQABgAKwMBGEAAGwMBGGAAFydgECAbAAEkEAAWQ7MAAAAQAABQACAIARAwBrEgEAI4ECoJAAABPBaW8BBwD03RMAAAsAHgAPAHcLAAGIsFASQwD9sGASugABlQkDOjAAAAEAAAAAAA0AEAAAAOIM4wzkDHUOdg5yEHMQrhDREDIRMxE0ETURshIjgAKgEAAADOFubw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/28e355da3bd0.js b/pr/323/docs/search.index/28e355da3bd0.js new file mode 100644 index 000000000..fecc58cbe --- /dev/null +++ b/pr/323/docs/search.index/28e355da3bd0.js @@ -0,0 +1 @@ +rn_("AQAA9FMJAAABAPwJ3AAXAAAAyaBAAAAQubBgE9UAAaAQAAAMk7BQE3YAAbAwERwAAaAAAAAMVLEgE/QAAbBAELcB/gBOsAk6MAAAAQAAAAAAEQAQAAAAaAlpCX0JfgkhDCIMIwwsDC0M4gwuElcSWBJZEgETAhOFE4YT") \ No newline at end of file diff --git a/pr/323/docs/search.index/2912fbd679c0.js b/pr/323/docs/search.index/2912fbd679c0.js new file mode 100644 index 000000000..99c44211b --- /dev/null +++ b/pr/323/docs/search.index/2912fbd679c0.js @@ -0,0 +1 @@ +rn_("AQADoKAAABPEoCAAABMIsDARigABZWl08n0SAAABAAEDADswAAABAAAGAAEACREGAAEIAPRvEwAAAQB+AAUAEwEDsJATzgABsFASvgFOZ2xwOzAAAAEAABIACQAXEAIAjREBAK0SAQDOEgEA/xIBAGATAQCUEwEAoxMBAOATAQABgAKwMBGGAAGwMBGIAAFydgECAbAAEkMAAWQ7MAAAAQAABQACAIIRAwBtEgEAI4ECoJAAABPDaW8BBwD03xMAAAsAHgAPAHcLAAGIsFASRQD9sGASvAABlQkDOjAAAAEAAAAAAA0AEAAAAOQM5QzmDHcOeA50EHUQsBDTEDQRNRE2ETcRtBIjgAKgEAAADONubw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/291d7178c4eb.js b/pr/323/docs/search.index/291d7178c4eb.js new file mode 100644 index 000000000..e5930c2ee --- /dev/null +++ b/pr/323/docs/search.index/291d7178c4eb.js @@ -0,0 +1 @@ +rn_("AYACoIAAABJgsHATngABYXMBAAGggAAAFBtm8k4JAADCCQEHAPXiEgAAAQABAAEAAQAjgAKgUAAAEQhhb/MAAmZzOzAAAAEAAAgABACwBwAA4gcFAAkNAABBEQAA84ACZHk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/29233aa98cf2.js b/pr/323/docs/search.index/29233aa98cf2.js new file mode 100644 index 000000000..285a2298c --- /dev/null +++ b/pr/323/docs/search.index/29233aa98cf2.js @@ -0,0 +1 @@ +rn_("AYECoBAAABLEoHAAABMFYWMBhwKwABNZAAGwABNbAAE0NlMAhKCQAAAS67CwE38AAYEACfWKBwAAMwCECA8BGQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/29912ff817d4.js b/pr/323/docs/search.index/29912ff817d4.js deleted file mode 100644 index 8cede019b..000000000 --- a/pr/323/docs/search.index/29912ff817d4.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBoFAAABPHcwErEgAAAYICsEASTwABsEASUQABNDYBBgD0FhQAAAEACgABAPMCAXIBghMAAPMDAWX0KRAAAAEAqAABAFOEA7CwE58AAWFlaWOAA6BQAAAT/WNldA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/29ffaf5f711f.js b/pr/323/docs/search.index/29ffaf5f711f.js deleted file mode 100644 index cf078824c..000000000 --- a/pr/323/docs/search.index/29ffaf5f711f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYQCoCAAABDJoDAAABExaXQBAQA7MAAAAQAAfAACANQHAADBCXsA8wACZWz1PwUAAJkCDwkoAgEAAQIAOzAAAAEAAN4AAgAlDAAAFQ3dAMcAAACFsLASvgABoBAAABM7sDANEwABFDACOzAAAAEAAAoBCwC1AQYAFwYAAEQG3QCiBwcA8A8CAEARDQBoEQIALxIBAKgSAQDzEgEAhRMCAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2a155524c2da.js b/pr/323/docs/search.index/2a155524c2da.js new file mode 100644 index 000000000..0390af13d --- /dev/null +++ b/pr/323/docs/search.index/2a155524c2da.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAcAAQCiCQcAAQIA9/sPAADQAIgCAQABAHAAAQATgAKwMBCMAKdpdPMAAXPy9BMAAAEAM4ADsRATcgABZG5w") \ No newline at end of file diff --git a/pr/323/docs/search.index/2a2e9d6f638a.js b/pr/323/docs/search.index/2a2e9d6f638a.js deleted file mode 100644 index 5dcc70e30..000000000 --- a/pr/323/docs/search.index/2a2e9d6f638a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoAAAABA7oAAAABA8NDYBgAOwABNXAAGwABNZAAGwcBPIAAE0NmEBBAD0FhQAAAEACgABACOBAqBAAAARAW1wAQEAOzAAAAEAAAgAAwBeEgAAxxIAACMUBgCHAwACxqBAAAAQBKBgAAATfKCAAAASO0AHgAg7MAAAAQAAFgAIAKQPAQC4EAEA/RADAG4RBgA2EwIAghMAANgTAQAHFAEA8wACZXY6MAAAAQAAAAAAEQAQAAAA6AHpASUGBQ1lD2YPgA+ZDykQKhDSENMQTxJQElESUhKfE6ATAYECsHAQ/QABsHAQ/wABNDYjgAKwEAfDAAFucAEDArBAEp4AAaAwAAAS8mV39C8QAAAFALcAAQATgQKgcAAAEydnbyOAArBQD6QAAWR0VwEAAIWgEAAAEASxIBPYAAERIQH0dREAAAEAAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2a977e08489a.js b/pr/323/docs/search.index/2a977e08489a.js new file mode 100644 index 000000000..734417ec5 --- /dev/null +++ b/pr/323/docs/search.index/2a977e08489a.js @@ -0,0 +1 @@ +rn_("AQgAOjAAAAEAAAAAAAcAEAAAAM4SzxJgE2ETlBOVE+AT4RMjgAKwUBLCAFVmbgEAAbBAEp4AAWU7MAAAAQAADQACADsHDAC0EAAAI4ACoDAAAAwsYWUzgISwUBMRAAGwEBOtAAEIYAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2b09faf5f111.js b/pr/323/docs/search.index/2b09faf5f111.js deleted file mode 100644 index 934598a00..000000000 --- a/pr/323/docs/search.index/2b09faf5f111.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQACoCAAABMGsDARiAABaXTyexIAAAEAAQMAOzAAAAEAAAYAAQAHEQYAAQgA9G0TAAABAH4ABQATAQOwkBPMAAGwUBK8AU5nbHA7MAAAAQAAEgAJABUQAgCLEQEAqxIBAMwSAQD9EgEAXhMBAJITAQChEwEA3hMBAAGAArAwEYQAAbAwEYYAAXJ2AQQBsAASQQABZDswAAABAAAFAAIAgBEDAGsSAQC3BQABh7BQEkMA/bBgEroAAZQJAzswAAABAAAKAAUA4gwCAHUOAQCuEAAAMhEDALISAAAjgAKgEAAADOFubwEDAPMSEAAAAQABACOAAqAAAAAMKGRyYwGEoFAAABEvoDAAABBKFAEQ8vsHAAABABOAArBgEmAAAWNnE4ADoLAAABOKoEAAAA50ZW9z") \ No newline at end of file diff --git a/pr/323/docs/search.index/2b2e5db4b206.js b/pr/323/docs/search.index/2b2e5db4b206.js new file mode 100644 index 000000000..230cd3bff --- /dev/null +++ b/pr/323/docs/search.index/2b2e5db4b206.js @@ -0,0 +1 @@ +rn_("AYUCsHATigABsDAS5wABZXMTAAOwQA+JAAGgYAAAETJiZXM7MAAAAQAAEgAEACEGAQAwBwoA/w8BAFEQAwABAgDzLAYAAIEJAQABAwD1gQ8AAJoAAQAEAAEAAQoBoEAAABPQZfLvEwAAAQAjAAOgMAAAEMCgkAAAEwkwY2j1oBEAAHMBAQABAAEA84ACZHLnAAAAh7AQCUsC/7AgDB0A96AwAAAPpbAAB54AARJBODswAAABAAAQAAYAfAACABIGAABBBgMA/gcDAD8IAwAoDAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/2b35726b25dd.js b/pr/323/docs/search.index/2b35726b25dd.js deleted file mode 100644 index 2ba6b0a8f..000000000 --- a/pr/323/docs/search.index/2b35726b25dd.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9KwQAAABAHsBAQABAAD0oA8AAAEA6AAEASOCArBQEtAAAWVzAQIA9KISAAABAIkAAQABAwD01hIAAAEAjgABAPOFAmJ1AQIA9BgRAAABAGABAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAADTB18IYAhhCGIIxw+AECMTnhPmE+cT6RP0E/UTGBQcFAEMAPOFEwAAAQABAAGAArAACW0AAaAgAAANBmRuAQYA9KASAAABAIkAAQDzAwFi9KoQAAABAHsBAQCXBgAAh7BAD54AAaBwAAASMrCQEs4AATgJAzswAAABAAAiABEAOAYDAJgHAQD7DAIAAQ0CAJcPAQDpDwEA7g8BAAsQBgBOEAAAexABAAMTAAAnEwAASxMAAN0TAADoEwAABhQAABUUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2b47a005bd3d.js b/pr/323/docs/search.index/2b47a005bd3d.js new file mode 100644 index 000000000..439933295 --- /dev/null +++ b/pr/323/docs/search.index/2b47a005bd3d.js @@ -0,0 +1 @@ +rn_("AQMA9GMJAAABABEDAQABAwDzcgwAAAEAAQDzgAJpbwEBAbBQELEAAWP0wAcAAAEAAQDzASOBAqBQAAAQc2NuAQQAOzAAAAEAAAcAAwBqEQIA9RIBAIcTAgABAQA6MAAAAQAAAAAABwAQAAAAjRGOEa0SrhL/EgAToxOkE/MJAWkBsBAAANcCAACHsMAS7gABoFAAAA9roRAAABPJFAEXOzAAAAEAACMAAQCQASMA") \ No newline at end of file diff --git a/pr/323/docs/search.index/2b89b9636a42.js b/pr/323/docs/search.index/2b89b9636a42.js new file mode 100644 index 000000000..e3522a283 --- /dev/null +++ b/pr/323/docs/search.index/2b89b9636a42.js @@ -0,0 +1 @@ +rn_("AQcAOzAAAAEAAHwAAgCbEAAAphF7AAEDADswAAABAAB7AAEA9Q17AAECADswAAABAAAHAAEAVQwHAPMAA2JpdDswAAABAAB8AAIAKgUAAGMFewA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2c835af2195f.js b/pr/323/docs/search.index/2c835af2195f.js new file mode 100644 index 000000000..bfc5732c5 --- /dev/null +++ b/pr/323/docs/search.index/2c835af2195f.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHwAAgDVBwAAwgl7AAGEArBwE6cAAaBAAAAQvGlzAQcA85UQAAABAA8BI4ACoLAAABJgYXQBAAOwMAl1AAGwMBMRAAGgIAAAB9ZhZG8B6wEAADcDAICGoMAAABOfsAATdgABAJgV") \ No newline at end of file diff --git a/pr/323/docs/search.index/2cd2c4b18c64.js b/pr/323/docs/search.index/2cd2c4b18c64.js new file mode 100644 index 000000000..6ae2fd00f --- /dev/null +++ b/pr/323/docs/search.index/2cd2c4b18c64.js @@ -0,0 +1 @@ +rn_("AQEA98YQAAABAAEApwIBAH4ABQABAgD0WRAAAAEAAQABABOAAqDQAAAT3mNzU4ADoHAAABPZZWlvQ4ADoCAAAA+bsAAHtQABZGx0") \ No newline at end of file diff --git a/pr/323/docs/search.index/2ce4b4057c53.js b/pr/323/docs/search.index/2ce4b4057c53.js deleted file mode 100644 index f017607b1..000000000 --- a/pr/323/docs/search.index/2ce4b4057c53.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQ8A9JITAAABAEsAAQABBAD0KxAAAAEAsQABAAECADowAAABAAAAAAAJABAAAAB8EX0RfxKAElwTXROtE64THRQeFPMAAW/yvwkAAAEAU4ADsQAT7wABaG9w84ACc3Q=") \ No newline at end of file diff --git a/pr/323/docs/search.index/2d2960f9b8d4.js b/pr/323/docs/search.index/2d2960f9b8d4.js deleted file mode 100644 index 8b491b5d4..000000000 --- a/pr/323/docs/search.index/2d2960f9b8d4.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoGAAABN7oKAAABNvYWkBCwD0ERMAAAEAAQABAAEEAPRPEAAAAQABAAEAAQQA9KYQAAABAAEAAQDzgAJscvMAA2NkcwGdEAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/2d73282266f1.js b/pr/323/docs/search.index/2d73282266f1.js new file mode 100644 index 000000000..107145e35 --- /dev/null +++ b/pr/323/docs/search.index/2d73282266f1.js @@ -0,0 +1 @@ +rn_("AQIA9VIRAAABAAEAAQABAAGAArBgEpoAAaAgAAAQpG95AQABoMAAABOqafTrEgAABwAXABsAAQMAOzAAAAEAAAUAAQAKEwUAAQYA9A0UAAABAAEAAQABAgD0ohAAAAEAnAABAAECAaBAAAASw3I7MAAAAQAABwACAKAQAQA5EQUA84ACYW9nBwAAh7CAEsIAVbCgEzIAAQwwBzswAAABAAAOAAQARQQFADIMAABlEQMAORIDAKOAhKDwAAATQqBQAAAP/pQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2dcb4666ca26.js b/pr/323/docs/search.index/2dcb4666ca26.js new file mode 100644 index 000000000..f9168701e --- /dev/null +++ b/pr/323/docs/search.index/2dcb4666ca26.js @@ -0,0 +1 @@ +rn_("AYACsHASJQABsCAUFQABaW8BAAGw8BQVAAFr9YwTAAAFAAEAAQAkAPMFAWTyYhIAAAEAAQIA838QAAABAAEA8wABaTowAAABAAAAAAAHABAAAABdEHgSURNSE4ETghOeE58TI4MCsLAT9AABYXMBgQKgoAAAEsSgQAAAECJvdvOAhAFQAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2eb9123a10fd.js b/pr/323/docs/search.index/2eb9123a10fd.js deleted file mode 100644 index 95d506d1e..000000000 --- a/pr/323/docs/search.index/2eb9123a10fd.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoJAAABL8oBAAAAYiaW8BCQDz5hMAAAEANQABDAGgoAAAFCp38hoUAAABAAEBAPOoDwAAAQABAPOAAnR1AYECsIAT9AABoGAAABQYZHM3AgCDhqBAAAATyrBgE54AS7AwE4sAAQKQDgESAPQCFAAAAQABAAEAAQDGoFAAABJooAAAABA/sCAQ7wABoFAAABGUoQAAABP9oEAAABOoAAMCCzswAAABAAAVAAcAIwYAAEUMAADrDAAAtw8NABMRAAB9EgEAoxMBAAEBAbCwE+8AAWw7MAAAAQAADAADAIoHCAAJDQEA9Q8BAAEBAaAwAAAQymjzVAkAAC4A/AcBAgD0dREAAAEAAQABABOBArAgEXkAAWFz84ICbm/3AwCAh6BAAAAMLhUBBw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2f94b1f135cc.js b/pr/323/docs/search.index/2f94b1f135cc.js new file mode 100644 index 000000000..d3b52164c --- /dev/null +++ b/pr/323/docs/search.index/2f94b1f135cc.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAUAAQAKEwUAAQAA9YgMAAABAAEAAQABAPOAAmdrE4ACsLATYgABYW8BgAKgIAAADBawYBD9AAFlaQGFArBwE4oAAbAwEucAAWVzEwADsEAPiQABoGAAABEyYmVzOzAAAAEAABIABAAhBgEAMAcKAP8PAQBREAMAAQMA9BsQAAABAAQAAQABCwGgQAAAE9Bl8u8TAAABAPOAAmRy8wACZW87MAAAAQAABwACAP4HAwA/CAMAAQIAOzAAAAEAAAkAAQAMDAkA84ACb3QBAwA7MAAAAQAA3gACANgHAAAuC90AAQIBoHAAABGdaTswAAABAADwAAIA1wcAAD4K7wATgAKgQAAADEZydQEEAPTHEgAAAQCOAAEAAQMA814SAAABAJoA84ACYW/XBwAAiLBADQEAAbEwE+wAATSBFjswAAABAAAeAA0ASAUBAPoFAQAeDAAASwwHAG4MAQCXDwEAexABAAgRAABXEQMAlBEBAD0SAACWEgEAERQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/2fac3831697f.js b/pr/323/docs/search.index/2fac3831697f.js deleted file mode 100644 index e597dde82..000000000 --- a/pr/323/docs/search.index/2fac3831697f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYECsDAP6wABsBATMAABc3QBAwDzVRIAAAEAAQAjgAKwABFhAAFoaQEAADswAAABAAATAAEA5wcTACOAAqAwAAAM4GFl84ACcnTzAAJhbDswAAABAAAoAAMAhQATAIQBAADRDxMA") \ No newline at end of file diff --git a/pr/323/docs/search.index/2fd891c7dddc.js b/pr/323/docs/search.index/2fd891c7dddc.js deleted file mode 100644 index 026e8ab57..000000000 --- a/pr/323/docs/search.index/2fd891c7dddc.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9LgQAAABALoAAQATgQOgAAAAEQGxIBQHAAFhZG4BFAD1JRQAAAEAAQABAAEAAQYBsIATyAABdjswAAABAAAHAAIAbhEEADYTAgBXAAABhqDAAAATAaCgAAATI6CQAAAS8bBQEDAAAQXgAvR7BwAAAQABAH8MAQYA81wSAAABAJoAAQABoGAAAA+bcgE/BAAA1wEAgMWgMAAADCcAAIoK") \ No newline at end of file diff --git a/pr/323/docs/search.index/3010308ae38e.js b/pr/323/docs/search.index/3010308ae38e.js new file mode 100644 index 000000000..e6d0768fd --- /dev/null +++ b/pr/323/docs/search.index/3010308ae38e.js @@ -0,0 +1 @@ +rn_("AQABsIASywABdvS6CQAAAQC3CQEAE4ACsDAQsQABbG4BgAKgAAAAED2gAAAAED40NvMDAXYBBw0AABMAAqBAAAARLmFz90gMAABgA1wBMgEBAAEAkQEBAgD0pBIAAAEAiQABAAEDAPTYEgAAAQCOAAEA8wACYnU6MAAAAQAAAAAAEwAQAAAArQeuB68HjQ+ODy8QMBBOEE8Q4RDiEJARkRGiEqMSrxKwEr8SLBMtE/OAAmR0E4ACoCAAAA0RZXIBBwGgQAAAE6ll8tMTAAABAAEHAPSoEAAAAQABAAEAAQAA9a0JAAABAAEAAQABACOAAqCgAAAT/mZwAQMBoJAAABN+ZfJnDwAAAQDXAAAAhaBQAAAQJbFAFCUAAYEpAPLuBQAAAQDzgAJsdJcBAICFsTATqwABsRAT1QABEUUA") \ No newline at end of file diff --git a/pr/323/docs/search.index/304f63ce8492.js b/pr/323/docs/search.index/304f63ce8492.js new file mode 100644 index 000000000..f20351b5b --- /dev/null +++ b/pr/323/docs/search.index/304f63ce8492.js @@ -0,0 +1 @@ +rn_("AQAEoAAAABBBsCAQ8QABoFAAABGWoEAAABOqZGxzdjswAAABAAATAAYAJAYAAEYMAADtDAAAuQ8NABURAAClEwEAAQIA9HcRAAABAAEAAQAThAKwIBF7AAFhc/OAAmFl") \ No newline at end of file diff --git a/pr/323/docs/search.index/307a630e6173.js b/pr/323/docs/search.index/307a630e6173.js new file mode 100644 index 000000000..56ef7734c --- /dev/null +++ b/pr/323/docs/search.index/307a630e6173.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAYAAgAwBgUAKhEAAAEEAbAAEqgAAXM7MAAAAQAACwACABwTBwAEFAMARwAAAYaggAAAEv2xABQRAAGwQBJrAAGwgBQfAAGwUBKqAAEVABb0cg4AAAEAAQABAAEFAPOvDwAAAQABAGOAA7BAEDQAAW1ubwGBArAADOsAAbAQEDIAAXN18wACYWk7MAAAAQAACQAEAMoHAACSDwAAaRABAAoTBQABgAOwABNZAAGwABNbAAGwcBPKAAE0NmEBBAD0GBQAAAEACgABACOBAqBAAAARA21wAQIAOzAAAAEAAA4ABADNBwEADQ0BAPMQCQAyEQAAAQEAOzAAAAEAAAgAAwBgEgAAyRIAACUUBgABAQA7MAAAAQAACgADABcRAgA0EwMADRQDACOAAqAwAAAQBmRnlwcAAMegYAAAE36ggAAAEj1AR4AIOzAAAAEAABgACQCmDwEAuhABAO8QAQD/EAMAcBEGADgTAgCEEwAA2hMBAAkUAQABAwD2bQkAAFAAcAYBALEAAQATgAKwcBOrAAFvcgEEAbCwE/EAAWz0Cw0AAAEA6wIBAAECADswAAABAAAMAAMATAkAAEcTBQD4EwUAAQEA8+0PAAABAD4CI4ACoLAAABOqZ3oBAgA6MAAAAQAAAAAACQAQAAAAfhF/EYESghJeE18TrxOwEx8UIBTzAAFv8sAJAAABAAEFAPSZDwAAAQDjAAEAgwCEoEAAAA0AsHAS7AABsCAMhgABEAUCOzAAAAEAAAkAAwBEBQMA9wUCAG0RAgABgAKgIAAAEMmwIBOrAAFkdAGEArCwE/QAAbAAE1EAAWFz04CEoLAAABPeQCgEAYACoDAAABGWsGAPrQABaHJH/wEAzaAgAAAGFaAgAAAGFrBwE0UAAQNnNAc7MAAAAQAANwAYACcFAABcBQUAtgkAANkMAABpDwEAVRADABIRAABgEgAAcRIFAMkSAQDrEgAA8hIAAAkTAAAaEwEAJBMAACYTAgAqEwEAfBMAAIMTAACPEwEApxMBALkTBQDNEwAAJRQGAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/30d311112226.js b/pr/323/docs/search.index/30d311112226.js deleted file mode 100644 index 072a640e2..000000000 --- a/pr/323/docs/search.index/30d311112226.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOjAAAAEAAAAAAA4AEAAAABANEQ23EJURlhGXEUASRxJIEkwSTRKcEp0S0BLREgEJAPMeEQAAAQABAAEKAPNVEgAAAQABAAEGAPXhEAAAAQABAAEAAQDzgAJkbnMAhLEgE6sAAQARA/KkEgAAAQABDQD2FhMAAAEAnwBTABYAAQABgAKwoBMuAAGgEAAADEhjdQEBAPQ9EwAAAQBKAAEA8wABZfK9CQAAAQD3AACAxbAwEFwAAQACqAg=") \ No newline at end of file diff --git a/pr/323/docs/search.index/30e3052528a0.js b/pr/323/docs/search.index/30e3052528a0.js new file mode 100644 index 000000000..55890d3db --- /dev/null +++ b/pr/323/docs/search.index/30e3052528a0.js @@ -0,0 +1 @@ +rn_("AQIBsEAT5gABdDswAAABAAANAAYAhBIDAOcSAQBOEwEAZRMBANwTAQAAFAEAE4ACsDASsQE0aXABBQDzJhMAAAEAAQATgAKgMAAAE3xleVMCA7BwE4oAAWJlcwHvDwAAEwACoCAAAAYZZm32cgAAAFYHAQDCAQEAXAc=") \ No newline at end of file diff --git a/pr/323/docs/search.index/30f0116407be.js b/pr/323/docs/search.index/30f0116407be.js new file mode 100644 index 000000000..51db20bc1 --- /dev/null +++ b/pr/323/docs/search.index/30f0116407be.js @@ -0,0 +1 @@ +rn_("AQAA9I0JAAABAF0HAQABCQGggAAAFBtmARATAAABAgD1fxAAAAEAAQA5AgEA8wACaXU6MAAAAQAAAAAABwAQAAAAXRB4ElETUhOBE4ITnhOfEwEIAPWxEwAAAQABAAEAAQDzAgFm9UIQAAABAAEAAQABABOAArCgE/QAAXJ08wACYXPzbxAAAAEAAQDzgAJ0dg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/31047b1ed472.js b/pr/323/docs/search.index/31047b1ed472.js new file mode 100644 index 000000000..021c97f46 --- /dev/null +++ b/pr/323/docs/search.index/31047b1ed472.js @@ -0,0 +1 @@ +rn_("AQMA9gYNAACWAlkAAQCmAgEAAQQAOzAAAAEAAAoABAA3EAUAXBIBALISAQDEEwAAAYYCoAAAABLUoAAAABLVZHLzAgF2AfoSAADzgAJlbycBAADHoDAAAA+boHAAABIvoIAAABOXoNAAABM9oLAAABOXQEUICTswAAABAAAJAAMANgYCAIUHBABsDAEAI4ACIgcnlDjmZHIBAAA6MAAAAQAAAAAACgAQAAAAoAmhCdAP7BLtEhgTGRO4EwsUIRQiFBOAAqAwAAAQTWx084ACYWU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/310c7951c52b.js b/pr/323/docs/search.index/310c7951c52b.js new file mode 100644 index 000000000..e643f7a98 --- /dev/null +++ b/pr/323/docs/search.index/310c7951c52b.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwBqEQIA9RIBAIcTAgABBwD2GBMAAAEAnwBTABYAAQABBADzbREAAAEAAQAjAAKgYAAAEoNhcvOTEAAAAQCyAQGAAqEQAAAUCLCAFAAAAWl1dwgAAIixUBQjAAGACWoAFwiwcBNRAAGhMAAAFBcKQRvyqgkAAKMGAYECoFAAABC4oKAAABLNa3ABAwA7MAAAAQAA4gACAOkMAQB5DuAA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/312e80bd916b.js b/pr/323/docs/search.index/312e80bd916b.js deleted file mode 100644 index 864e9da9b..000000000 --- a/pr/323/docs/search.index/312e80bd916b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYkCsCAT2gABoDAAABPjaXATAAOgIAAAEqqggAAAEr1laXX1KgwAADIAAQABAAEAAYYCsJAT/gABoAAAABJEYmQBgAKwMBKeAAGgMAAAEJpydtcAAAKFoDAAABAYoDAAABLyEAETOjAAAAEAAAAAAAkAEAAAAE4JTwlQCYAJgQkvEDQQ6xDsEIESAQMBsDASuAABc/NtEAAAAQABAAEAADswAAABAAAKAAMAFRECADITAwALFAMA0wCEsCAIYwABEBgB9YcMAAABAAEAAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/31a625c2b211.js b/pr/323/docs/search.index/31a625c2b211.js new file mode 100644 index 000000000..d1a4b29e3 --- /dev/null +++ b/pr/323/docs/search.index/31a625c2b211.js @@ -0,0 +1 @@ +rn_("AQEA9MoHAADIB9cAAQABBQD0uBIAAAEAigABAPMCAWP1BBAAAAEAvAABAAEAYwAEoDAAAAxJoHAAABGBZW5zdvMiEAAAlQD+AQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/31f87a770c85.js b/pr/323/docs/search.index/31f87a770c85.js new file mode 100644 index 000000000..60d84ffc6 --- /dev/null +++ b/pr/323/docs/search.index/31f87a770c85.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwBqEQIA9RIBAIcTAgABBADzbREAAAEAAQAjAAKgYAAAEoNhcvOTEAAAAQCyAQGAAqEQAAAUCLCAFAAAAWl1NwQAgoexUBQjAAGACWoAFwiwcBNRAAGhMAAAFBcKQBsTgAKwEBKPAAFpcg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/31fa229a9dc8.js b/pr/323/docs/search.index/31fa229a9dc8.js new file mode 100644 index 000000000..1c3721971 --- /dev/null +++ b/pr/323/docs/search.index/31fa229a9dc8.js @@ -0,0 +1 @@ +rn_("AQABsKASXAABZTswAAABAAAJAAIARwgHAJgSAQABAAGgIAAADPBtAfwFAAABgAKwoBJaAAGwQA+LAAFsbwEEAbAAEpQAAWT0PhIAAAEAAQABACMChLCgE8EAAbBAEpIAAbBwE3IAARWAAAGXEwAAAQABsIATqwABcPKnEwAAAQBDAAOgIAAAEQWgQAAAEkJkZXPzqQ8AABwDAQDzgQJladcCAACGsCASIwABsNATrwABFwEC9CwFAAABAAEAtQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/325b7d9a2242.js b/pr/323/docs/search.index/325b7d9a2242.js new file mode 100644 index 000000000..3e44c7f6d --- /dev/null +++ b/pr/323/docs/search.index/325b7d9a2242.js @@ -0,0 +1 @@ +rn_("AQQA820RAAABAAEAAYICsEASUQABsEASUwABNDYBBgD0GBQAAAEACgABAPMCAXIBhBMAAPMCAWX0KxAAAAEAqAABABOAAqBQAAASg2R00wCEsLAToQABEQEB95MQAAABAG8AHQABAAEAJAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/33aa643b366e.js b/pr/323/docs/search.index/33aa643b366e.js deleted file mode 100644 index 4c4203c98..000000000 --- a/pr/323/docs/search.index/33aa643b366e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoCAAAA92oDAAABJ1ZXIBAAKgIAAAEwawMBGIAAFpdPJ7EgAAAQABAwA7MAAAAQAABgABAAcRBgABCAD0bRMAAAEAfgAFABMBA7CQE8wAAbBQErwBTmdscDswAAABAAASAAkAFRACAIsRAQCrEgEAzBIBAP0SAQBeEwEAkhMBAKETAQDeEwEAAYACsDARhAABsDARhgABcnYBBAGwABJBAAFkOzAAAAEAAAUAAgCAEQMAaxIBALcFAAGHsFASQwD9sGASugABlAkDOzAAAAEAAAoABQDiDAIAdQ4BAK4QAAAyEQMAshIAACOAAqAQAAAM4W5vAQMA8xIQAAABAAEAI4ACoAAAAAwoZHJjAYSgUAAAES+gMAAAEEoUARDy+wcAAAEAE4ADsGASYAABsAAPYAABY2dyVwAAAIWgsAAAE4qgQAAADnSgIAAAEU8QwAoBYg8AAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/33b38805b1c6.js b/pr/323/docs/search.index/33b38805b1c6.js deleted file mode 100644 index 8aa3be57b..000000000 --- a/pr/323/docs/search.index/33b38805b1c6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEAOzAAAAEAAAYAAQAHEQYAAQAA93IJAAABALQGAQBrAAEADwEBggKgIAAAEMmgMAAAETFpdPOAA2FncwEBAaBAAAATp2Xy0RMAAAEAI4ACsFATYAABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3450a1e0ecdc.js b/pr/323/docs/search.index/3450a1e0ecdc.js new file mode 100644 index 000000000..4ef63e4ec --- /dev/null +++ b/pr/323/docs/search.index/3450a1e0ecdc.js @@ -0,0 +1 @@ +rn_("AQUA9fIPAAABAAEAPQIBAAEEAaAAAAAQgnP1Aw0AAAEAAQDrAgEAAQIA9VIRAAABAAEAAQABAAGAArBgEpoAAaAgAAAQpG95AQABoMAAABOqafTrEgAABwAXABsAAQYA9A0UAAABAAEAAQABAgD0ohAAAAEAnAABAAECAaBAAAASw3I7MAAAAQAABwACAKAQAQA5EQUA84ACYW9nAwAAhrCAEsIAVbCgEzIAAQwwBjswAAABAAAIAAMAMgwAAGURAwA5EgMA84ICZWlHAQAAhbBwEz8AAaCAAAAUG6BAAAAPqLBABPROCQAARQYBAHwDAYECoCAAABCesFASRwABYW8BBAGgAAAAE1hzAZ0QAABjBAOwUBEQAAFjZXP2VgkAAAEAAQAsAAEAqggBAAA7MAAAAQAAJAAIAKMHBwDyDwIAQhENAGoRAgAxEgEAqhIBAPUSAQCHEwIAAQACsBAMGwABsDATwQABZXD0lQ8AAAEA4gABAAEAA7BAE48AAaFAAAAT86BQAAARjGJjZgFyBwAA44CEsUAUGAABAjAQAQEDoLAAABNNsEAQXgABoGAAABE4Y2V09FkHAAClCEcC/QABAwA7MAAAAQAABQABAGQSBQABAAGgoAAAE7Zy9J4PAAABAOoAAQDzgQJncwEHAPXiEgAAAQABAAEAAQABgAKgQAAAEQiwUBDvAAFjafOAAmFvAQABoCAAABEocvNHDAAAJANeAROAAoAF6gAcPHJ4AQMCsEASoAABoDAAABL0ZXfyNhAAALgAE4MCoHAAABMpZ28BAgD1hhIAAAEA3wB2AAEAE4ACsDASsQE0aXABBgDzJhMAAAEAAQDzBQJicwHvDwAAAQIA9MwPAAABAAEAAQAjgAKgYAAAEi1iY/e/AACMoJAAABIuO8kaOjAAAAEAAAAAABcAEAAAAOAB7gHvATQENQQ2BHkHsAfiB+MH5AflB+YH5wcWDIIMCQ3qD/wP/Q+/EEERAhQDFA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/346918baff2e.js b/pr/323/docs/search.index/346918baff2e.js new file mode 100644 index 000000000..a08512459 --- /dev/null +++ b/pr/323/docs/search.index/346918baff2e.js @@ -0,0 +1 @@ +rn_("AYMCsQAUIQABsNAUEwABa20BCgDzdBMAAAEAbQATgAKiEAAAFCxsbzMBhKBgAAASIqEAAAAT5ASAAzswAAABAAA0AAIAsgcAAAIIMwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/348567575911.js b/pr/323/docs/search.index/348567575911.js deleted file mode 100644 index 515a4ff55..000000000 --- a/pr/323/docs/search.index/348567575911.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9KISAAABAIkAAQABAwD01hIAAAEAjgABAPMCAmJ1OjAAAAEAAAAAABMAEAAAAKwHrQeuB4sPjA8tEC4QTBBNEN8Q4BCOEY8RoBKhEq0SrhK9EioTKxM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/34e0c30a19a6.js b/pr/323/docs/search.index/34e0c30a19a6.js deleted file mode 100644 index 314f78484..000000000 --- a/pr/323/docs/search.index/34e0c30a19a6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9y8MAAABAC4DAQBDAWICAQABAwD0lw8AAAEA4wABAAGBAqAAAAARLKAQAAAPemV184ACYXIBgQKgkAAAE2SwUBIvAAFidxOAArCQErAAAW5y8wACZXT0aRAAAAEAFwABAAEBAPWHDAAAAQABAAEAAQATgAKwsBNgAAFhbyMAArBADP8AAWV0OzAAAAEAAAkABABHBQEA+QUBAG0MAQBVEQMAAQIAOzAAAAEAAAkAAgBGCAcAlhIBAAGAArCQEvMAAaCgAAATY2V2E4ACsYAUJQABY20BggKwQBONAAGgUAAAEYpiZhOAAqBAAAATtGRzI4ACoNAAABP9Y27zgIQRAUABBQGwQBLUAAFlAS4SAABDggOhAAAAFAmg0AAAE+FrbXMTgAKwYBCFAAFhcgEFADswAAABAAB8AAIAmRAAAKQRewABggOwIBKYAAGwIBAnAAGwQBEcAAFrbHNTgAOwIBPkAAFrdHUBiYSggAAAFBigQAAAE8qgYAAAE+mgkAAAFBwCgAoTgoSgoAAAE2KxoBQoAAGw0BPDAAEEAQkBgAKxcBQjAAGg0AAAE51jbyOAArBgEIMAAWlvAQAA9DkMAAABAEsAAQAjgQKg4AAAE7ZleQENADowAAABAAAAAAAHABAAAADMEs0SXhNfE5ITkxPeE98TAYECsEAS6gABoMAAABOcZW8BAgA6MAAAAQAAAAAACAAQAAAAjg+PDzYRdxJ4EmIT4RMRFBIU8wEBbzswAAABAAAJAAQAqgkBAEUQAwCDEAEAYBMBAPOAA21ucgEAADswAAABAAAGAAIAAhMAAGcTBQBDgAOwQBPVAAGgIAAADP5lcnMjAAKwMBNDAAFjcvIwEwAAAQABgQKwgBKcAAGhAAAAE+BjZgGAAqAAAAAQO6AAAAAQPDQ28wIBdgEFDQAA8wABZAFrCQAA1/4hAJKwoBOLAAGwcBF8AAGwgBJ/AAGgsAAAErKgcAAAEHCwAAmIAAGd3X87MAAAAQAAFgALAIIBAABDBQMA9gUCAGQHAQBuBwIARwwAAKYPAAACEQAAaxECADQSAgDHEwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/3525cfddb103.js b/pr/323/docs/search.index/3525cfddb103.js new file mode 100644 index 000000000..c293954b1 --- /dev/null +++ b/pr/323/docs/search.index/3525cfddb103.js @@ -0,0 +1 @@ +rn_("AQIA9FcRAAABAAEAAQABCgDzOBMAAAEAAQDzgQJtdAEGAPMXEQAAAQABAAGCArBgEsUAAbDQE9MAAWlyE4ACsBAM7gABZWkBBQD0eBMAAAEARgABAPMEAWPyjRIAAAEAI4ICoFAAABJhZnQBAAGwABPKAAFk9BcMAAABAAEAAQDzgQJlaTcGAACJsTAT7wABoGAAABEWoFAAABCnsJASnAABsEAQpQABtRAHAaoTAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/35485c36125e.js b/pr/323/docs/search.index/35485c36125e.js new file mode 100644 index 000000000..bdfa07e60 --- /dev/null +++ b/pr/323/docs/search.index/35485c36125e.js @@ -0,0 +1 @@ +rn_("AQUA9FUSAAABAJoAAQABCAD0ARMAAAEAgwABADMBA7BAEtYAAWFiZfX5DAAAAQDuAkgCwwABAAD1QQwAAAEALAMBAAEA84ACc3UjgQKwgBL7AAFydAEGAPQvEAAAAQCxAAEAUwCEsIASjwABoKAAABQsAgEU9fIFAAABANkNUAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/35baefe8b7a4.js b/pr/323/docs/search.index/35baefe8b7a4.js new file mode 100644 index 000000000..23d831b96 --- /dev/null +++ b/pr/323/docs/search.index/35baefe8b7a4.js @@ -0,0 +1 @@ +rn_("AQQA9CUSAAABAFQBAQDzAAFsOzAAAAEAAAgAAwC4CQEAhxABAOMQBAABAAGwYBOBAAFl9GMMAAABAAEAUQcBBQD0thIAAAEAOgEBAAEHADswAAABAAAFAAEAChMFAPMCAXQ7MAAAAQAACAACAB4MAABLDAcA84ACYWUBgAKxEBQCAAGwEAxmAAFlbwEFADswAAABAAANAAEAQhENAAECAPO2DwAAAQABAAECAPSyDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBgEAgAhwcAAIewoBM7AAGw0BPHAAGgUAAAE5aMFBH1uQcAANQIAQABAAEA84ACbHQTgAOgIAAADNewoBOvAAFhZG4BgAKwgBKPAAGwoBL7AAFpdAGAArDgE+wAAbBAEAEAAWxyAQYA9I0QAAABAAEAAQBHBQAAh6DQAAATybBQD/8AAaAAAAAS/bDwE6cAAQWRA/KlDwAApwABAgD00BIAAAEA/QABACOAAqAgAAAMKmlyAQgA9DkSAAABAAEAAQABBAGwABCRAAFzAQMQAAABAgGgUAAAEpFjAdgMAAC3AgAAh7BADRIAAbAQEu4AAaAAAAASypUQAzowAAABAAAAAAANABAAAABPBVAFDwYQBhEG2wzcDHQPdQ92D4MPOxM8Ez4TAYACsCAJfQABoOAAABNxYmz3FgAAibAQBgkAAbAgB9oAAZEJRzswAAABAACKAAoATAQAAHcJAgCbEAAA6hAAACkRAACmEXsAIxIDADMSAAD0EgAAehMBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3766e569bd8e.js b/pr/323/docs/search.index/3766e569bd8e.js new file mode 100644 index 000000000..9a1a42e59 --- /dev/null +++ b/pr/323/docs/search.index/3766e569bd8e.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQAJEQYAAQAA93MJAAABALUGAQBrAAEADwEBggKgIAAAEMugMAAAETNpdPOAA2FncwEBAaBAAAATqWXy0xMAAAEAI4ACsFATYgABZnPzgAJpbw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/378bf69c5e2d.js b/pr/323/docs/search.index/378bf69c5e2d.js new file mode 100644 index 000000000..fea55f85e --- /dev/null +++ b/pr/323/docs/search.index/378bf69c5e2d.js @@ -0,0 +1 @@ +rn_("AQMBoAAAABLKc/SDDwAAuAMBAAIAAQECsGATrQABsEAT5gABZHQ7MAAAAQAADQAGAIQSAwDnEgEAThMBAGUTAQDcEwEAABQBAAEBA7BwEZ4AAbCAEmIAAYAS9wAQ7G1uczswAAABAAALAAIARQkFANwSBQCjgISgsAAAEz2xcBQfAAEIQIIBAQA7MAAAAQAABgABACUUBgDzBgFl9NoTAAABAC4AAQATAQOwQBQCAAGgYAAAE4NhcnUBJxAAAFcBAACFsRATrQABsDASsQE0EYEIOzAAAAEAAAYAAgAEEwAAaRMFAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/382876485374.js b/pr/323/docs/search.index/382876485374.js new file mode 100644 index 000000000..b18eb167a --- /dev/null +++ b/pr/323/docs/search.index/382876485374.js @@ -0,0 +1 @@ +rn_("AQ0A9VMTAAABAAEAcAABAAECAPSuEAAAAQB7AQEAAQMA9BoRAAABAGABAQDzAAJidQHJDwAAAQMA9KwQAAABAHsBAQDzhAJkcwECAPNfCQAAAQBUAPOAAml0ARgA8ykUAAABAAEAAYACsLATUQABoMAAABL+ZXXzgQJjbQEFAaBgAAASUHL0NwwAAAEAjwMBAAEJAPZ5EgAAAQDqAH8AMAABABOAArBgEG0AAWRuAQMBoHAAABKsZ/S+BwAAAQDzAQEAAQkA8+gTAAABADUAAYECsIAT9gABoGAAABQaZHMXAQCJhbDQFBwAAaBAAAATzLBgE6AASwKQCmdeAACLsHAQ0QABsLASugABoKAAABJ4sPATgQABbYEP9HcAAAABAAEAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/383ef2254913.js b/pr/323/docs/search.index/383ef2254913.js new file mode 100644 index 000000000..8d612dda9 --- /dev/null +++ b/pr/323/docs/search.index/383ef2254913.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAHwAAgCbEAAAphF7AAEAAbBwEz8AAWXykw8AAAEA8wABc/T8DwAAAQAFBAEAM4ADoGAAABFQZGlv") \ No newline at end of file diff --git a/pr/323/docs/search.index/38cb6e24cf5d.js b/pr/323/docs/search.index/38cb6e24cf5d.js deleted file mode 100644 index 607315a02..000000000 --- a/pr/323/docs/search.index/38cb6e24cf5d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBoQAAABPoZPLCBwAA9AEBBgGgMAAAEY10ASMTAAABAwGwUBJ9AAFl9GUJAAASAwEArAMBAQD0JwYAAAEAAQABAAEBAaEAAAAT3WTyJgYAAC4ClwcAAIewoBJ7AAGg4AAAE7QegArzEQQAAAEAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/3922a16454e0.js b/pr/323/docs/search.index/3922a16454e0.js deleted file mode 100644 index a27545fd2..000000000 --- a/pr/323/docs/search.index/3922a16454e0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA9ZgJAAABAAEAAQABAPMAAWnz8QwAACIHAQABBAKgIAAAEXugMAAAEjNiaAF9DwAApwAAgMWgEAAADPKwEBQAAAGgMAAAECYEECgB") \ No newline at end of file diff --git a/pr/323/docs/search.index/392fb1700c95.js b/pr/323/docs/search.index/392fb1700c95.js deleted file mode 100644 index 6b59c786b..000000000 --- a/pr/323/docs/search.index/392fb1700c95.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoCAAAA+CoDAAABNLYXLzAAF0AeAHAAABBAGgQAAAE85l8u0TAAABAAEFADswAAABAAALAAUABRAFAFgSAQByEwEAwRMAAOATAADjAISgMAAAD4CFAAI7MAAAAQAACwAFABoGAgAlBwQAaAwBAC8RAABoEgAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/393da311e403.js b/pr/323/docs/search.index/393da311e403.js deleted file mode 100644 index a87c8af33..000000000 --- a/pr/323/docs/search.index/393da311e403.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBsEAT5AABdDswAAABAAANAAYAghIDAOUSAQBMEwEAYxMBANoTAQD+EwEAE4ACsDASrwE0aXABBQDzJBMAAAEAAQATgAKgMAAAE3pleVMAA7BwE4gAAWJlcwHtDwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/398e9b9f2f38.js b/pr/323/docs/search.index/398e9b9f2f38.js new file mode 100644 index 000000000..91cacb2c7 --- /dev/null +++ b/pr/323/docs/search.index/398e9b9f2f38.js @@ -0,0 +1 @@ +rn_("AQIBsIASywABdvS6CQAAAQC3CQEA8wABYwF3EgAAAQICoLAAABPDoLAAABPEY3PzdBAAAAEAXgABgQKwUBGSAAGgwAAAE5ZoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/3a529860f4ab.js b/pr/323/docs/search.index/3a529860f4ab.js deleted file mode 100644 index 941c7626d..000000000 --- a/pr/323/docs/search.index/3a529860f4ab.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9N0TAAALAB4ADwABAgGwQBJHAAFlAX4PAAABgAKgkAAAE6OgkAAAE6QyM5cCAADGoCAAAAyBoGAAABDnsGANCwABAWQAAzswAAABAAAGAAIAfgcFAFIMAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/3a646e8890e2.js b/pr/323/docs/search.index/3a646e8890e2.js new file mode 100644 index 000000000..e8683b8f2 --- /dev/null +++ b/pr/323/docs/search.index/3a646e8890e2.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABEuc/VIDAAAvAQyAQEAAQATggKgIAAADRFlciOAA6BAAAAMR7AwCXoAAWVvdAECAPQaEwAAAQAPAAEAE4ACsEATxwABYnYTgAKwAAecAAFveAEKAPVTEwAAAQABAHAAAQABgQKgIAAAEJ6wUBJHAAFhbwEEAaAAAAATWHMBnRAAAGMDA7BQERAAAWNlc/ZWCQAAAQABACwAAQCqCAEBAPTeDAAAAQABAAEAI4ACsDASvgFOcnT3AQAAhrAQB54AAQHAB/LHAQAAig8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/3a6bd631205f.js b/pr/323/docs/search.index/3a6bd631205f.js deleted file mode 100644 index 6273f3fec..000000000 --- a/pr/323/docs/search.index/3a6bd631205f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAAAcAAgCMDAUA+xABAAECAPMeEQAAAQABAAEGAPQLFAAAAQABAAEA8wYBbfQ3EgAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3a74061c4f9f.js b/pr/323/docs/search.index/3a74061c4f9f.js deleted file mode 100644 index be8f5e249..000000000 --- a/pr/323/docs/search.index/3a74061c4f9f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsAAPzwABsCAMbwABZWkBiQKwIBPaAAGgMAAAE+NpcBMAA6AgAAASqqCAAAASvWVpdfUqDAAAMgABAAEAAQABhgKwkBP+AAGgAAAAEkRiZAGAArAwEp4AAaAwAAAQmnJ21wAAAIWgMAAAEBigMAAAEvIQARM6MAAAAQAAAAAACgAQAAAATglPCVAJgAmBCTQMLxA0EOsQ7BCBEmOAA6AQAAAJQ2RtdA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3adb9518fd74.js b/pr/323/docs/search.index/3adb9518fd74.js deleted file mode 100644 index 5e9126e58..000000000 --- a/pr/323/docs/search.index/3adb9518fd74.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBoEAAABEmZQHoDwAAAQgA9GMRAAABAAEAAQABAwGgQAAAESVlAXkPAAABAgDzQgwAAAEAAQAjAAKwIAwyAAFrcvZZBwAAAQBLCesAAQADAvOAAnJ4AQMBsIATyAABdjswAAABAAALAAMAuBABAG4RBgA2EwIAI4ACsGAQLwC8YXIBAwGgAAAAD+dz8ncPAAABACOAAqCwAAATW2VoAQMBoEAAABC3ZfJnCQAAAQDXJQAAjLAgB8gJHrBwES0AAbAgB6oAAbCwErQAAaCAAAARZ6AAAAASwb8MTDowAAABAAAAAAAJABAAAADeASsELAQtBB4MHwx9DH4MfwzCEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3afe7bef2754.js b/pr/323/docs/search.index/3afe7bef2754.js deleted file mode 100644 index 80d670aaa..000000000 --- a/pr/323/docs/search.index/3afe7bef2754.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkA8+YTAAABADUAAYACsCATiwABoCAAAA+GaXkBCQGggAAAFBlmAQ4TAAABAwGwABOeAEtzAUsTAABDgAOwIA+CARqwUBJFAAFhb3IBAgD1fRAAAAEAAQA5AgEA8wADaXR1OjAAAAEAAAAAAA4AEAAAAOAHfwnlD1sQfxF2EqYSpxJPE1ATVhN/E4ATnBOdEwEGAPSUEgAAAQB6AQEAEwICsGASlgABZm3y5QwAAAEAI4ICsEASvgABZWwBgAKwQBCBAAGgIAAAE8pjdBOAArCwEygAAWFsAQYA85APAADXAAEAAQUA9HYTAAABAEYAAQDzAgFj8osSAAABAAGBArBQEM0AAbAgD2AAAWNuAQUAOzAAAAEAAA4AAgDrDAAAtw8NAAECADswAAABAAAHAAEAoQkHAOcFAAAJoDAAAAxnoLAAABOcoIAAABFOsNATqwABMGFmaWttcnV2OzAAAAEAADkCBQDSAQAALQPdANcHAAAtC90A8w17AAEMAaCgAAAUKnfyGhQAAAEAAQEA86gPAAABAAEA84ACdHUBAwDzSRIAAAEAAQABAQD14RAAAAEAAQABAAEAAQEA9soHAAABAIwARAejAOIBY4ADsJATeAABYW10AQMBoEAAABPOZfLtEwAAAQATgAKwYBQdAAFlcgEDAaAAAAATVnMBmxAAAAECADowAAABAAAAAAAKABAAAACfCaAJzg/qEusSFhMXE7YTCRQfFCAUgwAEoIAAABIlsHAREQABsCAQsQDqZm5ydgFWCAAAAYECoAAAAAl/oDAAAA+Gc3THAQCAxrAAE6kAAbCQEiMAAaAQAAAHsAASiCgBAgA7MAAAAQAADQAGAAUQBQAOEQEAWBIBAHITAQDBEwAA4BMAABOCArDwFB0AAWZnAYACsIAT9AABoGAAABQYZHMTgAKwwBM9AAFpbwEIAPWvEwAAAQABAAEAAQDzAgFm9UAQAAABAAEAAQABACOAA6AgAAAPgLCgE/IAAWRydPf7BwCPoDAAAA91l9cPOzAAAAEAACsBHQA2BAEAPAUCAPMFAgAaBgIAPgYBACUHBACfBwAAuQcAAFAIAwBVCQIAgwkBAD0MAgBoDAEAjAwFAOcMAQB3DuAAng8BABsQAgBcEAEAbRACAJoQAQD7EAEALxEAAIYRAQAtEgAAaBIAAPkSAQAaEwcAAhQDAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3b1e455f5b16.js b/pr/323/docs/search.index/3b1e455f5b16.js deleted file mode 100644 index 7d0122470..000000000 --- a/pr/323/docs/search.index/3b1e455f5b16.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9CEQAAABAKkAAQABhAKwABEhAAGwABEjAAE0NvMCAWHyggwAAAEAAQEA9k4SAABmAAEARwDzAAEA84ACZW/zgAJoaw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3c6024526c76.js b/pr/323/docs/search.index/3c6024526c76.js new file mode 100644 index 000000000..597fda6ae --- /dev/null +++ b/pr/323/docs/search.index/3c6024526c76.js @@ -0,0 +1 @@ +rn_("AQIA9KQSAAABAIkAAQABAwD02BIAAAEAjgABAPMFAmJ1OjAAAAEAAAAAAAcAEAAAAC8QMBDhEOIQkBGREa8SsBIBAgD0GhEAAAEAYAEBAAEFAPMgEQAAAQABAAEEAPRVEgAAAQCaAAEA84CEEQkA") \ No newline at end of file diff --git a/pr/323/docs/search.index/3cc516400042.js b/pr/323/docs/search.index/3cc516400042.js new file mode 100644 index 000000000..31378684a --- /dev/null +++ b/pr/323/docs/search.index/3cc516400042.js @@ -0,0 +1 @@ +rn_("AQEA98YQAAABAAEApwIBAH4ABQABAQKgQAAAE9mgMAAAEMxlaPVVCQAALgDtAgEADwUBAgD0dxEAAAEAAQABABOBArAgEXsAAWFz84ACbm8BAgD0WRAAAAEAAQABABMAAqDQAAAT3mNz8tEPAAABAPOAA2Vpbw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3ce49910eea7.js b/pr/323/docs/search.index/3ce49910eea7.js deleted file mode 100644 index 588de421e..000000000 --- a/pr/323/docs/search.index/3ce49910eea7.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBsEAT5AABdDswAAABAAANAAYAghIDAOUSAQBMEwEAYxMBANoTAQD+EwEAE4ACsDASrwE0aXABBQDzJBMAAAEAAQATgAKgMAAAE3pleVMBA7BwE4gAAWJlcwHtDwAAE4ADsDAT8gABsCAHZAABZWxz") \ No newline at end of file diff --git a/pr/323/docs/search.index/3d023b7ea367.js b/pr/323/docs/search.index/3d023b7ea367.js new file mode 100644 index 000000000..6cffc88ca --- /dev/null +++ b/pr/323/docs/search.index/3d023b7ea367.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgBdEQMASxICAAEKAbBAE9EAAWX1mRMAAAEAAQABAAEA8wACaXP0OgwAAAEASwABACOBAqDgAAATuGV5") \ No newline at end of file diff --git a/pr/323/docs/search.index/3d1db5465a6b.js b/pr/323/docs/search.index/3d1db5465a6b.js new file mode 100644 index 000000000..93d6376d0 --- /dev/null +++ b/pr/323/docs/search.index/3d1db5465a6b.js @@ -0,0 +1 @@ +rn_("AQQA9N8TAAALAB4ADwABAgGwQBJJAAFlAYAPAAABgAKgkAAAE6WgkAAAE6YyM5cCAADGoCAAAAyCoGAAABDpsGANDQABAWQAAzswAAABAAAGAAIAfwcFAFMMAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/3db858c2a458.js b/pr/323/docs/search.index/3db858c2a458.js new file mode 100644 index 000000000..6321fbe23 --- /dev/null +++ b/pr/323/docs/search.index/3db858c2a458.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA94oDAAABJ3ZXIBAAKgIAAAEwiwMBGKAAFpdPJ9EgAAAQABAwA7MAAAAQAABgABAAkRBgABCAD0bxMAAAEAfgAFABMBA7CQE84AAbBQEr4BTmdscDswAAABAAASAAkAFxACAI0RAQCtEgEAzhIBAP8SAQBgEwEAlBMBAKMTAQDgEwEAAYACsDARhgABsDARiAABcnYBBAGwABJDAAFkOzAAAAEAAAUAAgCCEQMAbRIBALcFAAGHsFASRQD9sGASvAABlAkDOzAAAAEAAAoABQDkDAIAdw4BALAQAAA0EQMAtBIAACOAAqAQAAAM425vAQMA8xQQAAABAAEAI4ACoAAAAAwpZHJjAYSgUAAAETGgMAAAEEwUARDy/AcAAAEAE4ADsGASYgABsAAPYgABY2dyVwAAAIWgsAAAE4ygQAAADnagIAAAEVEQwAoBZA8AAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3e94edaedbd9.js b/pr/323/docs/search.index/3e94edaedbd9.js new file mode 100644 index 000000000..4e56883e0 --- /dev/null +++ b/pr/323/docs/search.index/3e94edaedbd9.js @@ -0,0 +1 @@ +rn_("AQAAOjAAAAEAAAAAAAoAEAAAAJcPmA97EHwQCBGUEZURlhKXEhEUEhQBBADzkg8AANcAAQDzAAFx9w8NAAABAOkCAQDCAGIAAQDzAAJleTowAAABAAAAAAAMABAAAABmDGcMNBA1EMYQxxDIEG8TcBN/E4AT7hPzEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3f16c6444948.js b/pr/323/docs/search.index/3f16c6444948.js deleted file mode 100644 index 941dac3ae..000000000 --- a/pr/323/docs/search.index/3f16c6444948.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQACoDAAABC+oJAAABMHMGj1nhEAAHMBAQABAAEAE4ACoDAAAA9/ZHIjAYSwEAlKAv+wIAwcAPagMAAAD6MSABj1EQYAAC8AAQABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/3f968c92ee8a.js b/pr/323/docs/search.index/3f968c92ee8a.js new file mode 100644 index 000000000..6f4a659db --- /dev/null +++ b/pr/323/docs/search.index/3f968c92ee8a.js @@ -0,0 +1 @@ +rn_("AQAA9CMQAAABAKkAAQABhAKwABEjAAGwABElAAE0NvMCAWHygwwAAAEAAQEA9lASAABmAAEARwDzAAEA84ACZW/zgAJoaw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3fa1cc510dc9.js b/pr/323/docs/search.index/3fa1cc510dc9.js new file mode 100644 index 000000000..371cef811 --- /dev/null +++ b/pr/323/docs/search.index/3fa1cc510dc9.js @@ -0,0 +1 @@ +rn_("AQMA80sSAAABAAEAI4ECsSAUHwABY2sBBAGgQAAAEZdl8woNAACzAwEAAQAA9ZcQAAABAAEAAQBpAnOAhKAwAAATqQgwAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/3fd5ad3a998a.js b/pr/323/docs/search.index/3fd5ad3a998a.js new file mode 100644 index 000000000..9a678a7b7 --- /dev/null +++ b/pr/323/docs/search.index/3fd5ad3a998a.js @@ -0,0 +1 @@ +rn_("AQEBoEAAABEuc/VIDAAAvAQyAQEAAQATggKgIAAADRFlcgECAPSeDwAAAQDqAAEAI4ECoBAAAAxHZWkTgAKg8AAAE5ducjMAA7AwCXoAAWVvdAF9EQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/400291f3e805.js b/pr/323/docs/search.index/400291f3e805.js new file mode 100644 index 000000000..6639d1820 --- /dev/null +++ b/pr/323/docs/search.index/400291f3e805.js @@ -0,0 +1 @@ +rn_("AQIA9zAMAAABAC8DAQBDAWICAQABAwD0mQ8AAAEA4wABAAGBAqAAAAARLqAQAAAPfGV184ACYXIBgQKgkAAAE2awUBIxAAFidxOAArCQErIAAW5y8wACZXT0axAAAAEAFwABAAEBAPWIDAAAAQABAAEAAQATgAKwsBNiAAFhbyMAArBADQEAAWV0OzAAAAEAAAkABABIBQEA+gUBAG4MAQBXEQMAAQIAOzAAAAEAAAkAAgBHCAcAmBIBAAGAArCQEvUAAaCgAAATZWV2E4ACsYAUJwABY20BggKwQBOPAAGgUAAAEYxiZhOAAqBAAAATtmRzI4ACoNAAABP/Y27zgIQRAUABBQGwQBLWAAFlATASAABDggOhAAAAFAug0AAAE+NrbXMTgAKwYBCHAAFhcgEFADswAAABAAB8AAIAmxAAAKYRewABggOwIBKaAAGwIBApAAGwQBEeAAFrbHNTgAOwIBPmAAFrdHUBiYSggAAAFBqgQAAAE8ygYAAAE+ugkAAAFB4CgAoTgoSgoAAAE2SxoBQqAAGw0BPFAAEEAQkBgAKxcBQlAAGg0AAAE59jbyOAArBgEIUAAWlvAQAA9DoMAAABAEsAAQAjgQKg4AAAE7hleQENADowAAABAAAAAAAHABAAAADOEs8SYBNhE5QTlRPgE+ETAYECsEAS7AABoMAAABOeZW8BAgA6MAAAAQAAAAAACAAQAAAAkA+RDzgReRJ6EmQT4xMTFBQU8wEBbzswAAABAAAJAAQAqwkBAEcQAwCFEAEAYhMBAPOAA21ucgEAADswAAABAAAGAAIABBMAAGkTBQBDgAOwQBPXAAGgIAAADQBlcnMjAAKwMBNFAAFjcvIyEwAAAQABgQKwgBKeAAGhAAAAE+JjZgGAAqAAAAAQPaAAAAAQPjQ28wIBdgEHDQAA8wABZAFsCQAA1/4hAJKwoBONAAGwcBF+AAGwgBKBAAGgsAAAErSgcAAAEHKwAAmJAAGd3X87MAAAAQAAFgALAIMBAABEBQMA9wUCAGUHAQBvBwIASAwAAKgPAAAEEQAAbRECADYSAgDJEwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/402fdd03797d.js b/pr/323/docs/search.index/402fdd03797d.js new file mode 100644 index 000000000..c7222c9af --- /dev/null +++ b/pr/323/docs/search.index/402fdd03797d.js @@ -0,0 +1 @@ +rn_("AQkA8+gTAAABADUAAYACsCATjQABoCAAAA+IaXkBCQGggAAAFBtmARATAAABAwGwABOgAEtzAU0TAABDgAOwIA+EARqwUBJHAAFhb3IBAgD1fxAAAAEAAQA5AgEA8wADaXR1OjAAAAEAAAAAAA4AEAAAAOEHgAnnD10QgRF4EqgSqRJRE1ITWBOBE4ITnhOfEwEGAPSWEgAAAQB6AQEAEwICsGASmAABZm3y5wwAAAEAI4ICsEASwAABZWwBgAKwQBCDAAGgIAAAE8xjdBOAArCwEyoAAWFsAQYA85IPAADXAAEAAQUA9HgTAAABAEYAAQDzAgFj8o0SAAABAAGBArBQEM8AAbAgD2IAAWNuAQUAOzAAAAEAAA4AAgDtDAAAuQ8NAAECADswAAABAAAHAAEAogkHAOcFAAAJoDAAAAxooLAAABOeoIAAABFQsNATrQABMGFmaWttcnV2OzAAAAEAADkCBQDTAQAALgPdANgHAAAuC90A9Q17AAEMAaCgAAAULHfyHBQAAAEAAQEA86oPAAABAAEA84ACdHUBAwDzSxIAAAEAAQABAQD14xAAAAEAAQABAAEAAQEA9ssHAAABAIwARQejAOIBY4ADsJATegABYW10AQMBoEAAABPQZfLvEwAAAQATgAKwYBQfAAFlcgEDAaAAAAATWHMBnRAAAAECADowAAABAAAAAAAKABAAAACgCaEJ0A/sEu0SGBMZE7gTCxQhFCIUgwAEoIAAABInsHAREwABsCAQswDqZm5ydgFXCAAAAYECoAAAAAmAoDAAAA+Ic3THAQCAxrAAE6sAAbCQEiUAAaAQAAAHsQASiCgBAgA7MAAAAQAADQAGAAcQBQAQEQEAWhIBAHQTAQDDEwAA4hMAABOCArDwFB8AAWZnAYACsIAT9gABoGAAABQaZHMTgAKwwBM/AAFpbwEIAPWxEwAAAQABAAEAAQDzAgFm9UIQAAABAAEAAQABACOAA6AgAAAPgrCgE/QAAWRydPf7BwCPoDAAAA93l9cPOzAAAAEAACsBHQA3BAEAPQUCAPQFAgAbBgIAPwYBACYHBACgBwAAugcAAFEIAwBWCQIAhAkBAD4MAgBpDAEAjQwFAOkMAQB5DuAAoA8BAB0QAgBeEAEAbxACAJwQAQD9EAEAMREAAIgRAQAvEgAAahIAAPsSAQAcEwcABBQDAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/40301c557124.js b/pr/323/docs/search.index/40301c557124.js new file mode 100644 index 000000000..e92f24fbc --- /dev/null +++ b/pr/323/docs/search.index/40301c557124.js @@ -0,0 +1 @@ +rn_("AQYA9DQTAAABAAEAAQABDwD0DRQAAAEAAQABAAEAAbDwFBUAAWv1jBMAAAUAAQABACQAAQIA9CMQAAABAKkAAQD3AAAAhbEAFAIAAYkwAPJiEgAAAQATgAKwEAxmAAFlbwEFADswAAABAAANAAEAQhENAAECAPO2DwAAAQABAAECAPSyDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBgEAgAAYACoAAAAAw8sDAT0QABcXIBgAKgQAAAELSwwBPHAAFlcwEBADswAAABAAATAAEA0w8TACOAArCQEzsAAWltdzYAAIqgUAAAE5awcBN6AAGgIAAADPacVRE7MAAAAQAACQADALkHAACNEAMAmRMEAAECAbAwERwAAXABLhIAALOAhKAgAAARgQIoBA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/40909919ad1e.js b/pr/323/docs/search.index/40909919ad1e.js new file mode 100644 index 000000000..4b8b23b19 --- /dev/null +++ b/pr/323/docs/search.index/40909919ad1e.js @@ -0,0 +1 @@ +rn_("AYACsAARIwABsAARJQABNDYBBAGgoAAAFCx38hwUAAABAAELAPOREwAAAQABACOAAqCAAAATt2VvYwIDoGAAABNQZW5wATkMAADzAAJhdgFIBwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4092c180885b.js b/pr/323/docs/search.index/4092c180885b.js new file mode 100644 index 000000000..899ee5416 --- /dev/null +++ b/pr/323/docs/search.index/4092c180885b.js @@ -0,0 +1 @@ +rn_("AQkA8+gTAAABADUAAYACsCATjQABoCAAAA+IaXkBCQGggAAAFBtmARATAAABAwGwABOgAEtzAU0TAAAjgAKgIAAAD4RhcgECAPV/EAAAAQABADkCAQDzAANpdHU6MAAAAQAAAAAADQAQAAAA4QeACecPXRB4EqgSqRJRE1ITWBOBE4ITnhOfEwGAArBAEIMAAaAgAAATzGN0E4ACsLATKgABYWwBDAGgoAAAFCx38hwUAAABAAEBAPOqDwAAAQABAPOAAnR1AQMA80sSAAABAAEAAQEA9eMQAAABAAEAAQABACOAArCQE3oAAWF0AQQBoEAAABPQZfLvEwAAAQABAgA7MAAAAQAACwAFAAcQBQBaEgEAdBMBAMMTAADiEwAAE4ICsPAUHwABZmcBgQKwgBP2AAGgYAAAFBpkcwEIAPWxEwAAAQABAAEAAQDzAgFm9UIQAAABAAEAAQABACOAA6AgAAAPgrCgE/QAAWRydHfvAQCNsBATqwABsOATrQABl9UOOzAAAAEAABEBEgA3BAEAPQUCABsGAgAmBwQAUQgDAD4MAgBpDAEA6QwBAHkO4ACgDwEAXhABAG8QAgCcEAEAMREAAIgRAQBqEgAAHBMHAAQUAwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/409b3322db69.js b/pr/323/docs/search.index/409b3322db69.js deleted file mode 100644 index ff45d8e2a..000000000 --- a/pr/323/docs/search.index/409b3322db69.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9fAPAAABAAEAPQIBAAEEAaAAAAAQgHP1AQ0AAAEAAQDrAgEAAQIA9VARAAABAAEAAQABAAGAArBgEpgAAaAgAAAQom95AQABoMAAABOoafTpEgAABwAXABsAAQYA9AsUAAABAAEAAQABAgD0oBAAAAEAnAABAAECAaBAAAASwXI7MAAAAQAABwACAJ4QAQA3EQUA84ACYW9nAwAAhrCAEsAAVbCgEzAAAQwwBjswAAABAAAIAAMAMQwAAGMRAwA3EgMA84ICZWlHAQAAhbBwEz0AAaCAAAAUGaBAAAAPprBABPRNCQAARAYBAHwDAYECoCAAABCcsFASRQABYW8BBAGgAAAAE1ZzAZsQAABjBAOwUBEOAAFjZXP2VQkAAAEAAQAsAAEAqQgBAAA7MAAAAQAAJAAIAKIHBwDwDwIAQBENAGgRAgAvEgEAqBIBAPMSAQCFEwIAAQACsBAMGgABsDATvwABZXD0kw8AAAEA4gABAAEAA7BAE40AAaFAAAAT8aBQAAARimJjZgFxBwAA44CEsUAUFgABAjAQAQEDoLAAABNLsEAQXAABoGAAABE2Y2V09FgHAACkCEcC/QABAwA7MAAAAQAABQABAGISBQABAAGgoAAAE7Ry9JwPAAABAOoAAQDzgQJncwEHAPXgEgAAAQABAAEAAQABgAKgQAAAEQawUBDtAAFjafOAAmFvAQABoCAAABEmcvNGDAAAIwNeAROAAoAF6QAcO3J4AQMCsEASngABoDAAABLyZXfyNBAAALgAE4MCoHAAABMnZ28BAgD1hBIAAAEA3wB2AAEAE4ACsDASrwE0aXABBgDzJBMAAAEAAQDzBQJicwHtDwAAAQIA9MoPAAABAAEAAQAjgAKgYAAAEitiY/e/AACMoJAAABIsO8kaOjAAAAEAAAAAABcAEAAAAN8B7QHuATMENAQ1BHgHrwfhB+IH4wfkB+UH5gcVDIEMBw3oD/oP+w+9ED8RABQBFA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/40c49d5174fc.js b/pr/323/docs/search.index/40c49d5174fc.js new file mode 100644 index 000000000..3dfbfd7ff --- /dev/null +++ b/pr/323/docs/search.index/40c49d5174fc.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgAEEwAAaRMFACOIArBAE9cAAWVzAQQBsDAS2gABY/MrEQAAAQABAAEFADswAAABAAAIAAMAmhECAIgSBACYEwAA8wMDY2Vw89QMAAABAAEAAQABsGATgQABZQG2EwAA8wIBc/Z3CQAAAQABAHEHPwAKAQECAaBQAAARKnIByhAAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/40d03efa707b.js b/pr/323/docs/search.index/40d03efa707b.js deleted file mode 100644 index ad3c24a58..000000000 --- a/pr/323/docs/search.index/40d03efa707b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAAOzAAAAEAAAYAAgACEwAAZxMFACOIArBAE9UAAWVzAQQBsDAS2AABY/MpEQAAAQABAAEFADswAAABAAAIAAMAmBECAIYSBACWEwAA8wEDY2Vw89IMAAABAAEAAQIA9mwJAABQAG8GAQCxAAEA84ACbnI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/40fcfec120bb.js b/pr/323/docs/search.index/40fcfec120bb.js deleted file mode 100644 index 5b4721e8e..000000000 --- a/pr/323/docs/search.index/40fcfec120bb.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBsEARBAABZfImDAAASQIBBQA7MAAAAQAABgABAAsQBgABAwD03AwAAAEAAQABAPMAAnBzOjAAAAEAAAAAAA4AEAAAAB0GHgYfBioHZAlqDHYMphCnEKgQqRBBEkISkhKTEjMAA7BQD4MAxmRwcjswAAABAAAPAAYAKgUAAN4FAwBvDwAAgBEDADwSAwBrEgEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4165ec97becb.js b/pr/323/docs/search.index/4165ec97becb.js deleted file mode 100644 index ef2110ac3..000000000 --- a/pr/323/docs/search.index/4165ec97becb.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9OUPAADBAgEArwDzAAFzOzAAAAEAABkACAA8BQIAPQwCAJ4PAQBcEAEAmhABAIYRAQAaEwcAAhQDACOAArEQFAcAAWF0") \ No newline at end of file diff --git a/pr/323/docs/search.index/4179e32fae1c.js b/pr/323/docs/search.index/4179e32fae1c.js deleted file mode 100644 index 644e191b3..000000000 --- a/pr/323/docs/search.index/4179e32fae1c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYADsAATVwABsAATWQABsHATyAABNDZhAQQA9BYUAAABAAoAAQAjgQKgQAAAEQFtcAEBADswAAABAAAIAAMAXhIAAMcSAAAjFAYAhwMAAMagQAAAEASgYAAAE3yggAAAEjtAB4AIOzAAAAEAABYACACkDwEAuBABAP0QAwBuEQYANhMCAIITAADYEwEABxQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4204d4b478b6.js b/pr/323/docs/search.index/4204d4b478b6.js deleted file mode 100644 index fe2da09f3..000000000 --- a/pr/323/docs/search.index/4204d4b478b6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCoFAAABC2oKAAABLLa3ABAAGwQBIjAAFs9LcJAAABAM0GAQABCAD0NxIAAAEAAQABAAEEAbAAEI8AAXMBARAAAAECAaBQAAASj2MB1gwAALcAAACFsEANEAABoDAAAAwplQABOzAAAAEAAAgABABPBQAADgYCANkMAQByDwIAJwEAgIWwMAl8AAGgEAAAEvuwIAfZAAGRAEQBEAD0AhQAAAEAAQABAAEDAPS3CQAAAQDNBgEAY4ADoAAAAAwpbnJ1AQACsNATPQABoDAAAA91Ymr28wUAAAEAAQBJAAEAYAEBAAGwgBNcAAF0OzAAAAEAAD4ABAA8BgAAZgkAAHEJAACTDDsAAQkA81USAAABAAEA84ADY2d0AYMCoJAAABOjoJAAABOkMjMBgAKgUAAAE8uggAAAEvhjZfMAAnN08qAHAAABAAEAAaCwAAATP2Lz0xIAABcBAQAjgAKgAAAAEtJkciOAAqBQAAATlWNl84ACaXkBBAD14RAAAAEAAQABAAEA8wEBbgGaBwAAAQEAOzAAAAEAAAkAAgBGCAcAlhIBAAEGAPQyEwAAAQABAAEAAQ8A9AsUAAABAAEAAQDzgQJobQEAAbBAE88AAWX1lxMAAAEAAQABAAEAU4ADoCAAABF/bG50120AgMugIAAAE/2wYBB0AAGwQA+rAAHAGzgK") \ No newline at end of file diff --git a/pr/323/docs/search.index/4238388d0318.js b/pr/323/docs/search.index/4238388d0318.js new file mode 100644 index 000000000..f9b9a3a56 --- /dev/null +++ b/pr/323/docs/search.index/4238388d0318.js @@ -0,0 +1 @@ +rn_("AQwBoKAAABQsd/IcFAAAAQABAQDzqg8AAAEAAQABiYSggAAAFBqgQAAAE8ygYAAAE+ugkAAAFB4CgAoTgYSgoAAAE2SxoBQqAAGw0BPFAAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4265138dd576.js b/pr/323/docs/search.index/4265138dd576.js new file mode 100644 index 000000000..a563044c7 --- /dev/null +++ b/pr/323/docs/search.index/4265138dd576.js @@ -0,0 +1 @@ +rn_("AYQCsAARIwABsAARJQABNDbzAAFh8oMMAAABACOAArCQESkBCmx0AYACsHATEQABsIATRQABY3MBAAA6MAAAAQAAAAAACQAQAAAAfhF/EYESghJeE18TrxOwEx8UIBTzgAJkZScBAICFoNAAABOEsBAFSgABoDAAABAlAggL") \ No newline at end of file diff --git a/pr/323/docs/search.index/427536da220b.js b/pr/323/docs/search.index/427536da220b.js new file mode 100644 index 000000000..c6355b8d9 --- /dev/null +++ b/pr/323/docs/search.index/427536da220b.js @@ -0,0 +1 @@ +rn_("AQUA9HgTAAABAEYAAQDzAwFj8o0SAAABACOAArBwFB8AAWhvAYICsAASawABsAARigABZXNDAAOgoAAAE8SgIAAAEwhlaXTyfRIAAAEAAQIAOzAAAAEAAAYAAQAJEQYAAQIBoEAAABOpZfLTEwAAAQDzgAJpbwEIAPRvEwAAAQB+AAUAEwEDsJATzgABsFASvgFOZ2xwOzAAAAEAABIACQAXEAIAjREBAK0SAQDOEgEA/xIBAGATAQCUEwEAoxMBAOATAQABgAKwQBJFAP2gIAAAEMxhaQGBArAwE38AAaAwAAASYWNpAQMBsAASqAABczswAAABAAANAAMAiBEBABwTBwAEFAMAAQMA9YYRAAABAAsBAQBGAfOAAnJ2AQAA9EMSAAABAFAAAQDzAgFkOzAAAAEAAAkAAwCCEQMAPhIDAG0SAQAjgAKgkAAAE8NpbxOAArDwFBEAAWxvAQUA9N8TAAALAB4ADwAjgAKgYAAAEv1ncyOAArCQE8EAAWJs98cAAI2wYBP0AAGwcBNyAAGwYBK8AAGwUBKqAAG1yRc6MAAAAQAAAAAAHwAQAAAALAZVCYMJcAxxDOQM5QzmDHIOcw50DnUOdw54Dq0Prg+vD7APsQ90EHUQsBDTEDQRNRE2ETcRgBG0EssSzBKXEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4296b91ec07a.js b/pr/323/docs/search.index/4296b91ec07a.js deleted file mode 100644 index c8f35673d..000000000 --- a/pr/323/docs/search.index/4296b91ec07a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9VARAAABAAEAAQABAAGAArBgEpgAAaAgAAAQom95AQABoMAAABOoafTpEgAABwAXABsAAQYA9AsUAAABAAEAAQABAgD0oBAAAAEAnAABAAECAaBAAAASwXI7MAAAAQAABwACAJ4QAQA3EQUA84ACYW9nAwAAhrCAEsAAVbCgEzAAAQwwBjswAAABAAAIAAMAMQwAAGMRAwA3EgMA84ACZWk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/42b912b0d8d6.js b/pr/323/docs/search.index/42b912b0d8d6.js deleted file mode 100644 index 695480078..000000000 --- a/pr/323/docs/search.index/42b912b0d8d6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQoA9BgTAAABAA8AAQABgwKgIAAAD/mgMAAAEIppdAEEAbAAEpIAAWT0PBIAAAEAAQABACOGhLCgE78AAbBAEpAAAbBwE3AAARWAAAEAArAwD+sAAbAQEzAAAXN08gwGAAAiBgGAArDAE+oAAaAAAAAHsnR584CFEcEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/43625da5722d.js b/pr/323/docs/search.index/43625da5722d.js deleted file mode 100644 index 0938617c2..000000000 --- a/pr/323/docs/search.index/43625da5722d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9HYTAAABAEYAAQDzAwFj8osSAAABACOAArBwFB0AAWhvAYICsAASaQABsAARiAABZXNDAAOgoAAAE8KgIAAAEwZlaXTyexIAAAEAAQIAOzAAAAEAAAYAAQAHEQYAAQIBoEAAABOnZfLREwAAAQDzgAJpbwEIAPRtEwAAAQB+AAUAEwEDsJATzAABsFASvAFOZ2xwOzAAAAEAABIACQAVEAIAixEBAKsSAQDMEgEA/RIBAF4TAQCSEwEAoRMBAN4TAQABgAKwQBJDAP2gIAAAEMphaQGBArAwE30AAaAwAAASX2NpAQMBsAASpgABczswAAABAAANAAMAhhEBABoTBwACFAMAAQMA9YQRAAABAAsBAQBGAfOAAnJ2AQAA9EESAAABAFAAAQDzAgFkOzAAAAEAAAkAAwCAEQMAPBIDAGsSAQAjgAKgkAAAE8FpbxOAArDwFA8AAWxvAQUA9N0TAAALAB4ADwAjgAKgYAAAEvtncyOAArCQE78AAWJs98cAAI2wYBPyAAGwcBNwAAGwYBK6AAGwUBKoAAG1yRc6MAAAAQAAAAAAHwAQAAAAKwZUCYIJbwxwDOIM4wzkDHAOcQ5yDnMOdQ52DqsPrA+tD64Prw9yEHMQrhDREDIRMxE0ETURfhGyEskSyhKVEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/43be9d36d389.js b/pr/323/docs/search.index/43be9d36d389.js new file mode 100644 index 000000000..9f7e1e4c9 --- /dev/null +++ b/pr/323/docs/search.index/43be9d36d389.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQDzEAkAI4ADsGAS+wABsDAPfQABaXN0AQgA9bETAAABAAEAAQABAPMBAWb1QhAAAAEAAQABAAEAY4ADoDAAABLDZWd0") \ No newline at end of file diff --git a/pr/323/docs/search.index/43e96dc71aca.js b/pr/323/docs/search.index/43e96dc71aca.js new file mode 100644 index 000000000..167e18a6b --- /dev/null +++ b/pr/323/docs/search.index/43e96dc71aca.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAkABACrCQEARxADAIUQAQBiEwEAAQIBsEAT5gABdDswAAABAAANAAYAhBIDAOcSAQBOEwEAZRMBANwTAQAAFAEAE4ACsDASsQE0aXABiAKwABNZAAGwABNbAAE0NhOBAqAwAAARKmRzAYACoCAAAAyFsGATigABbngBBQDzJhMAAAEAAQATgAKgMAAAE3xlefMAhBIAA/LvDwAA2wDzgAJydA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/440ac285a659.js b/pr/323/docs/search.index/440ac285a659.js deleted file mode 100644 index 47291bce5..000000000 --- a/pr/323/docs/search.index/440ac285a659.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9HYTAAABAEYAAQDzBAFj8osSAAABACOCAqBQAAASX2Z0AQABsAATyAABZPQWDAAAAQABAAEA84ACZWkTgAKw4BPTAAF0dg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/442424c43e39.js b/pr/323/docs/search.index/442424c43e39.js new file mode 100644 index 000000000..693cebffd --- /dev/null +++ b/pr/323/docs/search.index/442424c43e39.js @@ -0,0 +1 @@ +rn_("AQgA9CMQAAABAKkAAQABAAGggAAAFBtm8k4JAADCCQEHAPXiEgAAAQABAAEAAQAjgAKgUAAAEQhhb/MCAmZzOzAAAAEAAAgABACwBwAA4gcFAAkNAABBEQAAM4ADoEAAAAwoYWVv") \ No newline at end of file diff --git a/pr/323/docs/search.index/44630589167e.js b/pr/323/docs/search.index/44630589167e.js deleted file mode 100644 index e3f1cbb01..000000000 --- a/pr/323/docs/search.index/44630589167e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAAAYAAgAvBgUAKBEAAAEEAbAAEqYAAXM7MAAAAQAACwACABoTBwACFAMARwAAAYaggAAAEvuxABQPAAGwQBJpAAGwgBQdAAGwUBKoAAEVABb0cA4AAAEAAQABAAEFAPOtDwAAAQABAGOAA7BAEDIAAW1ubwGBArAADOkAAbAQEDAAAXN18wACYWk7MAAAAQAACQAEAMkHAACQDwAAZxABAAgTBQABgAOwABNXAAGwABNZAAGwcBPIAAE0NmEBBAD0FhQAAAEACgABACOBAqBAAAARAW1wAQIAOzAAAAEAAA4ABADMBwEACw0BAPEQCQAwEQAAAQEAOzAAAAEAAAgAAwBeEgAAxxIAACMUBgABAQA7MAAAAQAACgADABURAgAyEwMACxQDACOAAqAwAAAQBGRnlwcAAMegYAAAE3yggAAAEjtAR4AIOzAAAAEAABgACQCkDwEAuBABAO0QAQD9EAMAbhEGADYTAgCCEwAA2BMBAAcUAQABAwD2bAkAAFAAbwYBALEAAQATgAKwcBOpAAFvcgEEAbCwE+8AAWz0CQ0AAAEA6wIBAAECADswAAABAAAMAAMASwkAAEUTBQD2EwUAAQEA8+sPAAABAD4CI4ACoLAAABOoZ3oBAgA6MAAAAQAAAAAACQAQAAAAfBF9EX8SgBJcE10TrROuEx0UHhTzAAFv8r8JAAABAAEFAPSXDwAAAQDjAAEAgwCEoEAAAAz+sHAS6gABsCAMhQABEAUCOzAAAAEAAAkAAwBDBQMA9gUCAGsRAgABgAKgIAAAEMewIBOpAAFkdAGEArCwE/IAAbAAE08AAWFz04CEoLAAABPcQCgEAYACoDAAABGUsGAPqwABaHJH/wEAzaAgAAAGFKAgAAAGFbBwE0MAAQNnNAc7MAAAAQAANwAYACYFAABbBQUAtQkAANcMAABnDwEAUxADABARAABeEgAAbxIFAMcSAQDpEgAA8BIAAAcTAAAYEwEAIhMAACQTAgAoEwEAehMAAIETAACNEwEApRMBALcTBQDLEwAAIxQGAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/44a3f431018f.js b/pr/323/docs/search.index/44a3f431018f.js deleted file mode 100644 index 8cccbd3e0..000000000 --- a/pr/323/docs/search.index/44a3f431018f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAcAAwBoEQIA8xIBAIUTAgABBADzaxEAAAEAAQAjAAKgYAAAEoFhcvOREAAAAQCyAQGAAqEQAAAUBrCAE/4AAWl1NwQAgYexUBQhAAGACWkAFwewcBNPAAGhMAAAFBUKQBsBAAA7MAAAAQAAewABAPMNewABAgD2FhMAAAEAnwBTABYAAQAjgQKgIAAAELFydvOAAmVvI4ACoCAAABIlZHQBAAA7MAAAAQAA4AAEAD0GAABlCN0AkgwAABQRAADzgANkbnY=") \ No newline at end of file diff --git a/pr/323/docs/search.index/44bac3e74251.js b/pr/323/docs/search.index/44bac3e74251.js new file mode 100644 index 000000000..f79271eb3 --- /dev/null +++ b/pr/323/docs/search.index/44bac3e74251.js @@ -0,0 +1 @@ +rn_("AQkBoEAAABPQZfLvEwAAAQABgAKwAAzuAAGggAAAEz5kcAEDAaAAAAAP6XPyeQ8AAAEAAQIDsGASxQABsNAT0wABoIAAABNdaXJzAd4TAAD3AACAhaEwAAAT7pBJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/44bbaddcd3f2.js b/pr/323/docs/search.index/44bbaddcd3f2.js new file mode 100644 index 000000000..35e605f4e --- /dev/null +++ b/pr/323/docs/search.index/44bbaddcd3f2.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAYAAQANEAYAAQMA9N4MAAABAAEAAQBjAAOgkAAAEvJocHM6MAAAAQAAAAAAEAAQAAAAHgYfBiAGKwdlCWsMdwyoEKkQqhCrEEMSRBKUEpUSvBK9Eg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/44d998731581.js b/pr/323/docs/search.index/44d998731581.js new file mode 100644 index 000000000..aa8b1cc06 --- /dev/null +++ b/pr/323/docs/search.index/44d998731581.js @@ -0,0 +1 @@ +rn_("AQUA9FUSAAABAJoAAQABCAD0ARMAAAEAgwABADMBA7BAEtYAAWFiZTowAAABAAAAAAAJABAAAACXB5gH+Qz6DHwP6A8yEDMQMBLzEgEAADswAAABAAAeAA4ATQUBAAAGCAB6CQEAQQwBAOsMAQB2DgAAbg8CAIQPAACiDwEAixABAJ4QAAAzEQAAihEBAI8RAAABgQKwABEjAAGwABElAAE0NiOAArCAE/QAAW9wAQABsEAS0gABZfMuEQAAPQEBALcBAICFoGAAABDKEAEL") \ No newline at end of file diff --git a/pr/323/docs/search.index/44f8510a4206.js b/pr/323/docs/search.index/44f8510a4206.js deleted file mode 100644 index 15e54eda6..000000000 --- a/pr/323/docs/search.index/44f8510a4206.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAcAAwBoEQIA8xIBAIUTAgABBADzaxEAAAEAAQAjAAKgYAAAEoFhcvOREAAAAQCyAQGAAqEQAAAUBrCAE/4AAWl1NwQAgoexUBQhAAGACWkAFwewcBNPAAGhMAAAFBUKQBsTgAKwEBKNAAFpcg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4508fe205566.js b/pr/323/docs/search.index/4508fe205566.js deleted file mode 100644 index cc49c80f8..000000000 --- a/pr/323/docs/search.index/4508fe205566.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9g0NAAABAOkCAQAkAQEAAQoA81wSAAABAJoAAQEA93IJAAABALQGAQBrAAEADwEjgAKwYBNgAAFpbwECAPTPDAAAAQDIBQEAAQUA9HYTAAABAEYAAQD3BAAAh6AgAAAM0bCQEZYAAQScAzowAAABAAAAAAAIABAAAAAKBmIJYwl0DHUMoxCkEIsSjBI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/456883a708a4.js b/pr/323/docs/search.index/456883a708a4.js new file mode 100644 index 000000000..caf5518d2 --- /dev/null +++ b/pr/323/docs/search.index/456883a708a4.js @@ -0,0 +1 @@ +rn_("AQEBsIAS6QABdfKTCQAAAQAjgAKwEAmRAAEzNgEGAPOVEAAAAQAPAROAArAAD2wAAW91AQMA81cSAAABAAEAI4ACsAARYwABaGkBAAA7MAAAAQAAEwABAOgHEwAjgAKgMAAADOJhZfOAAnJ0E4ACsDATMgABYWwBAAGggAAAFBtm8k4JAADCCQEHAPXiEgAAAQABAAEAAQAjgAKgUAAAEQhhb/MCAmZzOzAAAAEAAAgABACwBwAA4gcFAAkNAABBEQAAE4ACoEAAAAwoYW8BAwGwQBPmAAF08k4TAAABACOFAqBAAAATfGJzEwACoFAAABEVYmb3WwkAAAEAQwDOBgEAYQABABOBAqBQAAARnWd0AYACoEAAABGMoCAAABNdYWkBAgD0jQkAAAEAXQcBAAECAPNjDAAAAQABABOAA7AQCYsAAbAwE48AAWl1eQECADswAAABAAALAAIARxMFAPgTBQDzCAFtOzAAAAEAAAwAAwASEQAAcRIFALkTBQABgQKggAAAEmCgcAAAE55hc/OAAm5yt7UBAI2hQAAAE/OwMBEvAAGgIAAACY+wIAe0Ady3lQ47MAAAAQAAFQIFANEBAAD2ASIBcgcAANcHAAA+Cu8A") \ No newline at end of file diff --git a/pr/323/docs/search.index/4599163a6a37.js b/pr/323/docs/search.index/4599163a6a37.js deleted file mode 100644 index 7fd1a32fc..000000000 --- a/pr/323/docs/search.index/4599163a6a37.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCsQAUHwABsNAUEQABa20BCgDzchMAAAEAbQATgAKiEAAAFCpsbzMChKBgAAASIKEAAAAT4gSAAzswAAABAAA0AAIAsQcAAAEIMwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/45d12af4ff3b.js b/pr/323/docs/search.index/45d12af4ff3b.js deleted file mode 100644 index a413f847b..000000000 --- a/pr/323/docs/search.index/45d12af4ff3b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9YQRAAABAAsBAQBGAQEBAPfEEAAAAQABAKcCAQB+AAUAAQIA9FcQAAABAAEAAQATgAKg0AAAE9xjc/OAAmVvI4ACsAAHtAABbHTzAAJhcvJgDwAAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/464fd77205b4.js b/pr/323/docs/search.index/464fd77205b4.js new file mode 100644 index 000000000..f794e5257 --- /dev/null +++ b/pr/323/docs/search.index/464fd77205b4.js @@ -0,0 +1 @@ +rn_("AYICoFAAABJqoQAAABP/Y3QjAAKwQA+GAAFldTswAAABAAA3ABYALgcBACQMAQD7DAEAiw8BAAcQBQA3EAUAsRABABARAQCaEQIAWhIDAIgSBACyEgEAuBIBAMcSAQBDEwEAVhMBAHQTAQB4EwEAmBMAAL8TAQDDEwEA4hMAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4682f9d07fab.js b/pr/323/docs/search.index/4682f9d07fab.js deleted file mode 100644 index 8f7c57642..000000000 --- a/pr/323/docs/search.index/4682f9d07fab.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYkCsCAT2gABoDAAABPjaXATAAOgIAAAEqqggAAAEr1laXX1KgwAADIAAQABAAEAAYYCsJAT/gABoAAAABJEYmQBAgD0LwwAAAEALgMBAAEAADswAAABAAAGAAIAAhMAAGcTBQAjgQKwQBPVAAFlc1OAA7AACYgAAWV4eQGAArAgEp4AAbAACYYAAXJ4RwEAAMawwBMuAAGgEAAAERSwQAzpAAGgMAAAEJoARIAL9OcFAAABAAcHAQDXAAAAhaAwAAAQGKAwAAAS8hABEzowAAABAAAAAAAKABAAAABOCU8JUAmACYEJNAwvEDQQ6xDsEIES") \ No newline at end of file diff --git a/pr/323/docs/search.index/46a11179261c.js b/pr/323/docs/search.index/46a11179261c.js new file mode 100644 index 000000000..e6b4b96ba --- /dev/null +++ b/pr/323/docs/search.index/46a11179261c.js @@ -0,0 +1 @@ +rn_("AQIA9FcRAAABAAEAAQABAQDzbREAAAEAAQATgAIluae+ZaxlcwEKAPM4EwAAAQABAAGAArAgE60AAaBgAAATBW92AYACoDAAABJQoAAAABLEY2TzgIUFEAY=") \ No newline at end of file diff --git a/pr/323/docs/search.index/46a6871d7ff2.js b/pr/323/docs/search.index/46a6871d7ff2.js new file mode 100644 index 000000000..bf3116d2b --- /dev/null +++ b/pr/323/docs/search.index/46a6871d7ff2.js @@ -0,0 +1 @@ +rn_("AYACsAAP0QABsCAMcAABZWkBiQKwIBPcAAGgMAAAE+VpcBMAA6AgAAASrKCAAAASv2VpdfUrDAAAMgABAAEAAQABhgKwkBQAAAGgAAAAEkZiZAGAArAwEqAAAaAwAAAQnHJ21wAAAIWgMAAAEBqgMAAAEvQQARM6MAAAAQAAAAAACgAQAAAATwlQCVEJgQmCCTUMMRA2EO0Q7hCDEmOAA6AQAAAJRGRtdA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/46b30dba3752.js b/pr/323/docs/search.index/46b30dba3752.js deleted file mode 100644 index 73693bc8e..000000000 --- a/pr/323/docs/search.index/46b30dba3752.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgBoIAAABQZZgEOEwAAE4ACoOAAABPiYnABAwGwABOeAEtz9ZcPAAABAOMAAQDPAgGAArBAEkUAAaAQAAAQunByAYEDoAAAABEssAAPggEaoBAAAA96ZXN184ADYW9yAQIA9X0QAAABAAEAOQIBAAEEADswAAABAAAJAAEA8RAJAAGBAqCQAAATZLBQEi8AAWJ3o4CEsFATfwABsJASsAABQCADVwMAAIagcAAAE1ugQAAAE8cwQQw6MAAAAQAAAAAAOwAQAAAATAVNBf8FAAYBBgIGAwYEBgUGBgYHBuAHeQl6CX8JQAxBDGIMYwxkDOkM6gx0DmwPbQ9uD4IPoA+hD+UPWxBpEGoQgRCCEIkQihCcEMIQwxAxEX8RiBGJEY0RdhKmEqcSTxNQE1YTfxOAE5wTnRO0ExYUFxQhFCIU") \ No newline at end of file diff --git a/pr/323/docs/search.index/46e6bf297811.js b/pr/323/docs/search.index/46e6bf297811.js new file mode 100644 index 000000000..19a1b14d0 --- /dev/null +++ b/pr/323/docs/search.index/46e6bf297811.js @@ -0,0 +1 @@ +rn_("AQIA9YYRAAABAAsBAQBGAQEBAPfGEAAAAQABAKcCAQB+AAUAAQIA9FkQAAABAAEAAQATgAKg0AAAE95jc/OAAmVvI4ACsAAHtQABbHTzAAJhcvJiDwAAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/46f8dda12533.js b/pr/323/docs/search.index/46f8dda12533.js new file mode 100644 index 000000000..67c11ac7f --- /dev/null +++ b/pr/323/docs/search.index/46f8dda12533.js @@ -0,0 +1 @@ +rn_("AYMCoFAAABC4oKAAABLNa3ABAAGwQBIlAAFs9LgJAAABAM4GAQABCAD0ORIAAAEAAQABAAEEAbAAEJEAAXMBAxAAAAECAaBQAAASkWMB2AwAALcAAACFsEANEgABoDAAAAwqlQABOzAAAAEAAAgABABQBQAADwYCANsMAQB0DwIAJwEAgIWwMAl9AAGgEAAAEv2wIAfaAAGRAEQBEAD0BBQAAAEAAQABAAEDAPS4CQAAAQDOBgEAY4ADoAAAAAwqbnJ1AQACsNATPwABoDAAAA93Ymr29AUAAAEAAQBJAAEAYAEBAAGwgBNeAAF0OzAAAAEAAD8ABAA9BgAAZwkAAHIJAACUDDwAAQkA81cSAAABAAEA84ADY2d0AYMCoJAAABOloJAAABOmMjMBgAKgUAAAE82ggAAAEvpjZfMAAnN08qEHAAABAAEAAaCwAAATQWLz1RIAABcBAQAjgAKgAAAAEtRkciOAAqBQAAATl2Nl84ACaXkBBAD14xAAAAEAAQABAAEA8wEBbgGbBwAAAQEAOzAAAAEAAAkAAgBHCAcAmBIBAAEGAPQ0EwAAAQABAAEAAQ8A9A0UAAABAAEAAQDzgQJobQEAAbBAE9EAAWX1mRMAAAEAAQABAAEAU4ADoCAAABGBbG50120AgMugIAAAE/+wYBB2AAGwQA+tAAHAGzgK") \ No newline at end of file diff --git a/pr/323/docs/search.index/47b13875e45c.js b/pr/323/docs/search.index/47b13875e45c.js deleted file mode 100644 index 93526365d..000000000 --- a/pr/323/docs/search.index/47b13875e45c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAAOjAAAAEAAAAAAAoAEAAAAJUPlg95EHoQBhGSEZMRlBKVEg8UEBTzAQF58n0TAAABACMAA6BAAAARJaBQAAATx2Vvc/J5DwAAsgI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/47b2eb85726d.js b/pr/323/docs/search.index/47b2eb85726d.js new file mode 100644 index 000000000..653c539ab --- /dev/null +++ b/pr/323/docs/search.index/47b2eb85726d.js @@ -0,0 +1 @@ +rn_("AQIBoQAAABPqZPLDBwAA9AEBBgGgMAAAEY90ASUTAAABAwGwUBJ/AAFl9GYJAAASAwEArQMBAQD0KAYAAAEAAQABAAEBAaEAAAAT32TyJwYAAC4ClwcAAIewoBJ9AAGg4AAAE7YegArzEgQAAAEAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/47cdb0491b20.js b/pr/323/docs/search.index/47cdb0491b20.js deleted file mode 100644 index 4532034a0..000000000 --- a/pr/323/docs/search.index/47cdb0491b20.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAcAAwBoEQIA8xIBAIUTAgABBADzaxEAAAEAAQAjAAKgYAAAEoFhcvOREAAAAQCyAQGAAqEQAAAUBrCAE/4AAWl1NwQAgYexUBQhAAGACWkAFwewcBNPAAGhMAAAFBUKQBsjgAKgMAAAEiVudg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/47d3233d790f.js b/pr/323/docs/search.index/47d3233d790f.js deleted file mode 100644 index 08844a15f..000000000 --- a/pr/323/docs/search.index/47d3233d790f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA98QQAAABAAEApwIBAH4ABQABAgD0VxAAAAEAAQABABOAAqDQAAAT3GNz84ACZW8jgQKwAAe0AAFsdAEAAPQnBgAAAQABAAEAU4ADoFAAABQqZG5y") \ No newline at end of file diff --git a/pr/323/docs/search.index/48917fd6b226.js b/pr/323/docs/search.index/48917fd6b226.js deleted file mode 100644 index 8248814fa..000000000 --- a/pr/323/docs/search.index/48917fd6b226.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA98QQAAABAAEApwIBAH4ABQABAgD0VxAAAAEAAQABABOAAqDQAAAT3GNzU4ADoHAAABPXZWlvQ4ADoCAAAA+ZsAAHtAABZGx0") \ No newline at end of file diff --git a/pr/323/docs/search.index/48b3ceed3817.js b/pr/323/docs/search.index/48b3ceed3817.js deleted file mode 100644 index f27d034b6..000000000 --- a/pr/323/docs/search.index/48b3ceed3817.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcA9J8RAAABAAEAAQAjAQKgcAAAEZ5hcgHbBwAAAQMAOzAAAAEAAAcAAgBYDwUA2BIBAPMAAmN0OzAAAAEAAFcAJgAwBQsA5QUBAGAJAQA8DAAAQgwCAPMMAAD+DAIAEA0BAH8PAACEDwEAhw8BAI0PAAASEAIAGRABAB4QAQA9EAAAsRAAALcQAAAEEQEAEREBACURAQApEQIAhBEBAJURAgCbEQAAQBIAAEcSAQBMEgEAkBIBAJwSAwC+EgEAzhIDANQSAQAuEwEAPxMAAKcTAADMEwQA1RMCAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/48d55f753fb7.js b/pr/323/docs/search.index/48d55f753fb7.js deleted file mode 100644 index b347f1992..000000000 --- a/pr/323/docs/search.index/48d55f753fb7.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMA8xIQAAABAAEAAYACoLAAABOUsDAS7AABbnRDAAOgIAAAEL2wUBGQAAFlaGk7MAAAAQAABgADACgMAACfEQMAQxMBAPOAAmRy") \ No newline at end of file diff --git a/pr/323/docs/search.index/4909652ee282.js b/pr/323/docs/search.index/4909652ee282.js deleted file mode 100644 index f635f3a76..000000000 --- a/pr/323/docs/search.index/4909652ee282.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBsIAS5wABdfKSCQAAAQAjgAKwEAmQAAEzNgEGAPOTEAAAAQAPAROAArAAD2oAAW91AQMA81USAAABAAEAI4ACsAARYQABaGkBAAA7MAAAAQAAEwABAOcHEwAjgAKgMAAADOBhZfOAAnJ0E4ACsDATMAABYWwBAAGggAAAFBlm8k0JAADBCQEHAPXgEgAAAQABAAEAAQAjgAKgUAAAEQZhb/MCAmZzOzAAAAEAAAgABACvBwAA4QcFAAcNAAA/EQAAE4ACoEAAAAwnYW8BAwGwQBPkAAF08kwTAAABACOFAqBAAAATemJzEwACoFAAABETYmb3WgkAAAEAQwDNBgEAYQABABOBAqBQAAARm2d0AYACoEAAABGKoCAAABNbYWkBAgD0jAkAAAEAXAcBAAECAPNiDAAAAQABABOAA7AQCYoAAbAwE40AAWl1eQECADswAAABAAALAAIARRMFAPYTBQDzCAFtOzAAAAEAAAwAAwAQEQAAbxIFALcTBQABgQKggAAAEl6gcAAAE5xhc/OAAm5yt7UBAI2hQAAAE/GwMBEtAAGgIAAACY6wIAezAdy3lQ47MAAAAQAAFQIFANABAAD1ASIBcQcAANYHAAA9Cu8A") \ No newline at end of file diff --git a/pr/323/docs/search.index/491a1690ed91.js b/pr/323/docs/search.index/491a1690ed91.js new file mode 100644 index 000000000..80606cd3f --- /dev/null +++ b/pr/323/docs/search.index/491a1690ed91.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAUAAQBkEgUAAQACsEATMAABoKAAABO2ZXI6MAAAAQAAAAAABwAQAAAALActB8QHxQeeD58PiRCKEFOAA6CgAAATlmdpcw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/49b3709bea71.js b/pr/323/docs/search.index/49b3709bea71.js new file mode 100644 index 000000000..826b28f0a --- /dev/null +++ b/pr/323/docs/search.index/49b3709bea71.js @@ -0,0 +1 @@ +rn_("AQwA8zgTAAABAAEAAQYA8xcRAAABAAEAAYICsGASxQABsNAT0wABaXITgAKwEAzuAAFlaRcDAIOIsTAT7wABoGAAABEWoFAAABCnsJASnAABsEAQpQABtBAH8wABYfSNCQAAAQBdBwEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4a10d1b50063.js b/pr/323/docs/search.index/4a10d1b50063.js deleted file mode 100644 index aab50e3e8..000000000 --- a/pr/323/docs/search.index/4a10d1b50063.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsKASWgABZTswAAABAAAJAAIARggHAJYSAQABAAGgIAAADO5tAfsFAAABgAKwoBJYAAGwQA+JAAFsbwEEAbAAEpIAAWT0PBIAAAEAAQABACMChLCgE78AAbBAEpAAAbBwE3AAARWAAAGVEwAAAQABsIATqQABcPKlEwAAAQBDAAOgIAAAEQOgQAAAEkBkZXPzpw8AABwDAQDzgQJladcCAACGsCASIQABsNATrQABFwEC9CsFAAABAAEAtQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4a869b28035c.js b/pr/323/docs/search.index/4a869b28035c.js deleted file mode 100644 index edaa65601..000000000 --- a/pr/323/docs/search.index/4a869b28035c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkA8+YTAAABADUAAQwBoKAAABQqd/IaFAAAAQABAQDzqA8AAAEAAQDzgAJ0dQGBArCAE/QAAaBgAAAUGGRzNwIAgIagQAAAE8qwYBOeAEuwMBOLAAECkA4BAAGwUBCvAAFj9L8HAAABAAEA8wHDgISgEAAADISgUAAAEi0MQAQ=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4b0aae8452bf.js b/pr/323/docs/search.index/4b0aae8452bf.js new file mode 100644 index 000000000..1a4cd48f1 --- /dev/null +++ b/pr/323/docs/search.index/4b0aae8452bf.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwBqEQIA9RIBAIcTAgABBADzbREAAAEAAQAjAAKgYAAAEoNhcvOTEAAAAQCyAQGAAqEQAAAUCLCAFAAAAWl1NwQAgYexUBQjAAGACWoAFwiwcBNRAAGhMAAAFBcKQBsjgAKgMAAAEidudg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4b88b05de3be.js b/pr/323/docs/search.index/4b88b05de3be.js deleted file mode 100644 index 76a1178da..000000000 --- a/pr/323/docs/search.index/4b88b05de3be.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoEAAABGVZfMIDQAAswMBAAEGAPS4EAAAAQC6AAEAE4EDoAAAABEBsSAUBwABYWRuARQA9SUUAAABAAEAAQABAAEGAbCAE8gAAXY7MAAAAQAABwACAG4RBAA2EwIAVwAAAYagwAAAEwGgoAAAEyOgkAAAEvGwUBAwAAEF4AL0ewcAAAEAAQB/DAEGAPNcEgAAAQCaAPOAA3J0dgEDADswAAABAAAHAAIAMhMDAAsUAwDzAAFzAcsTAADzAAFzAZQRAABnAQCAhqAwAAAMU7AAEuwAAaAQAAAM7hJADg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4bcec2741bca.js b/pr/323/docs/search.index/4bcec2741bca.js new file mode 100644 index 000000000..e8580423c --- /dev/null +++ b/pr/323/docs/search.index/4bcec2741bca.js @@ -0,0 +1 @@ +rn_("AYACoCAAAA+EoDAAABNNYXLzAAF0AeEHAAABBAGgQAAAE9Bl8u8TAAABAAEFADswAAABAAALAAUABxAFAFoSAQB0EwEAwxMAAOITAADjAISgMAAAD4KFAAI7MAAAAQAACwAFABsGAgAmBwQAaQwBADERAABqEgAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4bf42a7ae759.js b/pr/323/docs/search.index/4bf42a7ae759.js deleted file mode 100644 index 3ddf54791..000000000 --- a/pr/323/docs/search.index/4bf42a7ae759.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAcAAwBoEQIA8xIBAIUTAgABBwD2FhMAAAEAnwBTABYAAQABBADzaxEAAAEAAQAjAAKgYAAAEoFhcvOREAAAAQCyAQGAAqEQAAAUBrCAE/4AAWl1dwgAAIixUBQhAAGACWkAFwewcBNPAAGhMAAAFBUKQRvyqQkAAKIGAYECoFAAABC2oKAAABLLa3ABAwA7MAAAAQAA4gACAOcMAQB3DuAA84ACbHTzgAJhZQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4c58caa77bbd.js b/pr/323/docs/search.index/4c58caa77bbd.js new file mode 100644 index 000000000..e0a471293 --- /dev/null +++ b/pr/323/docs/search.index/4c58caa77bbd.js @@ -0,0 +1 @@ +rn_("AQEA9ZkJAAABAAEAAQABAPMAAWnz8wwAACIHAQABBAKgIAAAEX2gMAAAEjViaAF/DwAApwAAgMWgEAAADPSwEBQCAAGgMAAAECgEECgB") \ No newline at end of file diff --git a/pr/323/docs/search.index/4d0ce69e1262.js b/pr/323/docs/search.index/4d0ce69e1262.js new file mode 100644 index 000000000..8b7e54269 --- /dev/null +++ b/pr/323/docs/search.index/4d0ce69e1262.js @@ -0,0 +1 @@ +rn_("AQgA9F4TAAABAFAAAQABAgGgYAAAE/9z8nsRAAABAAGAArAgD9EAAaCQAAATtmFlAQcBsEAT0QABZfWZEwAAAQABAAEAAQABAwGwQBPmAAF08k4TAAABACOFAqBAAAATfGJzAQIA9HcRAAABAAEAAQA3dAAAi7BAEzAAAaBQAAARFaCQAAATJLBQEo8AAaAwAAAMfLOYBzswAAABAABcABEA3AEBABgEAwA9BgAALAcBAMQHAQBbCQEAZwkAAHIJAACfCQAAlAw8AOMMAACeDwEAbRABAIkQAQDPEAEAUhEEAEcSAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4d1ba35b8882.js b/pr/323/docs/search.index/4d1ba35b8882.js deleted file mode 100644 index 4ffa99b3a..000000000 --- a/pr/323/docs/search.index/4d1ba35b8882.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcA9hYTAAABAJ8AUwAWAAEA8wEBaQGpCQAAAQAA9awJAAABAAEAAQABAEcBAIDGTyQ22SWtoKAAABP8sGATeAABoBAAAAmFAAogig==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4d826694c35e.js b/pr/323/docs/search.index/4d826694c35e.js deleted file mode 100644 index e367abc02..000000000 --- a/pr/323/docs/search.index/4d826694c35e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgA9a8TAAABAAEAAQABAPMAAWY7MAAAAQAABQACAMcBAABAEAQAAQIAOzAAAAEAAAsAAgBFEwUA9hMFAPMGAW07MAAAAQAADQAEANcMAAAQEQAAbxIFALcTBQBjgAOwAAl0AAFlbXM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4e219bf4cf57.js b/pr/323/docs/search.index/4e219bf4cf57.js new file mode 100644 index 000000000..f4f178f08 --- /dev/null +++ b/pr/323/docs/search.index/4e219bf4cf57.js @@ -0,0 +1 @@ +rn_("AQQBsAASqAABczswAAABAAALAAIAHBMHAAQUAwBHAAAEhqCAAAAS/bEAFBEAAbBAEmsAAbCAFB8AAbBQEqoAARUAFvRyDgAAAQABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4e3799776c96.js b/pr/323/docs/search.index/4e3799776c96.js deleted file mode 100644 index 08089fc92..000000000 --- a/pr/323/docs/search.index/4e3799776c96.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABoCAAAAzubQH7BQAAAQQBsAASkgABZPQ8EgAAAQABAAEAI4SEsKATvwABsEASkAABsHATcAABFYAAM4ADsNATrQABYWVp") \ No newline at end of file diff --git a/pr/323/docs/search.index/4e4aaeeb80ed.js b/pr/323/docs/search.index/4e4aaeeb80ed.js deleted file mode 100644 index 48957fb6c..000000000 --- a/pr/323/docs/search.index/4e4aaeeb80ed.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAABEABQAjBgAARQwAAOsMAAC3Dw0AExEAAAGEArAAESEAAbAAESMAATQ2FwgAAIigIAAAEXugMAAAEOeg4AAAEyKgMAAAEjOwcA+RAAGgIAAABizLwAg7MAAAAQAAAQEVAI4BAAAYBgAALAYAAEsJAABRCQAAbwkBACUMAABgDAEAggwBAO4MAAAVDd0AfQ8AAI4PAQB4EAAANhEAAHcSAQBFEwUAYhMAAOETAAD2EwUAERQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4eb88f9761c0.js b/pr/323/docs/search.index/4eb88f9761c0.js deleted file mode 100644 index 947535e0c..000000000 --- a/pr/323/docs/search.index/4eb88f9761c0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsHARGgABsCATxQABYWQBBQD0xRIAAAEAjgABAPMAAWMBOxIAACOAArCgE6sAAXN5AQYA9LgQAAABALoAAQCTgISgAAAAEQGxIBQHAAEJIAHzgAJlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4f2436d925ad.js b/pr/323/docs/search.index/4f2436d925ad.js deleted file mode 100644 index dabbd4a6b..000000000 --- a/pr/323/docs/search.index/4f2436d925ad.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9FURAAABAAEAAQABCgDzNhMAAAEAAQDzgQJtdAEGAPMVEQAAAQABAAGCArBgEsMAAbDQE9EAAWlyE4ACsBAM7AABZWkBBQD0dhMAAAEARgABAPMEAWPyixIAAAEAI4ICoFAAABJfZnQBAAGwABPIAAFk9BYMAAABAAEAAQDzgQJlaTcGAACJsTAT7QABoGAAABEUoFAAABClsJASmgABsEAQowABtRAHAagTAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4f6d913c8284.js b/pr/323/docs/search.index/4f6d913c8284.js new file mode 100644 index 000000000..7570c87f6 --- /dev/null +++ b/pr/323/docs/search.index/4f6d913c8284.js @@ -0,0 +1 @@ +rn_("AQUBsEASTgABZfJvEgAAAQABAAA7MAAAAQAAEQAEANQBAQBgEAgAXREDAEsSAgABCgGwQBPRAAFl9ZkTAAABAAEAAQABAEcGAACHsCAQuAIVsMAUFQABoCAAAAdksDAHuwABGIkKOjAAAAEAAAAAAA4AEAAAAKsHrAe3B7gHjwkzDDQMOgw7DIYMhwzRDNIMmhKbEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/4fa3a94ba9bc.js b/pr/323/docs/search.index/4fa3a94ba9bc.js deleted file mode 100644 index ca521f83e..000000000 --- a/pr/323/docs/search.index/4fa3a94ba9bc.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgAOjAAAAEAAAAAAAcAEAAAAMwSzRJeE18TkhOTE94T3xMjgAKwUBLAAFVmbgEAAbBAEpwAAWU7MAAAAQAADQACADoHDACyEAAAI4ACoDAAAAwrYWUzgISwUBMPAAGwEBOrAAEIYAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/4fab115143ff.js b/pr/323/docs/search.index/4fab115143ff.js deleted file mode 100644 index 5d8edbbaf..000000000 --- a/pr/323/docs/search.index/4fab115143ff.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBsAASpgABczswAAABAAALAAIAGhMHAAIUAwBHAAABhqCAAAAS+7EAFA8AAbBAEmkAAbCAFB0AAbBQEqgAARUAFvRwDgAAAQABAAEAAQUA860PAAABAAEAY4CEsEAQMgABoAAAAAdHAPAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/4fe84adc4549.js b/pr/323/docs/search.index/4fe84adc4549.js new file mode 100644 index 000000000..fdd445730 --- /dev/null +++ b/pr/323/docs/search.index/4fe84adc4549.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAcAAgCNDAUA/RABAAECAPMgEQAAAQABAAEGAPQNFAAAAQABAAEA8wYBbfQ5EgAAAQABAAEA84ACbnbzgAJhaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/50097845f0e4.js b/pr/323/docs/search.index/50097845f0e4.js new file mode 100644 index 000000000..abb055bfd --- /dev/null +++ b/pr/323/docs/search.index/50097845f0e4.js @@ -0,0 +1 @@ +rn_("AQAA9OcPAADBAgEArwDzAAFzOzAAAAEAABkACAA9BQIAPgwCAKAPAQBeEAEAnBABAIgRAQAcEwcABBQDACOAArEQFAkAAWF0") \ No newline at end of file diff --git a/pr/323/docs/search.index/504119401aae.js b/pr/323/docs/search.index/504119401aae.js new file mode 100644 index 000000000..d53000d94 --- /dev/null +++ b/pr/323/docs/search.index/504119401aae.js @@ -0,0 +1 @@ +rn_("AQkA8+gTAAABADUAAQ0BoKAAABQsd/IcFAAAAQABgQKwgBP2AAGgYAAAFBpkczcBAIaFoEAAABPMsGAToABLApAK") \ No newline at end of file diff --git a/pr/323/docs/search.index/5259902f1872.js b/pr/323/docs/search.index/5259902f1872.js deleted file mode 100644 index 6698dd4da..000000000 --- a/pr/323/docs/search.index/5259902f1872.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9FMSAAABAJoAAQABCAD0/xIAAAEAgwABADMBA7BAEtQAAWFiZfX3DAAAAQDuAkgCwwABAAD1QAwAAAEAKwMBAAEA84ACc3UjgQKwgBL5AAFydAGAArBwEo0AAaAQAAAM2256AQYA9C0QAAABALEAAQBzAISgoAAAFCoCART18QUAAAEA2A1QAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/52f2f408bfbd.js b/pr/323/docs/search.index/52f2f408bfbd.js new file mode 100644 index 000000000..b702c1b12 --- /dev/null +++ b/pr/323/docs/search.index/52f2f408bfbd.js @@ -0,0 +1 @@ +rn_("AQUA9KgQAAABAAEAAQATgAKgsAAAE3FjcwEEAPN0DwAAAQABAAGAAqBQAAAQeqBgAAAQ6mlyAYACoCAAAAyCoGAAABDpZWgBAQGgIAAAEOhi834MAAABAAEAE4ECoBAAAAyBYWMBgAKgEAAAEz2wMA0VAAFlcyMAAqBAAAAT0GV38u8TAAABAAEGAPSWEgAAAQB6AQEAEwACsGASmAABZm3y5wwAAAEAAQEA9LUQAAABAKsAAQAjgAKgsAAAE1hkZwGCAqCgAAASxKBAAAAQIm9284ADZ25yAYACoEAAABKRoDAAAA9xaGw3PwAAirBAD3IAAaAwAAAMfTRgVzswAAABAAAaAAQA3gEAABwEDgAMDAkA2AwAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5324e2ec6b58.js b/pr/323/docs/search.index/5324e2ec6b58.js new file mode 100644 index 000000000..423c87e9e --- /dev/null +++ b/pr/323/docs/search.index/5324e2ec6b58.js @@ -0,0 +1 @@ +rn_("AQABsPAUFQABa/WMEwAABQABAAEAJAATAAKxABQCAAFkbvJiEgAAAQATgAKwEAxmAAFlbwEFADswAAABAAANAAEAQhENAAECAPO2DwAAAQABAAECAPSyDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBgEAgAAYACoEAAABC0sMATxwABZXMBAQA7MAAAAQAAEwABANMPEwAjgAKwkBM7AAFpbTc2AACKoBAAAAw8oFAAABOWsHATegABoCAAAAz2nFUR9bkHAADUCAEAAQABAPOAAmx0") \ No newline at end of file diff --git a/pr/323/docs/search.index/53369ac5a189.js b/pr/323/docs/search.index/53369ac5a189.js deleted file mode 100644 index fcac54a8c..000000000 --- a/pr/323/docs/search.index/53369ac5a189.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoAAAABLIc/SBDwAAuAMBAAIAAQECsGATqwABsEAT5AABZHQ7MAAAAQAADQAGAIISAwDlEgEATBMBAGMTAQDaEwEA/hMBAAEBA7BwEZwAAbCAEmAAAYAS9QAQ7G1uczswAAABAAALAAIARAkFANoSBQCjgISgsAAAEzuxcBQdAAEIQIIBAQA7MAAAAQAABgABACMUBgDzBgFl9NgTAAABAC4AAQATAQOwQBQAAAGgYAAAE4FhcnUBJRAAAFcBAACFsRATqwABsDASrwE0EYEIOzAAAAEAAAYAAgACEwAAZxMFAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5347b986d048.js b/pr/323/docs/search.index/5347b986d048.js deleted file mode 100644 index a3e891d04..000000000 --- a/pr/323/docs/search.index/5347b986d048.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9KwQAAABAHsBAQAjgQKgYAAAEr1iaQEAAPSgDwAAAQDoAAQBI4ECsFAS0AABZXMTgAKwAAezAdxhcgGJArAgE9oAAaAwAAAT42lwAQIA9KISAAABAIkAAQABAwD01hIAAAEAjgABAPOFAmJ1AQIA9BgRAAABAGABAQDzgAJpbLcBAACFoCAAABKqEgEMOjAAAAEAAAAAABYAEAAAANMHXwhgCGEIYggqDFwMXQxeDF8Mxw+AEOwS7RIjE54T5hPnE+kT9BP1ExgUHBQ=") \ No newline at end of file diff --git a/pr/323/docs/search.index/536138962c01.js b/pr/323/docs/search.index/536138962c01.js new file mode 100644 index 000000000..96a936fba --- /dev/null +++ b/pr/323/docs/search.index/536138962c01.js @@ -0,0 +1 @@ +rn_("AYkCsCAT3AABoDAAABPlaXATAAOgIAAAEqyggAAAEr9laXX1KwwAADIAAQABAAEAAYYCsJAUAAABoAAAABJGYmQBAgD0MAwAAAEALwMBAAEAADswAAABAAAGAAIABBMAAGkTBQAjgQKwQBPXAAFlc1OAA7AACYkAAWV4eQGAArAgEqAAAbAACYcAAXJ4RwEAAMawwBMwAAGgEAAAERawQAzrAAGgMAAAEJwARIAL9OgFAAABAAgHAQDXAAAAhaAwAAAQGqAwAAAS9BABEzowAAABAAAAAAAKABAAAABPCVAJUQmBCYIJNQwxEDYQ7RDuEIMS") \ No newline at end of file diff --git a/pr/323/docs/search.index/543c1d58de30.js b/pr/323/docs/search.index/543c1d58de30.js new file mode 100644 index 000000000..3632af33c --- /dev/null +++ b/pr/323/docs/search.index/543c1d58de30.js @@ -0,0 +1 @@ +rn_("AQACoDAAABDAoJAAABMJMGj1oBEAAHMBAQABAAEAE4ACoDAAAA+BZHIjAYSwEAlLAv+wIAwdAPegMAAAD6USABj1EgYAAC8AAQABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/54422e1b039e.js b/pr/323/docs/search.index/54422e1b039e.js new file mode 100644 index 000000000..84a288dcc --- /dev/null +++ b/pr/323/docs/search.index/54422e1b039e.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQD4EwUA8w0BbTswAAABAAAFAAEAuRMFACMDhLBAEk4AAaEAAAAT/qAAAAATgzAIAjswAAABAAAUAAUA1xAHAG8SAQDiEgQAsRMEABsUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/546c31e19f10.js b/pr/323/docs/search.index/546c31e19f10.js new file mode 100644 index 000000000..d8d7f56fd --- /dev/null +++ b/pr/323/docs/search.index/546c31e19f10.js @@ -0,0 +1 @@ +rn_("AQEAOjAAAAEAAAAAAAkAEAAAAJAPkQ96EDgReRJ6EmQT4xMTFBQUAYACsOATXgABsDATdgABZHDDgISxQBQjAAGgkAAAEyUCgAk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/547e81c6465c.js b/pr/323/docs/search.index/547e81c6465c.js deleted file mode 100644 index c65caac45..000000000 --- a/pr/323/docs/search.index/547e81c6465c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcAOzAAAAEAAAUAAQAIEwUA8wUBdDswAAABAAAIAAIAHQwAAEoMBwABBQDzJBMAAAEAAQA3AACAhbAwD/oAAaCgAAATP6AwAAATehBBSA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/556039691422.js b/pr/323/docs/search.index/556039691422.js deleted file mode 100644 index cb228ab41..000000000 --- a/pr/323/docs/search.index/556039691422.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoAAAABNWcwGbEAAAAYACsDAQMAABsFAQMgABYWkBgAKgsAAAE9ygMAAAEMdsbhcBAACFoCAAABIqoCAAAAlLsIATqQABEJEEOjAAAAEAAAAAABAAEAAAACYFWwVcBV0FXgVfBWAFtQlnD2gPyBJ6E4ETjROOE6UTphMBAgA6MAAAAQAAAAAACgAQAAAAnwmgCc4P6hLrEhYTFxO2EwkUHxQgFAEAADswAAABAAAGAAIAAhMAAGcTBQABAgGwMBLYAAFjOjAAAAEAAAAAABMAEAAAAP4Mfw8SEBMQFBAZEBoQHhAfELEQJREpESoRKxGbEc4SzxKnE8wTzRMBggKwMBOfAAGwQBERAAFhZQEBAbBAE88AAWX1lxMAAAEAAQABAAEAE4ACoBAAAAwpYW/HBwAAx7BAE9UAAaCAAAASJQBMiAnyVggAANcDAYACoIAAABLxoJAAABLLYWsBAwDzSRIAAAEAAQAjgISxIBQdAAGgAAAACX+gMAAAD4YEBAYBCAD1JRQAAAEAAQABAAEAE4oCsEAUAAABYXKTAISgwAAAEsehAAAAE4oBQAby2QcAAAEAAQMBoEAAABGVZfMIDQAAswMBAAEFAPQyEwAAAQABAAEAU4ADsAAS7AABZXN0AQYA9AsUAAABAAEAAQDzCAFt9DcSAAABAAEAAQABAgA7MAAAAQAABQABAPYTBQDzDQFtOzAAAAEAAAUAAQC3EwUAAQQA9xANAAABAC8FDAABAIMAAQABAwGAEvUAEOxzOzAAAAEAAAUAAQDaEgUAI4ICoJAAABPLaXMBgAKgsAAAE5SwMBLsAAFudAGAAqCAAAATB7BAEZAAAWFlAYACoBAAABC9oAAAABEDbnIBCQGgQAAAE85l8u0TAAABACOAAqCQAAATPGlsxyMAAAqgMAAAEL6wUBKkAAGw8BQTAAGwcBMEAAGg0AAAE9cwYmNlaGlrbm92OzAAAAEAABkACwAoDAAAlRADAJ4RBAABEwAAERMDAEMTAQCKEwAAjxMCAKkTAQC1EwAAyBMBAAECAqCwAAATwaCwAAATwmNz83IQAAABAF4AAQECsFARkAABoMAAABOUaGnyQxMAAAEA84ACZWkBAQGgkAAAEwFz9FcQAAABAAEAAQABAwGwABCPAAFzAQEQAADzgANoaXQBAQGgUAAAEo9jAdYMAAABBQGwgBPIAAF2OzAAAAEAAAcAAgBuEQQANhMCAAEBAPQhEAAAAQCpAAEA84ADY2Rs92UPANGwkBIjAAGgUAAAELahAAAAE/ygoAAAEyOgEAAAB7BAP78pOzAAAAEAADcAFgBOBQEADgYCAHsHAgAWDAMA2QwBAHIPAgCBDwAApw8AAM8PAQDUEAgALBEAAGASAQBpEgEAbRIBAMMSAQDgEgQAMBMBADkTAQA8EwAArxMEAPwTAAAZFAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/55b84fdfac9a.js b/pr/323/docs/search.index/55b84fdfac9a.js new file mode 100644 index 000000000..20922d876 --- /dev/null +++ b/pr/323/docs/search.index/55b84fdfac9a.js @@ -0,0 +1 @@ +rn_("AQQA83QPAAABAAEAAYACoFAAABB6oGAAABDqaXIBgAKgIAAADIKgYAAAEOllaAEBAaAgAAAQ6GLzfgwAAAEAAQATgQKgEAAADIFhYwEGAPSWEgAAAQB6AQEAEwACsGASmAABZm3y5wwAAAEAI4ACoMAAABNYbnInDwAAiKBAAAAPcbBAD3IAAaAwAAAMfTQgFzswAAABAAAZAAMA3gEAABwEDgAMDAkAAYADsHATEQABsDASwAABsIATRQABY3JzAQABsCATEQABbzowAAABAAAAAAAJABAAAAB+EX8RgRKCEl4TXxOvE7ATHxQgFAGAArAgCXUAAbAwEssAAXJ09wAAAMegIAAAB9agYAAAD52wgBLLAAFABpII9usBAABVAnoFAQC3CQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/56c46b53f55b.js b/pr/323/docs/search.index/56c46b53f55b.js new file mode 100644 index 000000000..175854841 --- /dev/null +++ b/pr/323/docs/search.index/56c46b53f55b.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAAcAAwBqEQIA9RIBAIcTAgABBADzbREAAAEAAQAjAAKgYAAAEoNhcvOTEAAAAQCyAQGAAqEQAAAUCLCAFAAAAWl1NwQAgYexUBQjAAGACWoAFwiwcBNRAAGhMAAAFBcKQBsBAAA7MAAAAQAAewABAPUNewABAgD2GBMAAAEAnwBTABYAAQAjgQKgIAAAELNydvOAAmVvI4ACoCAAABInZHQBAAA7MAAAAQAA4AAEAD4GAABmCN0AkwwAABYRAADzgANkbnY=") \ No newline at end of file diff --git a/pr/323/docs/search.index/56c583f9554a.js b/pr/323/docs/search.index/56c583f9554a.js new file mode 100644 index 000000000..761203335 --- /dev/null +++ b/pr/323/docs/search.index/56c583f9554a.js @@ -0,0 +1 @@ +rn_("AQQBoEAAABEoZQHqDwAAAQgA9GURAAABAAEAAQABAwGgQAAAESdlAXsPAAABAgDzQwwAAAEAAQAjAAKwIAwzAAFrcvZaBwAAAQBMCesAAQADAvOAAnJ4AQMBsIATygABdjswAAABAAALAAMAuhABAHARBgA4EwIAI4ACsGAQMQC8YXIBAwGgAAAAD+lz8nkPAAABACOAAqCwAAATXWVoAQMBoEAAABC5ZfJoCQAAAQDXJQAAjLAgB8kJH7BwES8AAbAgB6sAAbCwErYAAaCAAAARaaAAAAASw78MTDowAAABAAAAAAAJABAAAADfASwELQQuBB8MIAx+DH8MgAzEEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/56fbecce9190.js b/pr/323/docs/search.index/56fbecce9190.js deleted file mode 100644 index 33c9b2d13..000000000 --- a/pr/323/docs/search.index/56fbecce9190.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoAAAABLIc/SBDwAAuAMBAAIAAQECsGATqwABsEAT5AABZHQ7MAAAAQAADQAGAIISAwDlEgEATBMBAGMTAQDaEwEA/hMBAAEBhLBwEZwAAbCAEmAAAaBgAAAS/IAS9QAQ7AAwAzswAAABAAALAAIARAkFANoSBQCjgISgsAAAEzuxcBQdAAEIQIIBAISgcAAAEiqgcAAAELGxABOrAAGwABK8AU4CIQHy7wUAAAEAAQEAOzAAAAEAAAYAAQAjFAYA8wYBZfTYEwAAAQAuAAEAEwEDsEAUAAABoGAAABOBYXJ1ASUQAAB3BAAAh6AQAAAGIrAwEq8BNKBAAAAQ1BHBDDswAAABAAAGAAIAAhMAAGcTBQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/574d932e9c29.js b/pr/323/docs/search.index/574d932e9c29.js deleted file mode 100644 index bf9caeeff..000000000 --- a/pr/323/docs/search.index/574d932e9c29.js +++ /dev/null @@ -1 +0,0 @@ -rn_("ARMA9SUUAAABAAEAAQABABOAAqDAAAASx2FvAQICoLAAABPBoLAAABPCY3PzchAAAAEAXgABgQKwUBGQAAGgwAAAE5RoafOAAmVpI4ACoLAAABMBaXQBBgGwgBPIAAF2OzAAAAEAAAcAAgBuEQQANhMCAHcAAACGoKAAABMjoJAAABLxsFAQMAABBeAC9HsHAAABAAEAfww=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5765c75d05c7.js b/pr/323/docs/search.index/5765c75d05c7.js new file mode 100644 index 000000000..e39c7ea97 --- /dev/null +++ b/pr/323/docs/search.index/5765c75d05c7.js @@ -0,0 +1 @@ +rn_("AQUA9FUSAAABAJoAAQABCAD0ARMAAAEAgwABADMAA7BAEtYAAWFiZTowAAABAAAAAAAJABAAAACXB5gH+Qz6DHwP6A8yEDMQMBLzEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/577580cd62c1.js b/pr/323/docs/search.index/577580cd62c1.js deleted file mode 100644 index face2e92c..000000000 --- a/pr/323/docs/search.index/577580cd62c1.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA82sRAAABAAEAAYICsEASTwABsEASUQABNDYBBgD0FhQAAAEACgABAPMCAXIBghMAAPMCAWX0KRAAAAEAqAABABOAAqBQAAASgWR00wCEsLATnwABEQEB95EQAAABAG8AHQABAAEAJAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5785ea697f45.js b/pr/323/docs/search.index/5785ea697f45.js new file mode 100644 index 000000000..efcb25ed3 --- /dev/null +++ b/pr/323/docs/search.index/5785ea697f45.js @@ -0,0 +1 @@ +rn_("AQMBoAAAAA/nc/M+DAAAAQABAAECAPM9DAAAyQQBACMAAqBAAAAM9WVy8kEFAAABAAGAAqBAAAATULAwEn8AAWFzAQIBoEAAABPQZfLvEwAAAQDzgARsbnJ2") \ No newline at end of file diff --git a/pr/323/docs/search.index/57f124ccc65e.js b/pr/323/docs/search.index/57f124ccc65e.js deleted file mode 100644 index a00c94103..000000000 --- a/pr/323/docs/search.index/57f124ccc65e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIA9VARAAABAAEAAQABAAGAArBgEpgAAaAgAAAQom95AQABoMAAABOoafTpEgAABwAXABsAAQMAOzAAAAEAAAUAAQAIEwUAAQYA9AsUAAABAAEAAQABAgD0oBAAAAEAnAABAAECAaBAAAASwXI7MAAAAQAABwACAJ4QAQA3EQUA84ACYW9nBwAAh7CAEsAAVbCgEzAAAQwwBzswAAABAAAOAAQARAQFADEMAABjEQMANxIDAKOAhKDwAAATQKBQAAAP/JQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/57fa81511bf9.js b/pr/323/docs/search.index/57fa81511bf9.js deleted file mode 100644 index 1dfc5bd11..000000000 --- a/pr/323/docs/search.index/57fa81511bf9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoFAAABDooFAAABIgYXUBgwKxABQfAAGw0BQRAAFrbQEEAaCgAAAUKnfyGhQAAAEAAQsA848TAAABAAEAI4ACoIAAABO1ZW9jAwOgYAAAE05lbnABOAwAACOAAqBAAAAQeG1wAQoA83ITAAABAG0AE4ACohAAABQqbG/3AAAAhaEAAAAT4gSBAzswAAABAAA7AAQALwYFALEHAAABCDMAKBEAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/582e538ca031.js b/pr/323/docs/search.index/582e538ca031.js new file mode 100644 index 000000000..4473d9477 --- /dev/null +++ b/pr/323/docs/search.index/582e538ca031.js @@ -0,0 +1 @@ +rn_("AQgA9bETAAABAAEAAQABAPMAAWY7MAAAAQAABQACAMgBAABCEAQAAQIAOzAAAAEAAAsAAgBHEwUA+BMFAPMGAW07MAAAAQAADQAEANkMAAASEQAAcRIFALkTBQBjgAOwAAl1AAFlbXM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5833b407ef6d.js b/pr/323/docs/search.index/5833b407ef6d.js deleted file mode 100644 index 117c1197e..000000000 --- a/pr/323/docs/search.index/5833b407ef6d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAA9FMQAAABAAEAAQDzAgFz9ZQJAAABAAEAAQDvBQEGAPSfEQAAAQABAAEAAQEAOzAAAAEAAAYAAQAjFAYA8wYBZfTYEwAAAQAuAAEAEwYDsEAUAAABoGAAABOBYXJ1ASUQAADzgAJhZQEDAPRbEQAAAQABAAEAAQIA9HsPAAABAA8EAQABgAOwAAfZAAGgAAAADC2wcBAyAAFucnOnBwAAh6BwAAARnrBwEn8AAZEFQTowAAABAAAAAAAMABAAAAC8Ab0BvgG/AdsHRAhFCJ0JZwy0D7UPtg++EA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/58539c8b579b.js b/pr/323/docs/search.index/58539c8b579b.js deleted file mode 100644 index ab96c4f58..000000000 --- a/pr/323/docs/search.index/58539c8b579b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIAOzAAAAEAAAUAAQD2EwUA8w0BbTswAAABAAAFAAEAtxMFAEcAAACFsAATyAABsEASTAABoQAAABP8oAAAABOBOAgCOzAAAAEAABgABgAWDAMA1RAHAG0SAQDgEgQArxMEABkUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/588805b23add.js b/pr/323/docs/search.index/588805b23add.js deleted file mode 100644 index 8767efd79..000000000 --- a/pr/323/docs/search.index/588805b23add.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYQCoCAAABDJoDAAABExaXQBAQA7MAAAAQAAfAACANQHAADBCXsAYwADsAAB7QABZGVsOjAAAAEAAAAAAAcAEAAAAD8F/AX9Bf4F2AfnEA8TEBM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/590f4c9d8e22.js b/pr/323/docs/search.index/590f4c9d8e22.js deleted file mode 100644 index 75b2838b7..000000000 --- a/pr/323/docs/search.index/590f4c9d8e22.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcA9J8RAAABAAEAAQAjAAKgcAAAEZ5hcgHbBwAAAQAA9sIQAAABAFMDAQAKAAEAAQAAOzAAAAEAAAYAAgACEwAAZxMFACOIArBAE9UAAWVzAQ0BsEATzwABZfWXEwAAAQABAAEAAQABBQA7MAAAAQAACAADAJgRAgCGEgQAlhMAANcDAACGa9tI9uj+FIQG9UkFAAABAIgHAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/591de50f5474.js b/pr/323/docs/search.index/591de50f5474.js new file mode 100644 index 000000000..4609b7e1c --- /dev/null +++ b/pr/323/docs/search.index/591de50f5474.js @@ -0,0 +1 @@ +rn_("AYkCsCAT3AABoDAAABPlaXATAAOgIAAAEqyggAAAEr9laXX1KwwAADIAAQABAAEAAYYCsJAUAAABoAAAABJGYmQBgAKwMBKgAAGgMAAAEJxydtcAAAKFoDAAABAaoDAAABL0EAETOjAAAAEAAAAAAAkAEAAAAE8JUAlRCYEJggkxEDYQ7RDuEIMSAQMBsDASugABc/NvEAAAAQABAAEAADswAAABAAAKAAMAFxECADQTAwANFAMA0wCEsCAIZAABEBgB9YgMAAABAAEAAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/596235b6df67.js b/pr/323/docs/search.index/596235b6df67.js new file mode 100644 index 000000000..3b7d6bdd7 --- /dev/null +++ b/pr/323/docs/search.index/596235b6df67.js @@ -0,0 +1 @@ +rn_("AQIBsEAT5gABdDswAAABAAANAAYAhBIDAOcSAQBOEwEAZRMBANwTAQAAFAEAE4ACsDASsQE0aXABBQDzJhMAAAEAAQATgAKgMAAAE3xleVMAA7BwE4oAAWJlcwHvDwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/5a18d0d28110.js b/pr/323/docs/search.index/5a18d0d28110.js new file mode 100644 index 000000000..a43704f3e --- /dev/null +++ b/pr/323/docs/search.index/5a18d0d28110.js @@ -0,0 +1 @@ +rn_("AQIA9KQSAAABAIkAAQABAgD0ohAAAAEAnAABAAEDAPTYEgAAAQCOAAEAAQIBoEAAABLDcjswAAABAAAHAAIAoBABADkRBQDzAIQDQAg6MAAAAQAAAAAAFQAQAAAArQeuB68HjQ+ODy8QMBBOEE8Q4RDiEJARkRGiEqMSrxKwEr8S6RLqEiwTLRM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5a23f547788d.js b/pr/323/docs/search.index/5a23f547788d.js deleted file mode 100644 index 300e99974..000000000 --- a/pr/323/docs/search.index/5a23f547788d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCsQAUHwABsNAUEQABa20BCgDzchMAAAEAbQATgAKiEAAAFCpsbzMBhKBgAAASIKEAAAAT4gSAAzswAAABAAA0AAIAsQcAAAEIMwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5a45b3b18493.js b/pr/323/docs/search.index/5a45b3b18493.js deleted file mode 100644 index 82c11c6e0..000000000 --- a/pr/323/docs/search.index/5a45b3b18493.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgA9CEQAAABAKkAAQABAAGggAAAFBlm8k0JAADBCQEHAPXgEgAAAQABAAEAAQAjgAKgUAAAEQZhb/MCAmZzOzAAAAEAAAgABACvBwAA4QcFAAcNAAA/EQAAM4ADoEAAAAwnYWVv") \ No newline at end of file diff --git a/pr/323/docs/search.index/5a6385e2310f.js b/pr/323/docs/search.index/5a6385e2310f.js deleted file mode 100644 index 06def5abc..000000000 --- a/pr/323/docs/search.index/5a6385e2310f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9g0NAAABAOkCAQAkAQEAAQoA81wSAAABAJoAAQEA93IJAAABALQGAQBrAAEADwEjgAKwYBNgAAFpbwECAPTPDAAAAQDIBQEAAQUA9HYTAAABAEYAAQD3CAAAiLCQEzAAAaAgAAAM0bCQEZYAAQTcAzowAAABAAAAAAAIABAAAAAKBmIJYwl0DHUMoxCkEIsSjBI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5a813a4820e0.js b/pr/323/docs/search.index/5a813a4820e0.js deleted file mode 100644 index 25c0f0dc4..000000000 --- a/pr/323/docs/search.index/5a813a4820e0.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQBsAASpgABczswAAABAAALAAIAGhMHAAIUAwBHAAAEhqCAAAAS+7EAFA8AAbBAEmkAAbCAFB0AAbBQEqgAARUAFvRwDgAAAQABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/5ae4a918d2e4.js b/pr/323/docs/search.index/5ae4a918d2e4.js deleted file mode 100644 index fc0296e1d..000000000 --- a/pr/323/docs/search.index/5ae4a918d2e4.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQkA8+YTAAABADUAAYACsCATiwABoCAAAA+GaXkBCQGggAAAFBlmAQ4TAAABAwGwABOeAEtzAUsTAAAjgAKgIAAAD4JhcgECAPV9EAAAAQABADkCAQDzAANpdHU6MAAAAQAAAAAADQAQAAAA4Ad/CeUPWxB2EqYSpxJPE1ATVhN/E4ATnBOdEwGAArBAEIEAAaAgAAATymN0E4ACsLATKAABYWwBDAGgoAAAFCp38hoUAAABAAEBAPOoDwAAAQABAPOAAnR1AQMA80kSAAABAAEAAQEA9eEQAAABAAEAAQABACOAArCQE3gAAWF0AQQBoEAAABPOZfLtEwAAAQABAgA7MAAAAQAACwAFAAUQBQBYEgEAchMBAMETAADgEwAAE4ICsPAUHQABZmcBgQKwgBP0AAGgYAAAFBhkcwEIAPWvEwAAAQABAAEAAQDzAgFm9UAQAAABAAEAAQABACOAA6AgAAAPgLCgE/IAAWRydHfvAQCNsBATqQABsOATqwABl9UOOzAAAAEAABEBEgA2BAEAPAUCABoGAgAlBwQAUAgDAD0MAgBoDAEA5wwBAHcO4ACeDwEAXBABAG0QAgCaEAEALxEAAIYRAQBoEgAAGhMHAAIUAwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5b38d17093ab.js b/pr/323/docs/search.index/5b38d17093ab.js new file mode 100644 index 000000000..2f811d8d4 --- /dev/null +++ b/pr/323/docs/search.index/5b38d17093ab.js @@ -0,0 +1 @@ +rn_("AQIBoKAAABLrZPJcBwAAAQABgQKgMAAAE96gUAAAE3FjcwEEA7AgEPEAAaBQAAARlqBAAAATqmxzdvKlEwAAAQDzAANhZW/yOwQAAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/5bb7576d5482.js b/pr/323/docs/search.index/5bb7576d5482.js new file mode 100644 index 000000000..8332e3de9 --- /dev/null +++ b/pr/323/docs/search.index/5bb7576d5482.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAkAAQDzEAkAAQIAOzAAAAEAABMAAQDTDxMAE4GEoFAAABC0oBAAAAw8oCAAAAz2HEAAE4ACsFAS+wABYW4zgAOwMA99AAFpc3Q=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5beaadc1f163.js b/pr/323/docs/search.index/5beaadc1f163.js new file mode 100644 index 000000000..696880598 --- /dev/null +++ b/pr/323/docs/search.index/5beaadc1f163.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABGXZfMKDQAAswMBAAEGAPS6EAAAAQC6AAEAE4EDoAAAABEDsSAUCQABYWRuARQA9ScUAAABAAEAAQABAAEGAbCAE8oAAXY7MAAAAQAABwACAHARBAA4EwIAVwAAAYagwAAAEwOgoAAAEyWgkAAAEvOwUBAyAAEF4AL0fAcAAAEAAQCADAEGAPNeEgAAAQCaAPOAA3J0dgEDADswAAABAAAHAAIANBMDAA0UAwDzAAFzAc0TAADzAAFzAZYRAABnAQCAhqAwAAAMVLAAEu4AAaAQAAAM8BJADg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5c64021fc741.js b/pr/323/docs/search.index/5c64021fc741.js deleted file mode 100644 index 213a4879a..000000000 --- a/pr/323/docs/search.index/5c64021fc741.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBsIASyQABdvS5CQAAAQC2CQEA8wABYwF1EgAAAQICoLAAABPBoLAAABPCY3PzchAAAAEAXgABgQKwUBGQAAGgwAAAE5RoafOCAmVp84ACZW8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5d359e55eab7.js b/pr/323/docs/search.index/5d359e55eab7.js deleted file mode 100644 index 6ba04bd57..000000000 --- a/pr/323/docs/search.index/5d359e55eab7.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCoJAAABOjoJAAABOkMjMBgAKgUAAAE8uggAAAEvhjZfMAAnN08qAHAAABAAEAAaCwAAATP2Lz0xIAABcBAQAjgAKwAA+iAzBkcgEAAbCAE6kAAXDypRMAAAEAQwADoCAAABEDoEAAABJAZGVz86cPAAAcAwEAE4ICoCAAABOVZWnzgAJjZQEEADswAAABAAAJAAQAqgkBAEUQAwCDEAEAYBMBALOAhKCQAAAT/BABQg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5d6375c5609f.js b/pr/323/docs/search.index/5d6375c5609f.js new file mode 100644 index 000000000..b1fcd57de --- /dev/null +++ b/pr/323/docs/search.index/5d6375c5609f.js @@ -0,0 +1 @@ +rn_("AQEA9RcGAAA+DAEAmgABAAEAADswAAABAAAHAAMA5wEBAHoMAQDMDwMAAYMCsHATEQABsIATRQABY3MBBQD0uBIAAAEAigABAPMIAWP1BBAAAAEAvAABAAEAAQABoDAAABEudPRrEAAAAQAXAAEAAYACoAAAABA9oAAAABA+NDbzAwF2AQcNAACXAAAAhbCgEp4AAbAgDDoAAbCAEoEAAQkEBjowAAABAAAAAAAJABAAAABvB3AHcQdIDKgPBBE2EjcSOBLJEwECAPSkEgAAAQCJAAEAAQMA9NgSAAABAI4AAQDzAAJidTowAAABAAAAAAAVABAAAACtB64HrweND44PLxAwEE4QTxDhEOIQkBGREaISoxKvErASvxLpEuoSLBMtEwEBAPQTBgAAAQDjBgEAtwAAgIWxABOvAAGwAAe3AAEMCEQBCAA7MAAAAQAABQABAGQSBQDXDQAAiLBAENEAAaAgAAANEbkACzswAAABAAAPAAUAagABAGcHBwDGBwEAIxABAM0QAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5dd3ab5f1ef1.js b/pr/323/docs/search.index/5dd3ab5f1ef1.js new file mode 100644 index 000000000..f8ab23906 --- /dev/null +++ b/pr/323/docs/search.index/5dd3ab5f1ef1.js @@ -0,0 +1 @@ +rn_("AQYAOzAAAAEAAAUAAQDcEgUAg4YEsFASpgABsHATBgABoNAAABPZYm5vdhOAArAQEn8AAXByAQABoDAAABGPdAElEwAA8wIBc/JQEAAA2QITgAKgYAAAEEFldQECAPYPDQAAAQDpAgEAJAEBAAGAArAAEmsAAYAOdjFAAWVzAQEA87oHAABBCwEAY4ADsIATdgABYm5084ADYW9yARQA9AQUAAABAAEAAQABAAA7MAAAAQAABgACAAQTAABpEwUAI4gCsEAT1wABZXMBBAGwMBLaAAFj8ysRAAABAAEAAQUAOzAAAAEAAAgAAwCaEQIAiBIEAJgTAADzAgNjZXDz1AwAAAEAAQBjgAOgYAAAD6Rlb3UBAAGwYBOBAAFlAbYTAADzAgFz9ncJAAABAAEAcQc/AAoBAQgA80sSAAABAAEAQ4ADoBAAABMIoOAAABOMZG9zAQIBoFAAABEqcgHKEAAAAYACoEAAABBNoHAAABPEYWUBAAA7MAAAAQAACwACAF4HBQBkEgUA84ACdHYBAQD0UQgAAAEAAQABAPMAAm5y9m0JAABQAHAGAQCxAAEA8wCHMJEM9X0SAAABAHkAAQDsAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5dd8acc05993.js b/pr/323/docs/search.index/5dd8acc05993.js new file mode 100644 index 000000000..af9c57c2f --- /dev/null +++ b/pr/323/docs/search.index/5dd8acc05993.js @@ -0,0 +1 @@ +rn_("AYICsCAQ6wABsDARYwABZXABBQD0lw8AAAEA4wABAFOAA7CQEZQAAWFpcgGBArCgE40AAbCgE48AAWNyxwAAAIWwUAz7AAGgIAAACbygIAAADDU8AAE6MAAAAQAAAAAACgAQAAAAjAGNASsEZAdzCXQJKRAqEJUQlhClEQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/5dd964887ac3.js b/pr/323/docs/search.index/5dd964887ac3.js deleted file mode 100644 index 8a7fef51e..000000000 --- a/pr/323/docs/search.index/5dd964887ac3.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBsFAPiQABY/QrBQAAAQABALUAAQABoIAAABQZZgEOEwAA8wYBc/IHDQAAOAQzAAOgYAAAE/1jaXPyeREAAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/5e6ac22b141e.js b/pr/323/docs/search.index/5e6ac22b141e.js new file mode 100644 index 000000000..085c3c7b4 --- /dev/null +++ b/pr/323/docs/search.index/5e6ac22b141e.js @@ -0,0 +1 @@ +rn_("AQACoCAAABMIsDARigABaXTyfRIAAAEAAQMAOzAAAAEAAAYAAQAJEQYAAQgA9G8TAAABAH4ABQATAQOwkBPOAAGwUBK+AU5nbHA7MAAAAQAAEgAJABcQAgCNEQEArRIBAM4SAQD/EgEAYBMBAJQTAQCjEwEA4BMBAAGAArAwEYYAAbAwEYgAAXJ2AQQBsAASQwABZDswAAABAAAFAAIAghEDAG0SAQC3BQABh7BQEkUA/bBgErwAAZQJAzswAAABAAAKAAUA5AwCAHcOAQCwEAAANBEDALQSAAAjgAKgEAAADONubwEDAPMUEAAAAQABACOAAqAAAAAMKWRyYwGEoFAAABExoDAAABBMFAEQ8vwHAAABABOAArBgEmIAAWNnE4ADoLAAABOMoEAAAA52ZW9z") \ No newline at end of file diff --git a/pr/323/docs/search.index/5fd52e1e09cb.js b/pr/323/docs/search.index/5fd52e1e09cb.js new file mode 100644 index 000000000..d99171b34 --- /dev/null +++ b/pr/323/docs/search.index/5fd52e1e09cb.js @@ -0,0 +1 @@ +rn_("AQIA9YYSAAABAN8AdgABABOAArAwErEBNGlwAQYA8yYTAAABAAEA8wICYnMB7w8AAPMBAWbyyQcAAB8JAQIA9NgSAAABAI4AAQAzgAOwQBKmAAFpdXk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/5ff8ca3dd389.js b/pr/323/docs/search.index/5ff8ca3dd389.js new file mode 100644 index 000000000..a2fc57dfb --- /dev/null +++ b/pr/323/docs/search.index/5ff8ca3dd389.js @@ -0,0 +1 @@ +rn_("AYMCsCARKQEKoJAAABOYbXABggKwoBONAAGwoBOPAAFjcgECAaBAAAARKGUB6g8AADOAhLAgEKUAAbAgD2wAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/323/docs/search.index/601ea84f2bcd.js b/pr/323/docs/search.index/601ea84f2bcd.js new file mode 100644 index 000000000..9d2590151 --- /dev/null +++ b/pr/323/docs/search.index/601ea84f2bcd.js @@ -0,0 +1 @@ +rn_("AYACoGAAABN9oKAAABNxYWkBCwD0ExMAAAEAAQABAAEEAPRREAAAAQABAAEAAQQA9KgQAAABAAEAAQDzgAJscvMAA2NkcwGfEAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/602101d51cf9.js b/pr/323/docs/search.index/602101d51cf9.js new file mode 100644 index 000000000..2b8d3ad19 --- /dev/null +++ b/pr/323/docs/search.index/602101d51cf9.js @@ -0,0 +1 @@ +rn_("AQACoCAAABMIsDARigABaXTyfRIAAAEAAQMAOzAAAAEAAAYAAQAJEQYAAQgA9G8TAAABAH4ABQATAQOwkBPOAAGwUBK+AU5nbHA7MAAAAQAAEgAJABcQAgCNEQEArRIBAM4SAQD/EgEAYBMBAJQTAQCjEwEA4BMBAAGAArAwEYYAAbAwEYgAAXJ2AQQBsAASQwABZDswAAABAAAFAAIAghEDAG0SAQC3BQABh7BQEkUA/bBgErwAAZQJAzswAAABAAAKAAUA5AwCAHcOAQCwEAAANBEDALQSAAAjgAKgEAAADONubwEDAPMUEAAAAQABACOAAqAAAAAMKWRyYwCEoFAAABExoDAAABBMFAEQ8vwHAAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/604c5b6bbfc7.js b/pr/323/docs/search.index/604c5b6bbfc7.js new file mode 100644 index 000000000..2dbe1a2cd --- /dev/null +++ b/pr/323/docs/search.index/604c5b6bbfc7.js @@ -0,0 +1 @@ +rn_("AQIA9VIRAAABAAEAAQABAAGAArBgEpoAAaAgAAAQpG95AQABoMAAABOqafTrEgAABwAXABsAAQYA9A0UAAABAAEAAQABAgD0ohAAAAEAnAABAAECAaBAAAASw3I7MAAAAQAABwACAKAQAQA5EQUA84ACYW9nAwAAhrCAEsIAVbCgEzIAAQwwBjswAAABAAAIAAMAMgwAAGURAwA5EgMA84ICZWkjAAKggAAAFBtmaPJOCQAAwgkBAAKwEAwbAAGwMBPBAAFlcPSVDwAAAQDiAAEAAQADsEATjwABoUAAABPzoFAAABGMYmNmAXIHAADzgAJtbgEBA6CwAAATTbBAEF4AAaBgAAAROGNldPRZBwAApQhHAv0AAQUAOzAAAAEAAAUAAQBkEgUAAQcA9eISAAABAAEAAQABACOAAqBQAAARCGFv8wCFIEkCOzAAAAEAAAgABACwBwAA4gcFAAkNAABBEQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/604fecc750c5.js b/pr/323/docs/search.index/604fecc750c5.js new file mode 100644 index 000000000..d984bf36d --- /dev/null +++ b/pr/323/docs/search.index/604fecc750c5.js @@ -0,0 +1 @@ +rn_("AQAAOzAAAAEAAAYAAgAEEwAAaRMFACOIArBAE9cAAWVzAQQBsDAS2gABY/MrEQAAAQABAAEFADswAAABAAAIAAMAmhECAIgSBACYEwAA8wEDY2Vw89QMAAABAAEAAQIA9m0JAABQAHAGAQCxAAEA84ACbnI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/60d3db74dc82.js b/pr/323/docs/search.index/60d3db74dc82.js new file mode 100644 index 000000000..1ae1ffd32 --- /dev/null +++ b/pr/323/docs/search.index/60d3db74dc82.js @@ -0,0 +1 @@ +rn_("AQIBsEAT5gABdDswAAABAAANAAYAhBIDAOcSAQBOEwEAZRMBANwTAQAAFAEAE4ACsDASsQE0aXABBQDzJhMAAAEAAQATgAKgMAAAE3xleVMBA7BwE4oAAWJlcwHvDwAAE4ADsDAT9AABsCAHZQABZWxz") \ No newline at end of file diff --git a/pr/323/docs/search.index/611bcb3d4014.js b/pr/323/docs/search.index/611bcb3d4014.js deleted file mode 100644 index 8f247e2c3..000000000 --- a/pr/323/docs/search.index/611bcb3d4014.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQsA9BgTAAABAA8AAQABBAGwQBLUAAFlAS4SAADzAAF0AcoTAAABDQDz4RMAADAAAQABEADzCRQAABYAAQDzgQNrbXMBAAA7MAAAAQAABgACAAITAABnEwUAI4YCsEAT1QABZXMBBAD0aRAAAAEAFwABAPOAA2NydDOAhLBgEIUAAaAQAAAGLAEICQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/617a2f2dcad5.js b/pr/323/docs/search.index/617a2f2dcad5.js new file mode 100644 index 000000000..448d9aa0c --- /dev/null +++ b/pr/323/docs/search.index/617a2f2dcad5.js @@ -0,0 +1 @@ +rn_("AQcA9VMTAAABAAEAcAABAAEHAPNvEAAAAQABAAGAAqAAAAAHvaAQAAAJXWR48wEBZfLKDwAAAQDnAAAAhbBgDPEAAbBADG4AATIIAfRRBQAAYgIeCQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/618c79cfcc3c.js b/pr/323/docs/search.index/618c79cfcc3c.js new file mode 100644 index 000000000..19ad6cdd9 --- /dev/null +++ b/pr/323/docs/search.index/618c79cfcc3c.js @@ -0,0 +1 @@ +rn_("AQEAOzAAAAEAAAYAAQAlFAYA8wYBZfTaEwAAAQAuAAEAEwMDsEAUAgABoGAAABODYXJ1AScQAAAjgAKAESAAEAFkcg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/61b15e853fbd.js b/pr/323/docs/search.index/61b15e853fbd.js new file mode 100644 index 000000000..95335e6d2 --- /dev/null +++ b/pr/323/docs/search.index/61b15e853fbd.js @@ -0,0 +1 @@ +rn_("AQkA8+gTAAABADUAAQwBoKAAABQsd/IcFAAAAQABAQDzqg8AAAEAAQDzgAJ0dQGBArCAE/YAAaBgAAAUGmRzNwIAgIagQAAAE8ywYBOgAEuwMBONAAECkA4BAAGwUBCxAAFj9MAHAAABAAEA8wHDgISgEAAADIWgUAAAEi8MQAQ=") \ No newline at end of file diff --git a/pr/323/docs/search.index/61d7e34f1682.js b/pr/323/docs/search.index/61d7e34f1682.js new file mode 100644 index 000000000..3fdb3baba --- /dev/null +++ b/pr/323/docs/search.index/61d7e34f1682.js @@ -0,0 +1 @@ +rn_("AQcA9KgQAAABAAEAAQABAAD1rQkAAAEAAQABAAEAI4ACoKAAABP+ZnABAwGgkAAAE35l8mcPAAABANcAAACFoFAAABAlsUAUJQABgSkA8u4FAAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/62030eb677af.js b/pr/323/docs/search.index/62030eb677af.js deleted file mode 100644 index aa3b388e5..000000000 --- a/pr/323/docs/search.index/62030eb677af.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEAOjAAAAEAAAAAAAkAEAAAAI4Pjw94EDYRdxJ4EmIT4RMRFBIUAYACsOATXAABsDATdAABZHDDgISxQBQhAAGgkAAAEyMCgAk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/621b17540369.js b/pr/323/docs/search.index/621b17540369.js new file mode 100644 index 000000000..4ae85f6c9 --- /dev/null +++ b/pr/323/docs/search.index/621b17540369.js @@ -0,0 +1 @@ +rn_("AYECoCAAABCesFASRwABYW8BAwGgAAAAE1hzAZ0QAAAjgAKgAAAACFdydmMAA7BQERAAAWNlczswAAABAAAIAAQAVgkCAIQJAQAdEAIALxIAAAcBAICFsFAQzwABoKAAABMksPATOwABsCAPYgABRCEE") \ No newline at end of file diff --git a/pr/323/docs/search.index/628077ed8c99.js b/pr/323/docs/search.index/628077ed8c99.js new file mode 100644 index 000000000..d1311b052 --- /dev/null +++ b/pr/323/docs/search.index/628077ed8c99.js @@ -0,0 +1 @@ +rn_("AQEBsIAS6QABdfKTCQAAAQAjgAKwEAmRAAEzNgECAPSNCQAAAQBdBwEAAQIA82MMAAABAAEAE4ACsBAJiwABaXUBAgA7MAAAAQAACwACAEcTBQD4EwUA8wkBbTswAAABAAAMAAMAEhEAAHESBQC5EwUAdyAAAIqgMAAAE12wMBEvAAGgcAAAEZ2gIAAACY+wMBFjAAGwIAe0AdyzhQo7MAAAAQAAFAIEANEBAAD2ASIB1wcAAD4K7wA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/62e9a3f54499.js b/pr/323/docs/search.index/62e9a3f54499.js new file mode 100644 index 000000000..435c9c4d5 --- /dev/null +++ b/pr/323/docs/search.index/62e9a3f54499.js @@ -0,0 +1 @@ +rn_("AYMCoJAAABOloJAAABOmMjMBgAKgUAAAE82ggAAAEvpjZfMAAnN08qEHAAABAAEAAaCwAAATQWLz1RIAABcBAQAjgAKwAA+kAzBkcgEAAbCAE6sAAXDypxMAAAEAQwADoCAAABEFoEAAABJCZGVz86kPAAAcAwEAE4ICoCAAABOXZWnzgAJjZQEEADswAAABAAAJAAQAqwkBAEcQAwCFEAEAYhMBALOAhKCQAAAT/hABQg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6303a0a8f283.js b/pr/323/docs/search.index/6303a0a8f283.js new file mode 100644 index 000000000..80759be47 --- /dev/null +++ b/pr/323/docs/search.index/6303a0a8f283.js @@ -0,0 +1 @@ +rn_("AQMAOzAAAAEAAAcAAgCNDAUA/RABAAEAAbBQE60AAXIBTAUAAFMBhLBAEkkAAbBAE+YAAZgABDowAAABAAAAAAAoABAAAAA9BD4EPwQOBl8JYAmGCbQJ3QyAD+0P7g/7DygQfxCAEIEQyxAsEoQShRKGEocSrBK6ErsS5xLoEk4TTxNTE1QTVRNlE2YTxRPGE9wT3RMAFAEU") \ No newline at end of file diff --git a/pr/323/docs/search.index/6342e9f2829a.js b/pr/323/docs/search.index/6342e9f2829a.js new file mode 100644 index 000000000..52030abb6 --- /dev/null +++ b/pr/323/docs/search.index/6342e9f2829a.js @@ -0,0 +1 @@ +rn_("AYACsFATigABoAAAAAldY3QBAQA7MAAAAQAABQABAAoTBQABAwD09QwAAEwGlgABAAGAAqAQAAAMhaBQAAASL2NkAQIA81IJAAAPAwEA8wABbfMwBQAAtAABAAGBAqAQAAASxKBwAAATBWFj8wABcvO9BwAAkwkZAGcPAICIsDAPhQDGoDAAABP+KTAj") \ No newline at end of file diff --git a/pr/323/docs/search.index/63ffb5fa209f.js b/pr/323/docs/search.index/63ffb5fa209f.js new file mode 100644 index 000000000..15bc8709c --- /dev/null +++ b/pr/323/docs/search.index/63ffb5fa209f.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAABYACQAHEAUAEBEBAJoRAgBaEgEAiBIEAHQTAQCYEwAAwxMAAOITAAAjgAOh8AAAFCyw8BQfAAFjZmcjgAKwUBMyAAFtbgEFAPN0EwAAAQBtAPMEAWMBMREAAAEBAbBwE8cAAXA7MAAAAQAACQACADcIBwB2EAEAAQIAOzAAAAEAAAsAAgBHEwUA+BMFAPMBAW07MAAAAQAADQAEANkMAAASEQAAcRIFALkTBQATAAKgMAAADN1lc/KeEQAAAQABggKwcBOnAAGgQAAAELxpc/cBAICGsJATjQABkEkI") \ No newline at end of file diff --git a/pr/323/docs/search.index/642a5cb13021.js b/pr/323/docs/search.index/642a5cb13021.js deleted file mode 100644 index f8b6a53a7..000000000 --- a/pr/323/docs/search.index/642a5cb13021.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABoLAAABM/YvPTEgAAFwEBAAGAAqAgAAAPgqAwAAATS2Fy8wABdAHgBwAAAQQBoEAAABPOZfLtEwAAAQABBQA7MAAAAQAACwAFAAUQBQBYEgEAchMBAMETAADgEwAA4wGEoDAAAA+AhQACOzAAAAEAAAsABQAaBgIAJQcEAGgMAQAvEQAAaBIAAGOAA7AAD6IDMGRucg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6465615c7c4e.js b/pr/323/docs/search.index/6465615c7c4e.js deleted file mode 100644 index 28f3420ec..000000000 --- a/pr/323/docs/search.index/6465615c7c4e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQwBoKAAABQqd/IaFAAAAQABAQDzqA8AAAEAAQABiYSggAAAFBigQAAAE8qgYAAAE+mgkAAAFBwCgAoTgYSgoAAAE2KxoBQoAAGw0BPDAAEEAQnzgANhdHU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/64816476bdd2.js b/pr/323/docs/search.index/64816476bdd2.js deleted file mode 100644 index fc09247d1..000000000 --- a/pr/323/docs/search.index/64816476bdd2.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9DITAAABAAEAAQABDwD0CxQAAAEAAQABAAEAAbDwFBMAAWv1ihMAAAUAAQABACQAAQIA9CEQAAABAKkAAQD3AAAAhbEAFAAAAYkwAPJgEgAAAQATgAKwEAxlAAFlbwEFADswAAABAAANAAEAQBENAAECAPO0DwAAAQABAAECAPSwDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBeEAgAAYACoAAAAAw7sDATzwABcXIBgAKgQAAAELKwwBPFAAFlcwEBADswAAABAAATAAEA0Q8TACOAArCQEzkAAWltdzYAAIqgUAAAE5SwcBN4AAGgIAAADPScVRE7MAAAAQAACQADALgHAACLEAMAlxMEAAECAbAwERoAAXABLBIAALOAhKAgAAARfwIoBA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/648a36bbbcb1.js b/pr/323/docs/search.index/648a36bbbcb1.js deleted file mode 100644 index b0eaa0b87..000000000 --- a/pr/323/docs/search.index/648a36bbbcb1.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgA9FwTAAABAFAAAQABAgGgYAAAE/1z8nkRAAABAAGAArAgD88AAaCQAAATtGFlAQcBsEATzwABZfWXEwAAAQABAAEAAQABAwGwQBPkAAF08kwTAAABACOFAqBAAAATemJzAQIA9HURAAABAAEAAQA3dAAAi7BAEy4AAaBQAAARE6CQAAATIrBQEo0AAaAwAAAMe7OYBzswAAABAABbABEA2wEBABcEAwA8BgAAKwcBAMMHAQBaCQEAZgkAAHEJAACeCQAAkww7AOEMAACcDwEAaxABAIcQAQDNEAEAUBEEAEUSAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/64a47a036ee9.js b/pr/323/docs/search.index/64a47a036ee9.js deleted file mode 100644 index 4972d4330..000000000 --- a/pr/323/docs/search.index/64a47a036ee9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAAAUAAQBiEgUAAQACsEATLgABoKAAABO0ZXI6MAAAAQAAAAAABwAQAAAAKwcsB8MHxAecD50PhxCIEFOAA6CgAAATlGdpcw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/64d11c8fb329.js b/pr/323/docs/search.index/64d11c8fb329.js deleted file mode 100644 index c8ca8e518..000000000 --- a/pr/323/docs/search.index/64d11c8fb329.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUBsEASTAABZfJtEgAAAQABggKgkAAAE6OgkAAAE6QyMwEAAqAgAAASKqAgAAAJS2ltOzAAAAEAAAwABgAmBQAAWwUFALUJAABnDwEAehMAAI0TAQDzAAFzAdQQAAABBQA7MAAAAQAABQABAAgTBQABAAD1hwwAAAEAAQABAAEA84ACZ2tzgISwsBNgAAERSAABBgGgQAAAEZVl8wgNAACzAwEAAYACoJAAABNvsFAQ+wABZ3oBgAOwUBOcAAGgEAAADBWgcAAAEvhnbnLHAQAAhaBgAAATe6BQAAATyxUBBDswAAABAAARAAQA0wEBAF4QCABbEQMASRICAAGFArBwE4gAAbAwEuUAAWVzEwADsEAPhwABoGAAABEwYmVzOzAAAAEAABIABAAgBgEALwcKAP0PAQBPEAMAAQMA9BkQAAABAAQAAQABCwGgQAAAE85l8u0TAAABAPOAAmRy8wACZW87MAAAAQAABwACAP0HAwA+CAMAAQIAOzAAAAEAAAkAAQALDAkAAQAA9FMQAAABAAEAAQABAQA7MAAAAQAA3gACAD0GAABlCN0AYwADsBASpAABZWlzOjAAAAEAAAAAAAwAEAAAAF0JlAmVCZYJlwkvDDAMXg9fD4YPohAEEwUT44CEsCAHugABEUAEAQGEsDAMHgABoEAAAAzYoIAAABECsEAM2QABAAAdAeIBAAABBQD1URMAAAEAAQBwAAEAE4ACoAAAAAdjYWcBAwD0IxIAAAEAVAEBAPOAAmlvAQEAOzAAAAEAAN4AAgDXBwAALQvdAAEIAbBAE88AAWX1lxMAAAEAAQABAAEA84ECZ3QBAQGgcAAAEZtpOzAAAAEAAPAAAgDWBwAAPQrvAAEFAPNtEAAAAQABAPOAAmFvE4ACoEAAAAxFcnUBgAKgAAAAB7ygEAAACVxkeBMAArAgDP8AAWVy8sgPAAABABOAArCwFBMAAXJ4AQoA9BETAAABAAEAAQATgAKwEBC2AhVhZQEEAPRPEAAAAQABAAEAAQQAOjAAAAEAAAAAAAcAEAAAALYStxLFEsYSQRNCE1QTVRMBBAD0phAAAAEAAQABAAEDAPNcEgAAAQCaAPOAhAFIAce/AwCPoIAAABJesGAM7wABsEAMbQABsTAT6gABP6kfOzAAAAEAAE0AIwBoAAAAbAAAAEcFAQBQBQAA+QUBAKAHAQCqBwEAsgcAALYHAQCOCQAAoQkHALcJAQAdDAAAMgwBADkMAQBKDAcAbQwBAIUMAQDPDAEAlQ8BAAIQAQB5EAEAhRABAJ0QAAC/EAIAzxABAOEQBAAGEQAAVREDAJIRAQA7EgAAlBIBAJgSAQDBEgAADxQBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/64e752cb7ef8.js b/pr/323/docs/search.index/64e752cb7ef8.js deleted file mode 100644 index 36769a04c..000000000 --- a/pr/323/docs/search.index/64e752cb7ef8.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9FwTAAABAFAAAQAjgAKhEAAAFApjchOAAqAgAAAJXWh5AQEBoGAAABP9c/J5EQAAAQAjgAKwIAm9AAFldQGAArAgD88AAaCQAAATtGFlAQcBsEATzwABZfWXEwAAAQABAAEAAQABBAGwABKmAAFzOzAAAAEAAAsAAgAaEwcAAhQDAEcAAASGoIAAABL7sQAUDwABsEASaQABsIAUHQABsFASqAABFQAW9HAOAAABAAEAAQCDAISgAAAAE3ywQBQTAAGwMBMuAAFIAAP3QgUAAHELAQClAAEABQABAAEDAbBAE+QAAXTyTBMAAAEAI4UCoEAAABN6YnMBAQD0dREAAAEAAQABABOAAqAQAAAJfmRst+gAAIyg0AAAEwagUAAAEROgkAAAEyKwUBKNAAGgMAAADHu3mAc7MAAAAQAAXgATANUBAQDbAQEAFwQDADwGAAArBwEAwwcBANUHAABaCQEAZgkAAHEJAACeCQAAkww7AOEMAACcDwEAaxABAIcQAQDNEAEAUBEEAEUSAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/64eb9b668729.js b/pr/323/docs/search.index/64eb9b668729.js deleted file mode 100644 index 2a28b5160..000000000 --- a/pr/323/docs/search.index/64eb9b668729.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9HYTAAABAEYAAQDzAwFj8osSAAABACOAArBwFB0AAWhvAYICsAASaQABsAARiAABZXNDAAOgoAAAE8KgIAAAEwZlaXT1exIAAAEAeQABAOwAAYECoEAAABL8sEASugABYWYBgAKwcBJgAAGgsAAAE4plbwECADswAAABAAAGAAEABxEGAAECAaBAAAATp2Xy0RMAAAEA84ACaW8BCAD0bRMAAAEAfgAFABMBA7CQE8wAAbBQErwBTmdscDswAAABAAASAAkAFRACAIsRAQCrEgEAzBIBAP0SAQBeEwEAkhMBAKETAQDeEwEAAYACsEASQwD9oCAAABDKYWkBgQKwMBN9AAGgMAAAEl9jaQEDAbAAEqYAAXM7MAAAAQAADQADAIYRAQAaEwcAAhQDAAEDAPWEEQAAAQALAQEARgHzgAJydgEAAPRBEgAAAQBQAAEA8wIBZDswAAABAAAJAAMAgBEDADwSAwBrEgEAI4ACoJAAABPBaW8TgAKw8BQPAAFsbwEFAPTdEwAACwAeAA8AI4ACoGAAABL7Z3MjgAKwkBO/AAFibPeXAwCPsHARnAABsGAT8gABsHATcAABsFASqAABtfkXOzAAAAEAADsAFwArBgAAzAcBAEQJBQBUCQAAggkAACkMAABvDAEA4gwCAAsNAQBwDgMAdQ4BAKsPBAByEAEArhAAANEQAADxEAkAMBEAADIRAwB+EQAAshIAAMkSAQDaEgUAlRMAAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/65694b9e3122.js b/pr/323/docs/search.index/65694b9e3122.js deleted file mode 100644 index 9710192a4..000000000 --- a/pr/323/docs/search.index/65694b9e3122.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsRAUAAABsBAMZQABZW8BBQA7MAAAAQAADQABAEARDQABAgDztA8AAAEAAQABAgD0sA8AAAEAAQABAAEDADswAAABAAAIAAEAXhAIAIcHAACHsKATOQABsNATxQABoFAAABOUjBQR9bgHAADTCAEAAQABAPOBAmx08wABaTswAAABAAB9AAMASwQAAJkQAACkEXsAAQUA8+YTAAABADUAAQABoEAAABCdZfNYCQAAAQDZCBOAAqAwAAAPjWRy8wABZfNVBwAAAQABAPOAAmh0AQEAOzAAAAEAAHwAAgCZEAAApBF7ABOAAqBQAAAPzml0AQgA9G0TAAABAH4ABQATAISw8BPoAC2wkBPMAAGwUBK8AU5AiQA7MAAAAQAAEgAJABUQAgCLEQEAqxIBAMwSAQD9EgEAXhMBAJITAQChEwEA3hMBANcBAACGsIARWQABoEAAAAl7CDAmOjAAAAEAAAAAAA0AEAAAAIEAggDCB7YJHAwSDawQrRAoEikSohKjEiwTLRM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/65e587e4fe56.js b/pr/323/docs/search.index/65e587e4fe56.js deleted file mode 100644 index a7ae4ae8c..000000000 --- a/pr/323/docs/search.index/65e587e4fe56.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACoIAAABJesHATnAABYXMBAAGggAAAFBlm8k0JAADBCQEHAPXgEgAAAQABAAEAAQAjgAKgUAAAEQZhb/MAAmZzOzAAAAEAAAgABACvBwAA4QcFAAcNAAA/EQAA84ACZHk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/65f055028fa9.js b/pr/323/docs/search.index/65f055028fa9.js deleted file mode 100644 index fd5cb46e4..000000000 --- a/pr/323/docs/search.index/65f055028fa9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYMCsCARJwEKoJAAABOWbXABggKwoBOLAAGwoBONAAFjcgECAaBAAAARJmUB6A8AADOAhLAgEKMAAbAgD2oAAVAUAPOAAm9y") \ No newline at end of file diff --git a/pr/323/docs/search.index/663cc194c319.js b/pr/323/docs/search.index/663cc194c319.js new file mode 100644 index 000000000..d04d70e50 --- /dev/null +++ b/pr/323/docs/search.index/663cc194c319.js @@ -0,0 +1 @@ +rn_("AQAA9sQQAAABAFMDAQAKAAEAAQAAOzAAAAEAAAYAAgAEEwAAaRMFACOIArBAE9cAAWVzAQ0BsEAT0QABZfWZEwAAAQABAAEAAQABBQA7MAAAAQAACAADAJoRAgCIEgQAmBMAANcBAACFZkLNtVqUFIQC89QMAAABAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/6642cdb55a94.js b/pr/323/docs/search.index/6642cdb55a94.js new file mode 100644 index 000000000..6fa4ca9ab --- /dev/null +++ b/pr/323/docs/search.index/6642cdb55a94.js @@ -0,0 +1 @@ +rn_("AQMBsDAS2gABY/MrEQAAAQABAAEDAYAS9wAQ7HM7MAAAAQAABQABANwSBQAjggKgkAAAE81pcwcBAIDFsFASpgABoKAAABM+sHATBgABoNAAABPZgAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/323/docs/search.index/674a3988b3e5.js b/pr/323/docs/search.index/674a3988b3e5.js new file mode 100644 index 000000000..2d8c7c8e8 --- /dev/null +++ b/pr/323/docs/search.index/674a3988b3e5.js @@ -0,0 +1 @@ +rn_("AQIA9KQSAAABAIkAAQABAwD02BIAAAEAjgABAPMCAmJ1OjAAAAEAAAAAABMAEAAAAK0HrgevB40Pjg8vEDAQThBPEOEQ4hCQEZERohKjEq8SsBK/EiwTLRM=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6757d6132188.js b/pr/323/docs/search.index/6757d6132188.js new file mode 100644 index 000000000..b2e3fa7d0 --- /dev/null +++ b/pr/323/docs/search.index/6757d6132188.js @@ -0,0 +1 @@ +rn_("AQcA9KERAAABAAEAAQAjAAKgcAAAEaBhcgHcBwAAAQAA9sQQAAABAFMDAQAKAAEAAQAAOzAAAAEAAAYAAgAEEwAAaRMFACOIArBAE9cAAWVzAQ0BsEAT0QABZfWZEwAAAQABAAEAAQABBQA7MAAAAQAACAADAJoRAgCIEgQAmBMAANcDAACGZkLNtVqUFIQG9UoFAAABAIkHAQABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/675e04200622.js b/pr/323/docs/search.index/675e04200622.js new file mode 100644 index 000000000..35b460455 --- /dev/null +++ b/pr/323/docs/search.index/675e04200622.js @@ -0,0 +1 @@ +rn_("AQAA9FUQAAABAAEAAQDzAgFz9ZUJAAABAAEAAQDwBQEGAPShEQAAAQABAAEAAQEAOzAAAAEAAAYAAQAlFAYA8wYBZfTaEwAAAQAuAAEAEwYDsEAUAgABoGAAABODYXJ1AScQAADzgAJhZQEDAPRdEQAAAQABAAEAAQIA9H0PAAABAA8EAQABgAOwAAfaAAGgAAAADC6wcBA0AAFucnOnBwAAh6BwAAARoLBwEoEAAZEFQTowAAABAAAAAAAMABAAAAC9Ab4BvwHAAdwHRQhGCJ4JaAy2D7cPuA/AEA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/678443ae36ae.js b/pr/323/docs/search.index/678443ae36ae.js deleted file mode 100644 index 8d4fdc442..000000000 --- a/pr/323/docs/search.index/678443ae36ae.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBoEAAABOnZfLREwAAAQABBAGwABKSAAFk9DwSAAABAAEAAQCnAAAChbCgE78AAbBAEpAAAbBwE3AAARWIAAGVEwAAAQABsIATqQABcPKlEwAAAQBDAAOgIAAAEQOgQAAAEkBkZXPzpw8AABwDAQDzgAJlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/679d38ece98b.js b/pr/323/docs/search.index/679d38ece98b.js deleted file mode 100644 index fec03a673..000000000 --- a/pr/323/docs/search.index/679d38ece98b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUAOzAAAAEAAAYAAQALEAYAAQMA9NwMAAABAAEAAQBjAAOgkAAAEvBocHM6MAAAAQAAAAAAEAAQAAAAHQYeBh8GKgdkCWoMdgymEKcQqBCpEEESQhKSEpMSuhK7Eg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/67d6567c6683.js b/pr/323/docs/search.index/67d6567c6683.js new file mode 100644 index 000000000..ee352875c --- /dev/null +++ b/pr/323/docs/search.index/67d6567c6683.js @@ -0,0 +1 @@ +rn_("AQMBoEAAABOpZfLTEwAAAQABBAGwABKUAAFk9D4SAAABAAEAAQCnAAAChbCgE8EAAbBAEpIAAbBwE3IAARWIAAGXEwAAAQABsIATqwABcPKnEwAAAQBDAAOgIAAAEQWgQAAAEkJkZXPzqQ8AABwDAQDzgAJlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/681f74d11d66.js b/pr/323/docs/search.index/681f74d11d66.js deleted file mode 100644 index 8ffe0e971..000000000 --- a/pr/323/docs/search.index/681f74d11d66.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9LYSAAABAIoAAQDzAAFj9QIQAAABALwAAQABAAEJAPPmEwAAAQA1AAENAaCgAAAUKnfyGhQAAAEAAYECsIAT9AABoGAAABQYZHM3AQCEhaBAAAATyrBgE54ASwKQCiOAAqBwAAARAmRyAQsBoEAAABPOZfLtEwAAAQATgAKwMBAeAAFkcvMCAWX0PggAAAEAAQABACOAArAgDB4AAWV5AQoA80kSAAABAAEAAQsAOzAAAAEAAAcAAQAaEwcAAQIA9HYTAAABAEYAAQC3AACAhbBwEnsAAbDQFA8AAWAgBgEGAPNVEgAAAQABAAEDAbBAE+QAAXTyTBMAAAEAI4UCoEAAABN6YnMTAgKgUAAAERNiZvdaCQAAAQBDAM0GAQBhAAEA84ACaXABBADzGxAAAAEAAQDzAAFwOzAAAAEAAAwAAQBIBwwAI4ADoDAAAAwssHATLgABYWVp84ADZG1uAQAA9mQJAAASA8sFAQBQAAEA8wIBZDswAAABAAAJAAMAgBEDADwSAwBrEgEAI4ACoJAAABPBaW8BgAOwMBLnAAGAEyQAEAGwUBDCAAFjc3jzAAFlAVEFAADnFQAAiaAAAAAMhKBAAAAM2LBADNkAAZBIXQHiAQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/684759f8b36f.js b/pr/323/docs/search.index/684759f8b36f.js new file mode 100644 index 000000000..d00cbe21c --- /dev/null +++ b/pr/323/docs/search.index/684759f8b36f.js @@ -0,0 +1 @@ +rn_("AQYA9JYSAAABAHoBAQATAgKwYBKYAAFmbfLnDAAAAQAjggKwQBLAAAFlbAEFAPR4EwAAAQBGAAEA8wMBY/KNEgAAAQABgQKgAAAACYCgMAAAD4hzdBOAAqAQAAAHsW5404CEsHAUHwABkEAB") \ No newline at end of file diff --git a/pr/323/docs/search.index/6977b4c99668.js b/pr/323/docs/search.index/6977b4c99668.js deleted file mode 100644 index 58a783a82..000000000 --- a/pr/323/docs/search.index/6977b4c99668.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYICsEASTwABsEASUQABNDYBBgD0FhQAAAEACgABAPMCAXIBghMAAPMDAWX0KRAAAAEAqAABAFOAA7CwE58AAWFlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/69bf077966ff.js b/pr/323/docs/search.index/69bf077966ff.js deleted file mode 100644 index 788ceacaf..000000000 --- a/pr/323/docs/search.index/69bf077966ff.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBoGAAABP9c/J5EQAAAQABCAD1rxMAAAEAAQABAAEAowCEsCAJvQABoIAAABJ2MEAIOzAAAAEAAAUAAgDHAQAAQBAEAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/69eecfc94a5f.js b/pr/323/docs/search.index/69eecfc94a5f.js new file mode 100644 index 000000000..7c66d6abd --- /dev/null +++ b/pr/323/docs/search.index/69eecfc94a5f.js @@ -0,0 +1 @@ +rn_("AQIAOzAAAAEAAAUAAQD4EwUA8w0BbTswAAABAAAFAAEAuRMFAEcAAACFsAATygABsEASTgABoQAAABP+oAAAABODOAgCOzAAAAEAABgABgAXDAMA1xAHAG8SAQDiEgQAsRMEABsUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6a91ed0cfbc4.js b/pr/323/docs/search.index/6a91ed0cfbc4.js deleted file mode 100644 index be1c40f03..000000000 --- a/pr/323/docs/search.index/6a91ed0cfbc4.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsHASYAABoLAAABOKZW8jAYSwcBGcAAGgYAAAEvyAEvUAEOwAMAM7MAAAAQAAGgAGAMwHAQBECQUACw0BAPEQCQAwEQAA2hIFAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6a9268da8c28.js b/pr/323/docs/search.index/6a9268da8c28.js new file mode 100644 index 000000000..df8829ef0 --- /dev/null +++ b/pr/323/docs/search.index/6a9268da8c28.js @@ -0,0 +1 @@ +rn_("AQQBsEAT0QABZfWZEwAAAQABAAEAAQABAgA7MAAAAQAACwACAEcTBQD4EwUA8wEBbTswAAABAAANAAQA2QwAABIRAABxEgUAuRMFAPMBAWXynhEAAAEAM4CEsBAQNAABoCAAAAwuFEAE") \ No newline at end of file diff --git a/pr/323/docs/search.index/6a9ce3d21505.js b/pr/323/docs/search.index/6a9ce3d21505.js new file mode 100644 index 000000000..0ec28af8a --- /dev/null +++ b/pr/323/docs/search.index/6a9ce3d21505.js @@ -0,0 +1 @@ +rn_("AQIBsEATMAABZfIsBwAAAQABBADzHRAAAAEAAQBDAAOwQBKeAAGxABN0AAFlZnA7MAAAAQAAIwAGAO0BAAA7BwwASQcMAFoPBQC0EAAA2hIBAGOAA7AwDCwAAWFlaQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6adf1d99a4d5.js b/pr/323/docs/search.index/6adf1d99a4d5.js deleted file mode 100644 index 3889cc1cf..000000000 --- a/pr/323/docs/search.index/6adf1d99a4d5.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQoBoIAAABQZZgEOEwAAAQgA9a8TAAABAAEAAQABAPMDAWb1QBAAAAEAAQABAAEA84MCYXMBAgKgIAAAEXugMAAAEjNiaAF9DwAAI4ACoIAAABLHYXDzAAJobQEuBQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/6b465541ac5c.js b/pr/323/docs/search.index/6b465541ac5c.js deleted file mode 100644 index bbca78125..000000000 --- a/pr/323/docs/search.index/6b465541ac5c.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUBsEASTAABZfJtEgAAAQABAAA7MAAAAQAAEQAEANMBAQBeEAgAWxEDAEkSAgABCgGwQBPPAAFl9ZcTAAABAAEAAQABAEcGAACHsCAQtgIVsMAUEwABoCAAAAdjsDAHugABGIkKOjAAAAEAAAAAAA4AEAAAAKoHqwe2B7cHjgkyDDMMOQw6DIUMhgzPDNAMmBKZEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6b51a243b1b9.js b/pr/323/docs/search.index/6b51a243b1b9.js deleted file mode 100644 index 5c0752e2d..000000000 --- a/pr/323/docs/search.index/6b51a243b1b9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQgA9G0TAAABAH4ABQATAAOwkBPMAAGwUBK8AU5nbHA7MAAAAQAAEgAJABUQAgCLEQEAqxIBAMwSAQD9EgEAXhMBAJITAQChEwEA3hMBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6b5ec36867cc.js b/pr/323/docs/search.index/6b5ec36867cc.js deleted file mode 100644 index 747cd0766..000000000 --- a/pr/323/docs/search.index/6b5ec36867cc.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBgBL1ABDsczswAAABAAAFAAEA2hIFACOCAqCQAAATy2lzBwEAhMWwUBKkAAGgoAAAEzywcBMEAAGg0AAAE9eAARgI") \ No newline at end of file diff --git a/pr/323/docs/search.index/6b85aaae5278.js b/pr/323/docs/search.index/6b85aaae5278.js new file mode 100644 index 000000000..defe73e28 --- /dev/null +++ b/pr/323/docs/search.index/6b85aaae5278.js @@ -0,0 +1 @@ +rn_("AQQA9g8NAAABAOkCAQAkAQEAAQoA814SAAABAJoAAQEA93MJAAABALUGAQBrAAEADwEjgAKwYBNiAAFpbwECAPTRDAAAAQDIBQEAAQUA9HgTAAABAEYAAQD3CAAAiLCQEzIAAaAgAAAM07CQEZgAAQTcAzowAAABAAAAAAAIABAAAAALBmMJZAl1DHYMpRCmEI0SjhI=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6bdb48f6e8fe.js b/pr/323/docs/search.index/6bdb48f6e8fe.js deleted file mode 100644 index 335093a74..000000000 --- a/pr/323/docs/search.index/6bdb48f6e8fe.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMBsDAS2AABY/MpEQAAAQABAAEDAYAS9QAQ7HM7MAAAAQAABQABANoSBQAjggKgkAAAE8tpcwcBAIDFsFASpAABoKAAABM8sHATBAABoNAAABPXgAEYCPOAAmRy") \ No newline at end of file diff --git a/pr/323/docs/search.index/6bf8cc22533d.js b/pr/323/docs/search.index/6bf8cc22533d.js new file mode 100644 index 000000000..025cadb9d --- /dev/null +++ b/pr/323/docs/search.index/6bf8cc22533d.js @@ -0,0 +1 @@ +rn_("AYMCsQAUIQABsNAUEwABa20BCgDzdBMAAAEAbQATgAKiEAAAFCxsbzMChKBgAAASIqEAAAAT5ASAAzswAAABAAA0AAIAsgcAAAIIMwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c103fa720e9.js b/pr/323/docs/search.index/6c103fa720e9.js new file mode 100644 index 000000000..fe29e97ea --- /dev/null +++ b/pr/323/docs/search.index/6c103fa720e9.js @@ -0,0 +1 @@ +rn_("AYQCsAARIwABsAARJQABNDbzAgFh8oMMAAABAAEAAPZQEgAAZgABAEcA8wABAPMAAWTzXhIAAAEAmgBjgAOgIAAACU1hZW8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c11604302a6.js b/pr/323/docs/search.index/6c11604302a6.js deleted file mode 100644 index a3c889536..000000000 --- a/pr/323/docs/search.index/6c11604302a6.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA9VgIAAABAAEAAQABAAEAAqAwAAAQvqCQAAATBzBo9Z4RAABzAQEAAQABABOAAqAwAAAPf2RyIwKEsBAJSgL/sCAMHAD2oDAAAA+jEgAY9REGAAAvAAEAAQABADOAhLCgElgAAbBAD4kAARBJAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c2ea040f627.js b/pr/323/docs/search.index/6c2ea040f627.js deleted file mode 100644 index 122ffd6cd..000000000 --- a/pr/323/docs/search.index/6c2ea040f627.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMA80kSAAABAAEAI4ECsSAUHQABY2sBBAGgQAAAEZVl8wgNAACzAwEAAQAA9ZUQAAABAAEAAQBpAnOAhKAwAAATpwgwAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c6bec7a76cc.js b/pr/323/docs/search.index/6c6bec7a76cc.js deleted file mode 100644 index 844c139e2..000000000 --- a/pr/323/docs/search.index/6c6bec7a76cc.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9CMSAAABAFQBAQDzAAFsOzAAAAEAAAgAAwC3CQEAhRABAOEQBAABAAGwYBN/AAFl9GIMAAABAAEAUAcBBQD0tBIAAAEAOgEBAAEHADswAAABAAAFAAEACBMFAPMCAXQ7MAAAAQAACAACAB0MAABKDAcA84ACYWUBgAKxEBQAAAGwEAxlAAFlbwEFADswAAABAAANAAEAQBENAAECAPO0DwAAAQABAAECAPSwDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBeEAgAhwcAAIewoBM5AAGw0BPFAAGgUAAAE5SMFBH1uAcAANMIAQABAAEA84ACbHQTgAOgIAAADNWwoBOtAAFhZG4BgAKwgBKNAAGwoBL5AAFpdAGAArDgE+oAAbBAD/8AAWxyAQYA9IsQAAABAAEAAQBHBQAAh6DQAAATx7BQD/0AAaAAAAAS+7DwE6UAAQWRA/KjDwAApwABAgD0zhIAAAEA/QABACOAAqAgAAAMKWlyAQgA9DcSAAABAAEAAQABBAGwABCPAAFzAQEQAAABAgGgUAAAEo9jAdYMAAC3AgAAh7BADRAAAbAQEuwAAaAAAAASyJUQAzowAAABAAAAAAANABAAAABOBU8FDgYPBhAG2QzaDHIPcw90D4EPORM6EzwTAYACsCAJfAABoOAAABNvYmz3FgAAibAQBggAAbAgB9kAAZEJRzswAAABAACKAAoASwQAAHYJAgCZEAAA6BAAACcRAACkEXsAIRIDADESAADyEgAAeBMBAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c99d3e874b0.js b/pr/323/docs/search.index/6c99d3e874b0.js new file mode 100644 index 000000000..274ee97c9 --- /dev/null +++ b/pr/323/docs/search.index/6c99d3e874b0.js @@ -0,0 +1 @@ +rn_("AYkCsCAT3AABoDAAABPlaXATAAOgIAAAEqyggAAAEr9laXX1KwwAADIAAQABAAEAAYYCsJAUAAABoAAAABJGYmQBgAKwMBKgAAGgMAAAEJxydtcAAACFoDAAABAaoDAAABL0EAETOjAAAAEAAAAAAAkAEAAAAE8JUAlRCYEJggkxEDYQ7RDuEIMS") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c9fa969dbc1.js b/pr/323/docs/search.index/6c9fa969dbc1.js new file mode 100644 index 000000000..d49f1aa71 --- /dev/null +++ b/pr/323/docs/search.index/6c9fa969dbc1.js @@ -0,0 +1 @@ +rn_("AYYCoAAAABLUoAAAABLVZHIBgAKgcAAAE12gMAAAELxmdAELAaBAAAATqWXy0xMAAAEAI4ECsFAQkwABY3YBAAGwgBOrAAFw8qcTAAABAEMGA6AgAAARBaBAAAASQmRlc/OpDwAAHAMBAPMABGlyc3Y7MAAAAQAADwAFAF4HBQBkEgUAjxIBAPoSAADeEwAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/6c9fb3b20c08.js b/pr/323/docs/search.index/6c9fb3b20c08.js new file mode 100644 index 000000000..20bca1576 --- /dev/null +++ b/pr/323/docs/search.index/6c9fb3b20c08.js @@ -0,0 +1 @@ +rn_("AQ8A9JQTAAABAEsAAQABBAD0LRAAAAEAsQABAAECADowAAABAAAAAAAJABAAAAB+EX8RgRKCEl4TXxOvE7ATHxQgFPMAAW/ywAkAAAEAU4ADsQAT8QABaG9w84ACc3Q=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6ca02f986331.js b/pr/323/docs/search.index/6ca02f986331.js new file mode 100644 index 000000000..8f0d3e675 --- /dev/null +++ b/pr/323/docs/search.index/6ca02f986331.js @@ -0,0 +1 @@ +rn_("AYECoCAAABP/sEAPrQABYnABBQD0xxIAAAEAjgABAPMDAWMBPRIAAAEMAPQEFAAAAQABAAEA84ACbnYBgAKwMAxpAAGgMAAADGtpbwECAaAAAAATzXMBlhEAACOAAqBAAAATfmFl5wEAgIWwAAXsAAGQSAE=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6ca19f0a3313.js b/pr/323/docs/search.index/6ca19f0a3313.js deleted file mode 100644 index 6d3811bb7..000000000 --- a/pr/323/docs/search.index/6ca19f0a3313.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBsEAT5AABdDswAAABAAANAAYAghIDAOUSAQBMEwEAYxMBANoTAQD+EwEAE4ACsDASrwE0aXABBQDzJBMAAAEAAQATgAKgMAAAE3pleVMCA7BwE4gAAWJlcwHtDwAAEwACoCAAAAYYZm32cgAAAFUHAQDCAQEAWwc=") \ No newline at end of file diff --git a/pr/323/docs/search.index/6ccad3bd4a7d.js b/pr/323/docs/search.index/6ccad3bd4a7d.js deleted file mode 100644 index b052fe0e4..000000000 --- a/pr/323/docs/search.index/6ccad3bd4a7d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQUA9FMSAAABAJoAAQABCAD0/xIAAAEAgwABADMAA7BAEtQAAWFiZTowAAABAAAAAAAJABAAAACWB5cH9wz4DHoP5g8wEDEQLhLxEg==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6da9782962b9.js b/pr/323/docs/search.index/6da9782962b9.js deleted file mode 100644 index 452e7cb91..000000000 --- a/pr/323/docs/search.index/6da9782962b9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAAOzAAAAEAAAYAAgACEwAAZxMFACOIArBAE9UAAWVzAQQBsDAS2AABY/MpEQAAAQABAAEFADswAAABAAAIAAMAmBECAIYSBACWEwAA8wMDY2Vw89IMAAABAAEAAQABsGATfwABZQG0EwAA8wIBc/Z2CQAAAQABAHAHPwAKAQECAaBQAAARKHIByBAAAPOAA2ZtcA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6dc00ec2b861.js b/pr/323/docs/search.index/6dc00ec2b861.js new file mode 100644 index 000000000..511590664 --- /dev/null +++ b/pr/323/docs/search.index/6dc00ec2b861.js @@ -0,0 +1 @@ +rn_("AQABsGATgQABZQG2EwAAAQYA9LYSAAABADoBAQABBAD00BIAAAEA/QABAPMAA2VyczowAAABAAAAAAAJABAAAAB3CXgJeQnqECkRJRImEjMSehN7Ew==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6e4b7a20ef6a.js b/pr/323/docs/search.index/6e4b7a20ef6a.js deleted file mode 100644 index f9ef47ef7..000000000 --- a/pr/323/docs/search.index/6e4b7a20ef6a.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMAOzAAAAEAAAcAAgCMDAUA+xABAAEAAbBQE6sAAXIBSwUAAFMBhLBAEkcAAbBAE+QAAZgABDowAAABAAAAAAAoABAAAAA8BD0EPgQNBl4JXwmFCbMJ2wx+D+sP7A/5DyYQfRB+EH8QyRAqEoISgxKEEoUSqhK4ErkS5RLmEkwTTRNRE1ITUxNjE2QTwxPEE9oT2xP+E/8T") \ No newline at end of file diff --git a/pr/323/docs/search.index/6ee2f712ff40.js b/pr/323/docs/search.index/6ee2f712ff40.js deleted file mode 100644 index 922539d97..000000000 --- a/pr/323/docs/search.index/6ee2f712ff40.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQwA8zYTAAABAAEAAQYA8xURAAABAAEAAYICsGASwwABsNAT0QABaXITgAKwEAzsAAFlaRcDAIOIsTAT7QABoGAAABEUoFAAABClsJASmgABsEAQowABtBAH8wABYfSMCQAAAQBcBwEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/6fcb853616ae.js b/pr/323/docs/search.index/6fcb853616ae.js deleted file mode 100644 index dfb329961..000000000 --- a/pr/323/docs/search.index/6fcb853616ae.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYACsHAT/gABoAAAAAUuZmcBAQA7MAAAAQAABwABAFQMBwABAQGgcAAAEithAXgHAAAjgAKhAAAAFAZkbnOAhKBgAAASKgBBSA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/6ff8f30623c6.js b/pr/323/docs/search.index/6ff8f30623c6.js new file mode 100644 index 000000000..00ca08203 --- /dev/null +++ b/pr/323/docs/search.index/6ff8f30623c6.js @@ -0,0 +1 @@ +rn_("AQcA9KERAAABAAEAAQAjAQKgcAAAEaBhcgHcBwAAAQMAOzAAAAEAAAcAAgBaDwUA2hIBAPMAAmN0OzAAAAEAAFcAJgAxBQsA5gUBAGEJAQA9DAAAQwwCAPUMAAAADQIAEg0BAIEPAACGDwEAiQ8BAI8PAAAUEAIAGxABACAQAQA/EAAAsxAAALkQAAAGEQEAExEBACcRAQArEQIAhhEBAJcRAgCdEQAAQhIAAEkSAQBOEgEAkhIBAJ4SAwDAEgEA0BIDANYSAQAwEwEAQRMAAKkTAADOEwQA1xMCAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/704f43eab3eb.js b/pr/323/docs/search.index/704f43eab3eb.js new file mode 100644 index 000000000..85e6b72eb --- /dev/null +++ b/pr/323/docs/search.index/704f43eab3eb.js @@ -0,0 +1 @@ +rn_("AQEA98YQAAABAAEApwIBAH4ABQABAgD0WRAAAAEAAQABABOAAqDQAAAT3mNz84ACZW8jgQKwAAe1AAFsdAEAAPQoBgAAAQABAAEAU4ADoFAAABQsZG5y") \ No newline at end of file diff --git a/pr/323/docs/search.index/7092b04deb4e.js b/pr/323/docs/search.index/7092b04deb4e.js new file mode 100644 index 000000000..5d8215c6e --- /dev/null +++ b/pr/323/docs/search.index/7092b04deb4e.js @@ -0,0 +1 @@ +rn_("AYACoJAAABL+oBAAAAYjaW8BCQDz6BMAAAEANQABDAGgoAAAFCx38hwUAAABAAEBAPOqDwAAAQABAPOAAnR1AYECsIAT9gABoGAAABQaZHM3AgCDhqBAAAATzLBgE6AAS7AwE40AAQKQDgESAPQEFAAAAQABAAEAAQDGoFAAABJqoAAAABBBsCAQ8QABoFAAABGWoQAAABP/oEAAABOqAAMCCzswAAABAAAVAAcAJAYAAEYMAADtDAAAuQ8NABURAAB/EgEApRMBAAEBAbCwE/EAAWw7MAAAAQAADAADAIsHCAALDQEA9w8BAAEBAaAwAAAQzGjzVQkAAC4A/QcBAgD0dxEAAAEAAQABABOBArAgEXsAAWFz84ICbm/3AwCAh6BAAAAMLxUBBw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/711db6f37c54.js b/pr/323/docs/search.index/711db6f37c54.js deleted file mode 100644 index d64951e5e..000000000 --- a/pr/323/docs/search.index/711db6f37c54.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcA9KYQAAABAAEAAQABAAD1rAkAAAEAAQABAAEAI4ACoKAAABP8ZnABAwGgkAAAE3xl8mUPAAABANcAAACFoFAAABAjsUAUIwABgSkA8u0FAAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/713ea441eaa3.js b/pr/323/docs/search.index/713ea441eaa3.js deleted file mode 100644 index 3b5343dc9..000000000 --- a/pr/323/docs/search.index/713ea441eaa3.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQIBoKAAABLpZPJbBwAAAQABgQKgMAAAE9ygUAAAE29jcwEEA7AgEO8AAaBQAAARlKBAAAATqGxzdvKjEwAAAQDzAANhZW/yOgQAAAEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/72352df403a5.js b/pr/323/docs/search.index/72352df403a5.js deleted file mode 100644 index 07a2b6946..000000000 --- a/pr/323/docs/search.index/72352df403a5.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQMA9GIJAAABABEDAQABAwDzcQwAAAEAAQDzgAJpbwEBAbBQEK8AAWP0vwcAAAEAAQDzASOBAqBQAAAQcWNuAQQAOzAAAAEAAAcAAwBoEQIA8xIBAIUTAgABAQA6MAAAAQAAAAAABwAQAAAAixGMEasSrBL9Ev4SoROiE/MJAWkBrhAAANcCAACHsMAS7AABoFAAAA9poRAAABPHFAEXOzAAAAEAACMAAQCPASMA") \ No newline at end of file diff --git a/pr/323/docs/search.index/729cae89d66a.js b/pr/323/docs/search.index/729cae89d66a.js new file mode 100644 index 000000000..72d610655 --- /dev/null +++ b/pr/323/docs/search.index/729cae89d66a.js @@ -0,0 +1 @@ +rn_("AQQBsAASqAABczswAAABAAALAAIAHBMHAAQUAwBHAAABhqCAAAAS/bEAFBEAAbBAEmsAAbCAFB8AAbBQEqoAARUAFvRyDgAAAQABAAEAAQUA868PAAABAAEAY4CEsEAQNAABoAAAAAdIAPAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/731dde1dce82.js b/pr/323/docs/search.index/731dde1dce82.js deleted file mode 100644 index 0ef573de7..000000000 --- a/pr/323/docs/search.index/731dde1dce82.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQAAOjAAAAEAAAAAAAoAEAAAAJUPlg95EHoQBhGSEZMRlBKVEg8UEBQBBADzkA8AANcAAQDzAAFx9w0NAAABAOkCAQDCAGIAAQDzAAJleTowAAABAAAAAAAMABAAAABlDGYMMhAzEMQQxRDGEG0TbhN9E34T7BPxEw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/734aaab2a00b.js b/pr/323/docs/search.index/734aaab2a00b.js deleted file mode 100644 index ddfc7fa59..000000000 --- a/pr/323/docs/search.index/734aaab2a00b.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYA9DITAAABAAEAAQABDwD0CxQAAAEAAQABAPOHAmhtAQQBsAASkgABZPQ8EgAAAQABAAEAI4eEsKATvwABsEASkAABsHATcAABFYAAAQABoEAAABA9ZTswAAABAAAGAAIAzgcEAF0IAQATgAKwUA/6AAFkcgEJAPVREwAAAQABAHAAAQC3AgCAhrAgEkUAAWtew2hnzBFJAQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/7393667e232e.js b/pr/323/docs/search.index/7393667e232e.js new file mode 100644 index 000000000..7975f9a43 --- /dev/null +++ b/pr/323/docs/search.index/7393667e232e.js @@ -0,0 +1 @@ +rn_("AQABoJAAABLyaPK8EgAAAQDzAgFk8noHAAABAAECADswAAABAAAFAAEA+BMFAPMLAW07MAAAAQAABQABALkTBQAjgAKwIAzxAAFhbwECAPNSCQAADwMBAAGKArAAE1kAAbAAE1sAATQ2I4ECoDAAAAyFZXIBgAKwQBGUAAFr+MwiUz1jdbcGAACHsBAQAQABsFAT1QABEVEJ9SYFAAAKALQAAQAsBw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/7489318a49dc.js b/pr/323/docs/search.index/7489318a49dc.js deleted file mode 100644 index c37aa8519..000000000 --- a/pr/323/docs/search.index/7489318a49dc.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsPAUEwABa/WKEwAABQABAAEAJAATAAKxABQAAAFkbvJgEgAAAQATgAKwEAxlAAFlbwEFADswAAABAAANAAEAQBENAAECAPO0DwAAAQABAAECAPSwDwAAAQABAAEAAQMAOzAAAAEAAAgAAQBeEAgAAYACoEAAABCysMATxQABZXMBAQA7MAAAAQAAEwABANEPEwAjgAKwkBM5AAFpbTc2AACKoBAAAAw7oFAAABOUsHATeAABoCAAAAz0nFUR9bgHAADTCAEAAQABAPOAAmx0") \ No newline at end of file diff --git a/pr/323/docs/search.index/7517f383313f.js b/pr/323/docs/search.index/7517f383313f.js new file mode 100644 index 000000000..9a37a5d37 --- /dev/null +++ b/pr/323/docs/search.index/7517f383313f.js @@ -0,0 +1 @@ +rn_("AQkBoIAAABQbZgEQEwAAAQIA9X8QAAABAAEAOQIBAPMAAml1OjAAAAEAAAAAAAcAEAAAAF0QeBJRE1ITgROCE54TnxMBCAD1sRMAAAEAAQABAAEA8wIBZvVCEAAAAQABAAEAAQATgAKwoBP0AAFydPMCAmFz828QAAABAAEAAQQBsEAT0QABZfWZEwAAAQABAAEAAQABAgA7MAAAAQAACwACAEcTBQD4EwUA8wMBbTswAAABAAANAAQA2QwAABIRAABxEgUAuRMFAPOBAmNl84ACYWU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/75b83b34138f.js b/pr/323/docs/search.index/75b83b34138f.js deleted file mode 100644 index 76c46754b..000000000 --- a/pr/323/docs/search.index/75b83b34138f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEA9RYGAAA9DAEAmgABAAEAADswAAABAAAHAAMA5gEBAHkMAQDKDwMAAYMCsHATDwABsIATQwABY3MBBQD0thIAAAEAigABAPMIAWP1AhAAAAEAvAABAAEAAQABoDAAABEsdPRpEAAAAQAXAAEAAYACoAAAABA7oAAAABA8NDbzAwF2AQUNAACXAAAAhbCgEpwAAbAgDDkAAbCAEn8AAQkEBjowAAABAAAAAAAJABAAAABuB28HcAdHDKYPAhE0EjUSNhLHEwECAPSiEgAAAQCJAAEAAQMA9NYSAAABAI4AAQDzAAJidTowAAABAAAAAAAVABAAAACsB60HrgeLD4wPLRAuEEwQTRDfEOAQjhGPEaASoRKtEq4SvRLnEugSKhMrEwEBAPQSBgAAAQDiBgEAtwAAgIWxABOtAAGwAAe2AAEMCEQBCAA7MAAAAQAABQABAGISBQDXDQAAiLBAEM8AAaAgAAAND7kACzswAAABAAAPAAUAagABAGYHBwDFBwEAIRABAMsQAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7635a73c43aa.js b/pr/323/docs/search.index/7635a73c43aa.js new file mode 100644 index 000000000..55159ff5b --- /dev/null +++ b/pr/323/docs/search.index/7635a73c43aa.js @@ -0,0 +1 @@ +rn_("AYQCoCAAABDLoDAAABEzaXQBAQA7MAAAAQAAfAACANUHAADCCXsA8wACZWz1QAUAAJkCEAkoAgEAAQIAOzAAAAEAAN4AAgAmDAAAFw3dAMcAAACFsLASwAABoBAAABM9sDANFQABFDACOzAAAAEAAAoBCwC2AQYAGAYAAEUG3QCjBwcA8g8CAEIRDQBqEQIAMRIBAKoSAQD1EgEAhxMCAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/7694027599d3.js b/pr/323/docs/search.index/7694027599d3.js new file mode 100644 index 000000000..48096bdcd --- /dev/null +++ b/pr/323/docs/search.index/7694027599d3.js @@ -0,0 +1 @@ +rn_("AQMA8xQQAAABAAEAAYACoLAAABOWsDAS7gABbnRDAAOgIAAAEL+wUBGSAAFlaGk7MAAAAQAABgADACkMAAChEQMARRMBAPOAAmRy") \ No newline at end of file diff --git a/pr/323/docs/search.index/7707a0af636d.js b/pr/323/docs/search.index/7707a0af636d.js deleted file mode 100644 index cd724e228..000000000 --- a/pr/323/docs/search.index/7707a0af636d.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYQCsAARIQABsAARIwABNDbzAgFh8oIMAAABAAEAAPZOEgAAZgABAEcA8wABAPMAAWTzXBIAAAEAmgBjgAOgIAAACUxhZW8=") \ No newline at end of file diff --git a/pr/323/docs/search.index/77f373856497.js b/pr/323/docs/search.index/77f373856497.js deleted file mode 100644 index 4d845a12c..000000000 --- a/pr/323/docs/search.index/77f373856497.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQEBoEAAABEsc/VHDAAAuwQyAQEAAQATggKgIAAADQ9lcgECAPScDwAAAQDqAAEAI4ECoBAAAAxGZWkTgAKg8AAAE5VucjMAA7AwCXkAAWVvdAF7EQAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/78318d6c832f.js b/pr/323/docs/search.index/78318d6c832f.js deleted file mode 100644 index 88a5c03f5..000000000 --- a/pr/323/docs/search.index/78318d6c832f.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYICsEATzAABsGAStAABZXIBBQD0RRAAAAEAAQABAPOAAmZtAQYAOjAAAAEAAAAAAAgAEAAAAI4Pjw82EXcSeBJiE+ETERQSFAEAAaFAAAAUCmf03AcAAAEAAQABAMMAhKCwAAATS7BAEFwAARQgBPVYBwAAfQUnA0cC/QBjgAOwMA/IAAFhZWk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7861667f4a43.js b/pr/323/docs/search.index/7861667f4a43.js deleted file mode 100644 index cd420f997..000000000 --- a/pr/323/docs/search.index/7861667f4a43.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AYUCsHATiAABsDAS5QABZXMTAAOwQA+HAAGgYAAAETBiZXM7MAAAAQAAEgAEACAGAQAvBwoA/Q8BAE8QAwABAgDzKwYAAIAJAQABAwD1fw8AAJoAAQAEAAEAAQoBoEAAABPOZfLtEwAAAQAjAAOgMAAAEL6gkAAAEwcwY2j1nhEAAHMBAQABAAEA84ACZHLnAAAAh7AQCUoC/7AgDBwA9qAwAAAPo7AAB50AARJBODswAAABAAAQAAYAfAACABEGAABABgMA/QcDAD4IAwAnDAAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/78f080218498.js b/pr/323/docs/search.index/78f080218498.js deleted file mode 100644 index 91527b83a..000000000 --- a/pr/323/docs/search.index/78f080218498.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABsIATXAABdAFmCQAAAQkA81USAAABAAEA84ACY2cBgwKgkAAAE6OgkAAAE6QyMwGAAqBQAAATy6CAAAAS+GNl8wACc3TyoAcAAAEAAQABoLAAABM/YvPTEgAAFwEBACOAAqAAAAAS0mRyI4ACoFAAABOVY2XzgAJpeQEGAPXhEAAAAQABAAEAAQABAQGwQBPPAAFl9ZcTAAABAAEAAQABAPcAAICFoBAAAAwpKWAA") \ No newline at end of file diff --git a/pr/323/docs/search.index/79a2ddf51533.js b/pr/323/docs/search.index/79a2ddf51533.js new file mode 100644 index 000000000..0f6e73692 --- /dev/null +++ b/pr/323/docs/search.index/79a2ddf51533.js @@ -0,0 +1 @@ +rn_("AYMCsQAUIQABsNAUEwABa20BBAGgoAAAFCx38hwUAAABAAELAPOREwAAAQABACOAAqCAAAATt2VvYwQDoGAAABNQZW5wATkMAAABCgDzdBMAAAEAbQATgAKiEAAAFCxsb3cAAACFoGAAABIioQAAABPkBIEDOzAAAAEAADsABAAwBgUAsgcAAAIIMwAqEQAAQ4ADsIARKQEKoAAAAAHsZGt0") \ No newline at end of file diff --git a/pr/323/docs/search.index/7b387f9a1467.js b/pr/323/docs/search.index/7b387f9a1467.js deleted file mode 100644 index 3e74e65f7..000000000 --- a/pr/323/docs/search.index/7b387f9a1467.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQACoCAAABMGsDARiAABaXTyexIAAAEAAQMAOzAAAAEAAAYAAQAHEQYAAQgA9G0TAAABAH4ABQATAQOwkBPMAAGwUBK8AU5nbHA7MAAAAQAAEgAJABUQAgCLEQEAqxIBAMwSAQD9EgEAXhMBAJITAQChEwEA3hMBAAGAArAwEYQAAbAwEYYAAXJ2AQQBsAASQQABZDswAAABAAAFAAIAgBEDAGsSAQC3BQABh7BQEkMA/bBgEroAAZQJAzswAAABAAAKAAUA4gwCAHUOAQCuEAAAMhEDALISAAAjgAKgEAAADOFubwEDAPMSEAAAAQABACOAAqAAAAAMKGRyYwCEoFAAABEvoDAAABBKFAEQ8vsHAAABAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/7b519bf60e51.js b/pr/323/docs/search.index/7b519bf60e51.js new file mode 100644 index 000000000..411b3b003 --- /dev/null +++ b/pr/323/docs/search.index/7b519bf60e51.js @@ -0,0 +1 @@ +rn_("AQUAOzAAAAEAAOIAAgDpDAEAeQ7gAAEBAaAAAAASynP1TwUAADQKuAMBAAIAAYACoCAAAA94oDAAABJ3ZXITAQKgIAAAEVFwdQFkDwAAAQECoGAAABNBoOAAABOYZXQ7MAAAAQAACAABAFMFCAABgAOwIBIjAAGwsBJaAAGwsBJcAAFiY3PzgMUAAQoK") \ No newline at end of file diff --git a/pr/323/docs/search.index/7dea95897c5e.js b/pr/323/docs/search.index/7dea95897c5e.js new file mode 100644 index 000000000..60c3df5c2 --- /dev/null +++ b/pr/323/docs/search.index/7dea95897c5e.js @@ -0,0 +1 @@ +rn_("AQABoDAAAAzXaQH0EgAAAQABsDAQ7wABbvK7BwAAAQABAQDzNggAAC8HAQATAAKgUAAAEFBlc/UMBAAAAQABAAEAAQABhAKwoBONAAGwoBOPAAFjcvMBAXTyjBEAANUAAQMBsGAT1QABZjswAAABAAAGAAMAIQwCAAETAQCFEwEA84ACYWXzgIQAKAU=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7e247cb6de94.js b/pr/323/docs/search.index/7e247cb6de94.js deleted file mode 100644 index 13eef2841..000000000 --- a/pr/323/docs/search.index/7e247cb6de94.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQcA9VETAAABAAEAcAABAAEHAPNtEAAAAQABAAGAAqAAAAAHvKAQAAAJXGR48wEBZfLIDwAAAQDnAAAAhbBgDO8AAbBADG0AATIIAfRQBQAAYgIdCQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/7e614ec002b9.js b/pr/323/docs/search.index/7e614ec002b9.js deleted file mode 100644 index 5418e0fb4..000000000 --- a/pr/323/docs/search.index/7e614ec002b9.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQQA9uYTAAABAA0AAQAjAAQAAQMA80kSAAABAAEAI4ECsSAUHQABY2sTgAKgMAAAE6duclMAA6BAAAAMNGVvcvTKEAAAaQGeAQEA") \ No newline at end of file diff --git a/pr/323/docs/search.index/7e738badf46e.js b/pr/323/docs/search.index/7e738badf46e.js new file mode 100644 index 000000000..e13369099 --- /dev/null +++ b/pr/323/docs/search.index/7e738badf46e.js @@ -0,0 +1 @@ +rn_("AQIA9VIRAAABAAEAAQABAAGAArBgEpoAAaAgAAAQpG95AQABoMAAABOqafTrEgAABwAXABsAAQYA9A0UAAABAAEAAQABAgD0ohAAAAEAnAABAAECAaBAAAASw3I7MAAAAQAABwACAKAQAQA5EQUA84ACYW9nAwAAhrCAEsIAVbCgEzIAAQwwBjswAAABAAAIAAMAMgwAAGURAwA5EgMA84ACZWk=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7ebc0d37a846.js b/pr/323/docs/search.index/7ebc0d37a846.js deleted file mode 100644 index 60c99898d..000000000 --- a/pr/323/docs/search.index/7ebc0d37a846.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQYAOzAAAAEAAAkAAQDxEAkAI4ADsGAS+QABsDAPewABaXN0AQgA9a8TAAABAAEAAQABAPMBAWb1QBAAAAEAAQABAAEAY4ADoDAAABLBZWd0") \ No newline at end of file diff --git a/pr/323/docs/search.index/7edd6678fc26.js b/pr/323/docs/search.index/7edd6678fc26.js new file mode 100644 index 000000000..70b1b74db --- /dev/null +++ b/pr/323/docs/search.index/7edd6678fc26.js @@ -0,0 +1 @@ +rn_("AQIA9K4QAAABAHsBAQABAAD0og8AAAEA6AAEASOCArBQEtIAAWVzAQIA9KQSAAABAIkAAQABAwD02BIAAAEAjgABAPOFAmJ1AQIA9BoRAAABAGABAQDzgAJpbPMAA2J0dTowAAABAAAAAAAPABAAAADUB2AIYQhiCGMIyQ+CECUToBPoE+kT6xP2E/cTGhQeFAEMAPOHEwAAAQABAAGAArAACW4AAaAgAAANCGRuAQYA9KISAAABAIkAAQDzAwFi9KwQAAABAHsBAQCXBgAAh7BAD6AAAaBwAAASNLCQEtAAATgJAzswAAABAAAiABEAOQYDAJkHAQD9DAIAAw0CAJkPAQDrDwEA8A8BAA0QBgBQEAAAfRABAAUTAAApEwAATRMAAN8TAADqEwAACBQAABcUAAA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7eff5365274d.js b/pr/323/docs/search.index/7eff5365274d.js new file mode 100644 index 000000000..dbb51f732 --- /dev/null +++ b/pr/323/docs/search.index/7eff5365274d.js @@ -0,0 +1 @@ +rn_("AQQAOjAAAAEAAAAAAAcAEAAAAI0Pjg9OEE8QohKjEiwTLRMBAAD0UwkAAAEA/AncABcAAACHoEAAABC5sGAT1QABoBAAAAyTsFATdgABsDARHAABsSAT9AABOMECOjAAAAEAAAAAAA4AEAAAAGgJaQl9CX4JIQwiDCMMLAwtDOIMLhIBEwIThROGEzOAA6CAAAATfWVveQ==") \ No newline at end of file diff --git a/pr/323/docs/search.index/7f51efe5046e.js b/pr/323/docs/search.index/7f51efe5046e.js deleted file mode 100644 index 942753c2d..000000000 --- a/pr/323/docs/search.index/7f51efe5046e.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQ0A9VETAAABAAEAcAABAAECAPSsEAAAAQB7AQEAAQMA9BgRAAABAGABAQDzAAJidQHHDwAAAQMA9KoQAAABAHsBAQDzhAJkcwECAPNeCQAAAQBUAPOAAml0ARgA8ycUAAABAAEAAYACsLATTwABoMAAABL8ZXXzgQJjbQEFAaBgAAASTnL0NgwAAAEAjgMBAAEJAPZ3EgAAAQDqAH8AMAABABOAArBgEGsAAWRuAQMBoHAAABKqZ/S9BwAAAQDzAQEAAQkA8+YTAAABADUAAYECsIAT9AABoGAAABQYZHMXAQCJhbDQFBoAAaBAAAATyrBgE54ASwKQCmdeAACLsHAQzwABsLASuAABoKAAABJ2sPATfwABbYEP9HcAAAABAAEAAQA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/7fc1930a76ba.js b/pr/323/docs/search.index/7fc1930a76ba.js deleted file mode 100644 index 8d3d7ac56..000000000 --- a/pr/323/docs/search.index/7fc1930a76ba.js +++ /dev/null @@ -1 +0,0 @@ -rn_("AQABoJAAABLwaPK6EgAAAQDzAgFk8nkHAAABAAECADswAAABAAAFAAEA9hMFAPMLAW07MAAAAQAABQABALcTBQAjgAKwIAzvAAFhbwECAPNRCQAADwMBAAGKArAAE1cAAbAAE1kAATQ2I4ECoDAAAAyEZXIBgAKwQBGSAAFFmRY6ajdjdbcGAACHsBAP/wABsFAT0wABEVEJ9SUFAAAKALQAAQArBw==") \ No newline at end of file diff --git a/pr/323/docs/search.index/entry/23e89d8be7e8.js b/pr/323/docs/search.index/entry/23e89d8be7e8.js deleted file mode 100644 index 7a877cc5e..000000000 --- a/pr/323/docs/search.index/entry/23e89d8be7e8.js +++ /dev/null @@ -1 +0,0 @@ -rd_("Ah[3938,15,3939,0,117,0,0]Ak[4725,15,4726,4726,118,0,0]Ai[3938,15,3939,0,4124,0,0]Al[4725,15,4726,1930,4125,0,0]Al[4431,15,4432,4432,4126,0,0]Al[4725,15,1836,1836,1589,0,0]Ao[1361,13,1362,1362,3970,3132,0]Ao[3938,13,3939,3702,4767,3132,0]Ao[3938,13,3939,4074,4391,3132,0]Ao[3938,13,3939,4074,3140,3132,0]Ao[3938,13,3939,4074,3327,3132,0]Ao[3938,13,3939,4074,4390,3132,0]Ao[3938,13,3939,4079,3345,3132,0]Ao[3938,13,3939,4079,4158,3132,0]Al[3938,13,3939,0,1599,3132,0]Al[3938,13,3939,0,4926,3132,0]Al[3938,13,3939,0,1320,3132,0]Al[3938,13,3939,0,1582,3132,0]Al[3938,13,3939,0,3137,3132,0]Al[3938,13,3939,0,2010,3132,0]Al[3938,13,3939,0,4110,3132,0]Al[3938,13,3939,0,4485,3132,0]Al[3938,13,3939,0,3983,3132,0]Al[3938,13,3939,0,3973,3132,0]Al[3938,13,3939,0,3120,3132,0]Al[3938,13,3939,0,4482,3132,0]Al[3938,13,3939,0,4670,3132,0]Al[3938,13,3939,0,4116,3132,0]Al[3938,13,3939,0,4119,3132,0]Al[3938,13,3939,0,4124,3132,0]Al[3938,13,3939,0,4825,3132,0]Ak[3938,13,3939,0,481,3132,0]Ak[3938,13,3939,0,117,3132,0]Al[3938,13,3939,0,2414,3132,0]Al[3938,13,1840,0,5072,3132,0]Al[3938,13,1919,0,1337,3132,0]Ao[4725,13,4726,1952,1600,3132,0]Ao[4725,13,4726,1952,4927,3132,0]Ao[4725,13,4726,3156,1321,3132,0]Ao[4725,13,4726,3165,4768,3132,0]Ao[4725,13,4726,1522,1583,3132,0]Ao[4725,13,4726,1522,3321,3132,0]Ao[4725,13,4726,1522,3136,3132,0]Ao[4725,13,4726,1522,4822,3132,0]Ao[4725,13,4726,1522,3296,3132,0]Ao[4725,13,4726,1538,3138,3132,0]Ao[4725,13,4726,1071,3141,3132,0]Ao[4725,13,4726,1071,4280,3132,0]Ao[4725,13,4726,1076,2011,3132,0]Ao[4725,13,4726,1076,3346,3132,0]Ao[4725,13,4726,1076,3982,3132,0]Ao[4725,13,4726,2144,4000,3132,0]Ao[4725,13,4726,2144,4392,3132,0]Ao[4725,13,4726,2144,4114,3132,0]Ao[4725,13,4726,3703,4486,3132,0]Ao[4725,13,4726,3703,3133,3132,0]Ao[4725,13,4726,3703,4358,3132,0]Ao[4725,13,4726,3283,3984,3132,0]Ao[4725,13,4726,1838,3974,3132,0]Ao[4725,13,4726,1512,3121,3132,0]Ao[4725,13,4726,1512,4373,3132,0]Ao[4725,13,4726,1512,3313,3132,0]Ao[4725,13,4726,1512,3335,3132,0]Ao[4725,13,4726,1512,4121,3132,0]Ao[4725,13,4726,1512,4484,3132,0]Ao[4725,13,4726,1512,4672,3132,0]Ao[4725,13,4726,2437,4676,3132,0]Ao[4725,13,4726,2437,4117,3132,0]Ao[4725,13,4726,1903,2398,3132,0]Ao[4725,13,4726,1903,4766,3132,0]Ao[4725,13,4726,1930,4120,3132,0]Ao[4725,13,4726,1930,4125,3132,0]Al[4725,13,4726,0,4396,3132,0]Al[4725,13,4726,0,4826,3132,0]An[4725,13,4726,4726,482,3132,0]An[4725,13,4726,4726,118,3132,0]Ao[4725,13,4726,4726,2415,3132,0]Ao[4725,13,4726,4726,3119,3132,0]Ao[4725,13,1836,1836,4286,3132,0]Ao[4725,13,4968,4968,4867,3132,0]Ao[4725,13,4968,4968,1339,3132,0]Ao[4725,13,1841,1841,5073,3132,0]Al[4725,13,1920,0,1338,3132,0]Ao[4431,13,4432,4432,4126,3132,0]Ak[3938,13,3939,1045,474,0,0]Al[3938,13,3939,4074,3325,0,0]Al[3938,13,3939,4074,4868,0,0]Al[3938,13,3939,4079,3331,0,0]Ai[4725,14,3134,0,1576,0,0]Ai[4725,14,3134,0,1566,0,0]Ai[4725,14,3134,0,3099,0,0]Ai[4725,14,3134,0,4263,0,0]Ai[4725,14,3134,0,4145,0,0]Ai[4725,14,3134,0,4138,0,0]Ai[4725,14,3999,0,2408,0,0]Ai[4725,14,3999,0,2429,0,0]Ai[4725,14,3999,0,2393,0,0]Ai[4725,14,3999,0,1999,0,0]Ae[1361,7,1362,0,0,0,0]Ah[4725,14,4497,0,470,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Ag[3938,5,3697,133,0,0,0]Ag[4725,5,3698,133,0,0,0]Al[1046,15,1047,1047,4860,0,0]Ae[3958,5,3959,0,0,0,0]Ah[3938,6,3939,4726,0,0,0]Ae[4725,6,4726,0,0,0,0]Al[4725,15,1836,1836,1589,0,0]Ai[3938,19,3939,0,1599,0,0]Ai[3938,19,3939,0,1582,0,0]Al[4725,19,4726,1952,1600,0,0]Al[4725,19,4726,1522,1583,0,0]Ae[1361,5,1362,0,0,0,0]Ae[1046,5,1830,0,0,0,0]Ae[1046,5,1925,0,0,0,0]Ah[3938,15,3939,0,117,0,0]Ak[4725,15,4726,4726,118,0,0]Ak[3938,13,3939,0,1599,128,0]Ak[3938,13,3939,0,1582,128,0]Ak[3938,13,3939,0,3983,128,0]Ak[3938,13,3939,0,4119,128,0]Aj[3938,13,3939,0,481,128,0]Aj[3938,13,3939,0,117,128,0]Ak[3938,13,3939,0,2414,128,0]An[4725,13,4726,1952,1600,128,0]An[4725,13,4726,3165,4768,128,0]An[4725,13,4726,1522,1583,128,0]An[4725,13,4726,1071,3141,128,0]An[4725,13,4726,1071,4280,128,0]An[4725,13,4726,1076,3346,128,0]An[4725,13,4726,3283,3984,128,0]An[4725,13,4726,2437,4676,128,0]An[4725,13,4726,1903,4766,128,0]An[4725,13,4726,1930,4120,128,0]Am[4725,13,4726,4726,482,128,0]Am[4725,13,4726,4726,118,128,0]An[4725,13,4726,4726,2415,128,0]Ai[3938,13,3939,0,2414,0,0]Al[4725,13,4726,4726,2415,0,0]Ai[4725,14,3999,0,1577,0,0]Ai[4725,14,3999,0,3105,0,0]Ai[3938,14,3939,0,2423,0,0]Al[4725,14,4726,4726,2424,0,0]Al[4431,14,4432,4432,2412,0,0]Al[4431,14,4432,4432,2425,0,0]An[1361,13,1362,1362,125,1327,0]Ao[1361,13,1362,1362,3970,1327,0]Ao[1361,13,1536,1536,4393,1327,0]Ao[4007,13,4008,4008,5014,1327,0]Ao[3938,13,3939,3702,3298,1327,0]Ao[3938,13,3939,3702,3300,1327,0]Ao[3938,13,3939,3702,4929,1327,0]Dd[3938,13,3939,3702,4767,1971,0,\"impl-Display-for-SendDatagramError\"]Db[3938,13,3939,3702,4767,1327,0,\"impl-Debug-for-SendDatagramError\"]Ao[3938,13,3939,2436,2391,1327,0]Ao[3938,13,3939,2436,4253,1327,0]Ao[3938,13,3939,2463,2395,1327,0]Cm[3938,13,3939,2463,3328,1971,0,\"impl-Display-for-RetryError\"]Ck[3938,13,3939,2463,3328,1327,0,\"impl-Debug-for-RetryError\"]Ao[3938,13,3939,2463,4372,1327,0]An[3938,13,3939,1045,474,1327,0]Ao[3938,13,3939,4074,3325,1327,0]Ao[3938,13,3939,4074,4868,1327,0]Da[3938,13,3939,4074,4391,1971,0,\"impl-Display-for-ReadToEndError\"]Co[3938,13,3939,4074,4391,1327,0,\"impl-Debug-for-ReadToEndError\"]Cj[3938,13,3939,4074,3140,1327,0,\"impl-Debug-for-ReadError\"]Cl[3938,13,3939,4074,3140,1971,0,\"impl-Display-for-ReadError\"]Ck[3938,13,3939,4074,3327,1327,0,\"impl-Debug-for-ResetError\"]Cm[3938,13,3939,4074,3327,1971,0,\"impl-Display-for-ResetError\"]Da[3938,13,3939,4074,4390,1971,0,\"impl-Display-for-ReadExactError\"]Co[3938,13,3939,4074,4390,1327,0,\"impl-Debug-for-ReadExactError\"]Ao[3938,13,3939,1548,4159,1327,0]Ao[3938,13,3939,1088,3996,1327,0]Ao[3938,13,3939,4079,3331,1327,0]Ck[3938,13,3939,4079,3345,1327,0,\"impl-Debug-for-WriteError\"]Cm[3938,13,3939,4079,3345,1971,0,\"impl-Display-for-WriteError\"]Cm[3938,13,3939,4079,4158,1327,0,\"impl-Debug-for-StoppedError\"]Co[3938,13,3939,4079,4158,1971,0,\"impl-Display-for-StoppedError\"]Cf[3938,13,3939,0,1599,1971,0,\"impl-Display-for-VarInt\"]Cd[3938,13,3939,0,1599,1327,0,\"impl-Debug-for-VarInt\"]Dd[3938,13,3939,0,4926,1971,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3938,13,3939,0,4926,1327,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3938,13,3939,0,1320,1327,0]Cd[3938,13,3939,0,1582,1327,0,\"impl-Debug-for-PathId\"]Cf[3938,13,3939,0,1582,1971,0,\"impl-Display-for-PathId\"]Al[3938,13,3939,0,2426,1327,0]Al[3938,13,3939,0,3306,1327,0]Al[3938,13,3939,0,3137,1327,0]Al[3938,13,3939,0,4489,1327,0]Al[3938,13,3939,0,2010,1327,0]Cj[3938,13,3939,0,4110,1327,0,\"impl-Debug-for-ClosedStream\"]Cl[3938,13,3939,0,4110,1971,0,\"impl-Display-for-ClosedStream\"]Cm[3938,13,3939,0,4485,1327,0,\"impl-Debug-for-ConnectionError\"]Co[3938,13,3939,0,4485,1971,0,\"impl-Display-for-ConnectionError\"]Al[3938,13,3939,0,4499,1327,0]Al[3938,13,3939,0,4506,1327,0]Al[3938,13,3939,0,4791,1327,0]Al[3938,13,3939,0,4806,1327,0]Al[3938,13,3939,0,3983,1327,0]Al[3938,13,3939,0,4367,1327,0]Al[3938,13,3939,0,4152,1327,0]Al[3938,13,3939,0,4983,1327,0]Al[3938,13,3939,0,4104,1327,0]Ci[3938,13,3939,0,3973,1327,0,\"impl-Debug-for-ConfigError\"]Ck[3938,13,3939,0,3973,1971,0,\"impl-Display-for-ConfigError\"]Ci[3938,13,3939,0,3120,1971,0,\"impl-Display-for-FrameType\"]Cg[3938,13,3939,0,3120,1327,0,\"impl-Debug-for-FrameType\"]Co[3938,13,3939,0,4482,1971,0,\"impl-Display-for-ConnectionClose\"]Cm[3938,13,3939,0,4482,1327,0,\"impl-Debug-for-ConnectionClose\"]Cn[3938,13,3939,0,4670,1327,0,\"impl-Debug-for-ApplicationClose\"]D`[3938,13,3939,0,4670,1971,0,\"impl-Display-for-ApplicationClose\"]Cj[3938,13,3939,0,4116,1327,0,\"impl-Debug-for-ConnectError\"]Cl[3938,13,3939,0,4116,1971,0,\"impl-Display-for-ConnectError\"]Cl[3938,13,3939,0,4119,1971,0,\"impl-Display-for-ConnectionId\"]Cj[3938,13,3939,0,4119,1327,0,\"impl-Debug-for-ConnectionId\"]Al[3938,13,3939,0,4124,1327,0]Db[3938,13,3939,0,4825,1971,0,\"impl-Display-for-TransportErrorCode\"]D`[3938,13,3939,0,4825,1327,0,\"impl-Debug-for-TransportErrorCode\"]Al[3938,13,3939,0,3309,1327,0]Al[3938,13,3939,0,4701,1327,0]Ak[3938,13,3939,0,481,1327,0]C`[3938,13,3939,0,117,1327,0,\"impl-Debug-for-Dir\"]Cb[3938,13,3939,0,117,1971,0,\"impl-Display-for-Dir\"]Cf[3938,13,3939,0,2414,1327,0,\"impl-Debug-for-StreamId\"]Ch[3938,13,3939,0,2414,1971,0,\"impl-Display-for-StreamId\"]Al[3938,13,3939,0,2423,1327,0]Ak[3938,13,3697,0,111,1327,0]Al[3938,13,3697,0,3108,1327,0]Al[3938,13,3697,0,1324,1327,0]Al[3938,13,3697,0,3978,1327,0]Al[3938,13,3697,0,1984,1327,0]Al[3938,13,3697,0,4272,1327,0]Al[3938,13,3697,0,4759,1327,0]Al[3938,13,1840,0,5072,1327,0]Al[3938,13,1840,0,3976,1327,0]Al[3938,13,1840,0,4829,1327,0]Dd[3938,13,1919,0,4922,1971,0,\"impl-Display-for-NoInitialCipherSuite\"]Db[3938,13,1919,0,4922,1327,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cc[3938,13,1919,0,1337,1327,0,\"impl-Debug-for-Error\"]Ce[3938,13,1919,0,1337,1971,0,\"impl-Display-for-Error\"]Ao[4725,13,4726,3703,3301,1327,0]Ao[4725,13,4726,2437,2392,1327,0]Ci[4725,13,4726,1952,1600,1971,0,\"impl-Display-for-VarInt\"]Cg[4725,13,4726,1952,1600,1327,0,\"impl-Debug-for-VarInt\"]Dg[4725,13,4726,1952,4927,1971,0,\"impl-Display-for-VarIntBoundsExceeded\"]De[4725,13,4726,1952,4927,1327,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Ao[4725,13,4726,3156,1321,1327,0]Db[4725,13,4726,3165,4768,1327,0,\"impl-Debug-for-SendDatagramError\"]Dd[4725,13,4726,3165,4768,1971,0,\"impl-Display-for-SendDatagramError\"]Cg[4725,13,4726,1522,1583,1327,0,\"impl-Debug-for-PathId\"]Ci[4725,13,4726,1522,1583,1971,0,\"impl-Display-for-PathId\"]Ao[4725,13,4726,1522,4148,1327,0]Ao[4725,13,4726,1522,3321,1327,0]Ao[4725,13,4726,1522,3136,1327,0]Dc[4725,13,4726,1522,4822,1327,0,\"impl-Debug-for-SetPathStatusError\"]De[4725,13,4726,1522,4822,1971,0,\"impl-Display-for-SetPathStatusError\"]Cm[4725,13,4726,1522,3296,1971,0,\"impl-Display-for-ClosedPath\"]Ck[4725,13,4726,1522,3296,1327,0,\"impl-Debug-for-ClosedPath\"]Ao[4725,13,4726,1538,2427,1327,0]Ao[4725,13,4726,1538,3307,1327,0]Ao[4725,13,4726,1538,3138,1327,0]Ao[4725,13,4726,1538,4490,1327,0]Cl[4725,13,4726,1071,3141,1971,0,\"impl-Display-for-ReadError\"]Cj[4725,13,4726,1071,3141,1327,0,\"impl-Debug-for-ReadError\"]D`[4725,13,4726,1071,4280,1971,0,\"impl-Display-for-ReadableError\"]Cn[4725,13,4726,1071,4280,1327,0,\"impl-Debug-for-ReadableError\"]Ao[4725,13,4726,1076,2011,1327,0]Cm[4725,13,4726,1076,3346,1971,0,\"impl-Display-for-WriteError\"]Ck[4725,13,4726,1076,3346,1327,0,\"impl-Debug-for-WriteError\"]Cl[4725,13,4726,1076,3982,1327,0,\"impl-Debug-for-FinishError\"]Cn[4725,13,4726,1076,3982,1971,0,\"impl-Display-for-FinishError\"]Ao[4725,13,4726,2144,4000,1327,0]Ao[4725,13,4726,2144,4392,1327,0]Co[4725,13,4726,2144,4114,1971,0,\"impl-Display-for-ClosedStream\"]Cm[4725,13,4726,2144,4114,1327,0,\"impl-Debug-for-ClosedStream\"]Db[4725,13,4726,3703,4486,1971,0,\"impl-Display-for-ConnectionError\"]D`[4725,13,4726,3703,4486,1327,0,\"impl-Debug-for-ConnectionError\"]Cl[4725,13,4726,3703,3133,1971,0,\"impl-Display-for-PathError\"]Cj[4725,13,4726,3703,3133,1327,0,\"impl-Debug-for-PathError\"]Co[4725,13,4726,3703,4358,1327,0,\"impl-Debug-for-ClosePathError\"]Da[4725,13,4726,3703,4358,1971,0,\"impl-Display-for-ClosePathError\"]Dg[4725,13,4726,3703,5010,1327,0,\"impl-Debug-for-MultipathNotNegotiated\"]Di[4725,13,4726,3703,5010,1971,0,\"impl-Display-for-MultipathNotNegotiated\"]Ao[4725,13,4726,3703,1342,1327,0]Ao[4725,13,4726,1067,4500,1327,0]Ao[4725,13,4726,3283,4507,1327,0]Ao[4725,13,4726,3283,4792,1327,0]Ao[4725,13,4726,3283,4807,1327,0]Ao[4725,13,4726,3283,3984,1327,0]Ao[4725,13,4726,1838,4368,1327,0]Ao[4725,13,4726,1838,4154,1327,0]Ao[4725,13,4726,1838,4984,1327,0]Ao[4725,13,4726,1838,4106,1327,0]Cn[4725,13,4726,1838,3974,1971,0,\"impl-Display-for-ConfigError\"]Cl[4725,13,4726,1838,3974,1327,0,\"impl-Debug-for-ConfigError\"]Cl[4725,13,4726,1512,3121,1971,0,\"impl-Display-for-FrameType\"]Cj[4725,13,4726,1512,3121,1327,0,\"impl-Debug-for-FrameType\"]Ao[4725,13,4726,1512,4373,1327,0]Cm[4725,13,4726,1512,3313,1971,0,\"impl-Display-for-MaybeFrame\"]Ck[4725,13,4726,1512,3313,1327,0,\"impl-Debug-for-MaybeFrame\"]Ck[4725,13,4726,1512,3335,1327,0,\"impl-Debug-for-StreamInfo\"]Cm[4725,13,4726,1512,3335,1971,0,\"impl-Display-for-StreamInfo\"]Co[4725,13,4726,1512,4121,1971,0,\"impl-Display-for-DatagramInfo\"]Cm[4725,13,4726,1512,4121,1327,0,\"impl-Debug-for-DatagramInfo\"]D`[4725,13,4726,1512,4484,1327,0,\"impl-Debug-for-ConnectionClose\"]Db[4725,13,4726,1512,4484,1971,0,\"impl-Display-for-ConnectionClose\"]Dc[4725,13,4726,1512,4672,1971,0,\"impl-Display-for-ApplicationClose\"]Da[4725,13,4726,1512,4672,1327,0,\"impl-Debug-for-ApplicationClose\"]Ao[4725,13,4726,1512,2385,1327,0]Ao[4725,13,4726,2437,4676,1327,0]Ao[4725,13,4726,2437,2396,1327,0]Co[4725,13,4726,2437,4117,1971,0,\"impl-Display-for-ConnectError\"]Cm[4725,13,4726,2437,4117,1327,0,\"impl-Debug-for-ConnectError\"]Ao[4725,13,4726,2437,3968,1327,0]Cm[4725,13,4726,2437,3329,1971,0,\"impl-Display-for-RetryError\"]Ck[4725,13,4726,2437,3329,1327,0,\"impl-Debug-for-RetryError\"]Ao[4725,13,4726,1903,4275,1327,0]Ao[4725,13,4726,1903,4498,1327,0]Ao[4725,13,4726,1903,5013,1327,0]Ao[4725,13,4726,1903,2398,1327,0]Dd[4725,13,4726,1903,4766,1971,0,\"impl-Display-for-PacketDecodeError\"]Db[4725,13,4726,1903,4766,1327,0,\"impl-Debug-for-PacketDecodeError\"]Ao[4725,13,4726,1930,4488,1327,0]Ao[4725,13,4726,1930,4252,1327,0]Cm[4725,13,4726,1930,4120,1327,0,\"impl-Debug-for-ConnectionId\"]Co[4725,13,4726,1930,4120,1971,0,\"impl-Display-for-ConnectionId\"]Ao[4725,13,4726,1930,4125,1327,0]Ce[4725,13,4726,0,4396,1971,0,\"impl-Display-for-Error\"]Cc[4725,13,4726,0,4396,1327,0,\"impl-Debug-for-Error\"]Cd[4725,13,4726,0,4826,1971,0,\"impl-Display-for-Code\"]Cb[4725,13,4726,0,4826,1327,0,\"impl-Debug-for-Code\"]Ao[4725,13,4726,4293,3310,1327,0]Ao[4725,13,4726,4293,5106,1327,0]Ao[4725,13,4726,4856,4702,1327,0]An[4725,13,4726,4726,482,1327,0]Cc[4725,13,4726,4726,118,1327,0,\"impl-Debug-for-Dir\"]Ce[4725,13,4726,4726,118,1971,0,\"impl-Display-for-Dir\"]Ck[4725,13,4726,4726,2415,1971,0,\"impl-Display-for-StreamId\"]Ci[4725,13,4726,4726,2415,1327,0,\"impl-Debug-for-StreamId\"]Ao[4725,13,4726,4726,2424,1327,0]Cj[4725,13,4726,4726,3119,1327,0,\"impl-Debug-for-FourTuple\"]Cl[4725,13,4726,4726,3119,1971,0,\"impl-Display-for-FourTuple\"]Cn[4725,13,1836,1836,4286,1327,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4725,13,1836,1836,4286,1971,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4725,13,4968,4968,4867,1327,0]Ch[4725,13,4968,4968,1339,1971,0,\"impl-Display-for-Error\"]Cf[4725,13,4968,4968,1339,1327,0,\"impl-Debug-for-Error\"]Ao[4725,13,1841,1841,5073,1327,0]Ao[4725,13,1841,1841,3977,1327,0]Ao[4725,13,1841,1841,4832,1327,0]Dg[4725,13,1920,1920,4923,1971,0,\"impl-Display-for-NoInitialCipherSuite\"]De[4725,13,1920,1920,4923,1327,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cc[4725,13,1920,0,1338,1327,0,\"impl-Debug-for-Error\"]Ce[4725,13,1920,0,1338,1971,0,\"impl-Display-for-Error\"]Am[4725,13,3698,133,112,1327,0]An[4725,13,3698,133,3109,1327,0]Ao[4725,13,3698,1507,1325,1327,0]Ao[4725,13,3698,1507,3979,1327,0]Ao[4725,13,3698,2485,1985,1327,0]Ao[4725,13,3698,2485,4273,1327,0]Ao[4725,13,3698,3698,4760,1327,0]Cf[4725,13,2115,2115,1340,1327,0,\"impl-Debug-for-Error\"]Ch[4725,13,2115,2115,1340,1971,0,\"impl-Display-for-Error\"]Ao[4725,13,2115,2115,1343,1327,0]An[4431,13,4432,389,4397,1327,0]Ao[4431,13,4432,4432,2412,1327,0]Ao[4431,13,4432,4432,2425,1327,0]Ao[4431,13,4432,4432,4126,1327,0]Al[4725,13,4726,1522,4148,0,0]Ai[3938,14,3939,0,2426,0,0]Al[4725,14,4726,1538,2427,0,0]Al[4725,13,4726,1903,4275,0,0]Al[4725,14,4726,1903,5013,0,0]Ah[4725,14,4497,0,470,0,0]Al[4431,14,4432,4432,2412,0,0]Ai[3938,13,3939,0,4983,0,0]Al[4725,13,4726,1838,4984,0,0]8Al[1361,13,1536,1536,4393,0,0]Al[4007,13,4008,4008,5014,0,0]Ai[3938,13,3939,0,4244,0,0]Al[3938,13,3939,2436,2391,0,0]Ai[3938,13,3939,0,3322,0,0]Ai[3938,13,3939,0,4499,0,0]Ai[3938,13,3939,0,4367,0,0]Ai[3938,13,3939,0,4152,0,0]Ai[3938,13,3939,0,4104,0,0]Ai[3938,13,3939,0,4119,0,0]Ai[3938,13,3939,0,4701,0,0]Ai[3938,13,3939,0,2414,0,0]Ah[3938,13,3697,0,111,0,0]Ai[3938,13,3697,0,1324,0,0]Ai[3938,13,3697,0,1984,0,0]Al[4725,13,4726,4516,4245,0,0]Al[4725,13,4726,1067,3323,0,0]Al[4725,13,4726,2437,2392,0,0]Al[4725,13,4726,1903,5131,0,0]Al[4725,13,4726,4293,5101,0,0]Al[4725,13,4726,1067,4500,0,0]Al[4725,13,4726,1838,4368,0,0]Al[4725,13,4726,1838,4154,0,0]Al[4725,13,4726,1838,4106,0,0]Al[4725,13,4726,1903,4275,0,0]Al[4725,13,4726,1930,4120,0,0]Ai[4725,13,4726,0,4396,0,0]Al[4725,13,4726,4293,5106,0,0]Al[4725,13,4726,4856,4702,0,0]Al[4725,13,4726,4726,2415,0,0]Aj[4725,13,3698,133,112,0,0]Al[4725,13,3698,1507,1325,0,0]Al[4725,13,3698,2485,1985,0,0]Ak[4431,13,4432,389,4397,0,0]Al[1046,13,4211,4211,5058,0,0]Al[1046,13,4211,4211,5059,0,0]Aj[3938,13,3939,0,481,124,0]Am[4725,13,4726,4726,482,124,0]Ai[3938,12,3939,0,3338,0,0]Al[3938,13,3939,2136,1996,0,0]0Ao[3938,13,3939,1548,4159,1996,0]Al[3938,13,3939,0,4284,3338,0]Al[4725,12,4726,1838,3339,0,0]Ao[4725,13,4726,1838,4285,3339,0]Al[3938,13,3939,3702,3300,0,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,13,4726,3703,3301,0,0]Al[4725,14,4726,1538,3138,0,0]Ae[1046,7,1830,0,0,0,0]Ae[1046,7,1925,0,0,0,0]Ae[3938,4,3939,0,0,0,0]Ai[3938,15,3939,0,4124,0,0]Al[4725,15,4726,1930,4125,0,0]Al[4431,15,4432,4432,4126,0,0]210Ah[3938,5,1840,1841,0,0,0]Ae[4725,5,1841,0,0,0,0]Al[4725,15,4726,1903,4498,0,0]Al[4725,15,4726,1512,3313,0,0]Ah[3938,5,3939,1045,0,0,0]Al[4725,15,4726,3703,1342,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Al[3938,15,3939,4074,4391,0,0]Ah[3938,6,3939,4726,0,0,0]Ae[4725,6,4726,0,0,0,0]Ai[3938,19,3939,0,1582,0,0]Al[4725,19,4726,1522,1583,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Ai[4725,14,3134,0,4138,0,0]Al[4431,14,4432,4432,2412,0,0]Aa[491,3,0,0,0,0,0]Ai[4725,14,4726,0,4396,0,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,14,4726,1538,3138,0,0]Al[4725,14,4726,1512,2385,0,0]An[3938,13,3939,3702,2405,458,0]An[3938,13,3939,2463,2395,458,0]An[3938,13,3939,4074,3325,458,0]An[3938,13,3939,4079,3331,458,0]An[4725,13,4726,1071,1562,458,0]Am[1361,13,1362,1362,125,465,0]An[1361,13,1362,1362,3970,465,0]An[1361,13,1536,1536,4001,465,0]An[1361,13,1536,1536,1357,465,0]An[1361,13,1536,1536,4393,465,0]Ak[491,13,492,492,3971,465,0]An[4007,13,4008,4008,5014,465,0]An[3938,13,3939,3702,4511,465,0]An[3938,13,3939,3702,2405,465,0]Ak[3938,13,3939,0,4244,465,0]An[3938,13,3939,1045,2406,465,0]An[3938,13,3939,1045,3111,465,0]An[3938,13,3939,1045,4668,465,0]An[3938,13,3939,3702,3298,465,0]An[3938,13,3939,3702,3300,465,0]An[3938,13,3939,3702,1987,465,0]An[3938,13,3939,3702,1575,465,0]An[3938,13,3939,3702,3101,465,0]An[3938,13,3939,3702,2379,465,0]An[3938,13,3939,3702,4144,465,0]An[3938,13,3939,3702,4149,465,0]An[3938,13,3939,3702,4929,465,0]An[3938,13,3939,3702,4767,465,0]0An[3938,13,3939,2436,2391,465,0]An[3938,13,3939,2436,4253,465,0]An[3938,13,3939,2436,1554,465,0]An[3938,13,3939,2463,2395,465,0]An[3938,13,3939,2463,3328,465,0]An[3938,13,3939,2463,4372,465,0]Am[3938,13,3939,1045,474,465,0]An[3938,13,3939,4074,3325,465,0]An[3938,13,3939,4074,4868,465,0]An[3938,13,3939,4074,4391,465,0]0Dm[3938,13,3939,4074,3140,465,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Dh[3938,13,3939,4074,3140,465,0,\"impl-From%3CResetError%3E-for-ReadError\"]An[3938,13,3939,4074,3140,465,0]An[3938,13,3939,4074,3327,465,0]0An[3938,13,3939,4074,4390,465,0]0An[3938,13,3939,1548,4159,465,0]An[3938,13,3939,1088,3996,465,0]An[3938,13,3939,4079,3331,465,0]An[3938,13,3939,4079,3345,465,0]Dn[3938,13,3939,4079,3345,465,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Dk[3938,13,3939,4079,3345,465,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dk[3938,13,3939,4079,3345,465,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3938,13,3939,4079,4158,465,0]0Ak[3938,13,3939,0,3322,465,0]Ak[3938,13,3939,0,4284,465,0]Ak[3938,13,3939,0,4503,465,0]Ak[3938,13,3939,0,4136,465,0]Ak[3938,13,3939,0,4381,465,0]Dj[3938,13,3939,0,1599,465,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ak[3938,13,3939,0,1599,465,0]Ck[3938,13,3939,0,1599,465,0,\"impl-From%3Cu32%3E-for-VarInt\"]D`[3938,13,3939,0,1599,465,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Cj[3938,13,3939,0,1599,465,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ck[3938,13,3939,0,1599,465,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ak[3938,13,3939,0,4926,465,0]Ak[3938,13,3939,0,1320,465,0]Ak[3938,13,3939,0,1582,465,0]0Ak[3938,13,3939,0,2426,465,0]Ak[3938,13,3939,0,3306,465,0]Ak[3938,13,3939,0,3137,465,0]Ak[3938,13,3939,0,4489,465,0]Ak[3938,13,3939,0,2010,465,0]Ak[3938,13,3939,0,4110,465,0]Dl[3938,13,3939,0,4485,465,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Do[3938,13,3939,0,4485,465,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Df[3938,13,3939,0,4485,465,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Ak[3938,13,3939,0,4485,465,0]Ak[3938,13,3939,0,4499,465,0]Ak[3938,13,3939,0,4506,465,0]Ak[3938,13,3939,0,4791,465,0]Ak[3938,13,3939,0,4806,465,0]Ak[3938,13,3939,0,3983,465,0]0Ak[3938,13,3939,0,4367,465,0]Ak[3938,13,3939,0,4152,465,0]Ak[3938,13,3939,0,4983,465,0]Ak[3938,13,3939,0,4104,465,0]Ea[3938,13,3939,0,3973,465,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3938,13,3939,0,3973,465,0]Dl[3938,13,3939,0,3973,465,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3938,13,3939,0,3120,465,0]Ak[3938,13,3939,0,4482,465,0]0Ak[3938,13,3939,0,4670,465,0]Ak[3938,13,3939,0,4116,465,0]0Ak[3938,13,3939,0,4119,465,0]Ak[3938,13,3939,0,4124,465,0]Ak[3938,13,3939,0,4825,465,0]0Ak[3938,13,3939,0,3309,465,0]Ak[3938,13,3939,0,4701,465,0]Aj[3938,13,3939,0,481,465,0]Aj[3938,13,3939,0,117,465,0]Ak[3938,13,3939,0,2414,465,0]0Ak[3938,13,3939,0,2423,465,0]Aj[3938,13,3697,0,111,465,0]Ak[3938,13,3697,0,3108,465,0]Ak[3938,13,3697,0,1324,465,0]Ak[3938,13,3697,0,3978,465,0]Ak[3938,13,3697,0,1984,465,0]Ak[3938,13,3697,0,4272,465,0]Ak[3938,13,3697,0,4759,465,0]Ak[3938,13,1840,0,1979,465,0]Aj[3938,13,1840,0,468,465,0]Ak[3938,13,1840,0,5072,465,0]Ak[3938,13,1840,0,3976,465,0]0Ak[3938,13,1840,0,4829,465,0]Ak[3938,13,1919,0,3340,465,0]Ak[3938,13,1919,0,4255,465,0]Ak[3938,13,1919,0,4697,465,0]Ak[3938,13,1919,0,4699,465,0]Ak[3938,13,1919,0,4922,465,0]Ak[3938,13,1919,0,1337,465,0]E`[3938,13,1919,0,1337,465,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[3938,13,1919,0,1337,465,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[3938,13,1919,0,1337,465,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]De[3938,13,1919,0,1337,465,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[3938,13,1919,0,1337,465,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dg[3938,13,1919,0,1337,465,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dn[3938,13,1919,0,1337,465,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]E`[3938,13,1919,0,1337,465,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Df[3938,13,1919,0,1337,465,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Df[3938,13,1919,0,1337,465,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Da[3938,13,1919,0,1337,465,0,\"impl-From%3COtherError%3E-for-Error\"]An[4725,13,4726,4516,4245,465,0]An[4725,13,4726,3165,3115,465,0]An[4725,13,4726,1071,1562,465,0]An[4725,13,4726,2144,2004,465,0]An[4725,13,4726,2144,3326,465,0]An[4725,13,4726,2144,3332,465,0]An[4725,13,4726,3703,3301,465,0]An[4725,13,4726,1067,3323,465,0]An[4725,13,4726,1838,4285,465,0]An[4725,13,4726,2437,2392,465,0]An[4725,13,4726,2437,4251,465,0]An[4725,13,4726,1903,5131,465,0]An[4725,13,4726,4293,5101,465,0]An[4725,13,4726,1547,4504,465,0]An[4725,13,4726,1547,4137,465,0]An[4725,13,4726,1547,4382,465,0]Cn[4725,13,4726,1952,1600,465,0,\"impl-From%3Cu32%3E-for-VarInt\"]Dc[4725,13,4726,1952,1600,465,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Co[4725,13,4726,1952,1600,465,0,\"impl-From%3CCode%3E-for-VarInt\"]Cm[4725,13,4726,1952,1600,465,0,\"impl-From%3Cu8%3E-for-VarInt\"]Cn[4725,13,4726,1952,1600,465,0,\"impl-From%3Cu16%3E-for-VarInt\"]An[4725,13,4726,1952,1600,465,0]An[4725,13,4726,1952,4927,465,0]An[4725,13,4726,3156,1321,465,0]An[4725,13,4726,3165,4768,465,0]An[4725,13,4726,1522,1583,465,0]0An[4725,13,4726,1522,4148,465,0]An[4725,13,4726,1522,3321,465,0]An[4725,13,4726,1522,3136,465,0]An[4725,13,4726,1522,4822,465,0]An[4725,13,4726,1522,3296,465,0]An[4725,13,4726,1538,2427,465,0]An[4725,13,4726,1538,3307,465,0]An[4725,13,4726,1538,3138,465,0]An[4725,13,4726,1538,4490,465,0]An[4725,13,4726,1071,3141,465,0]An[4725,13,4726,1071,4280,465,0]An[4725,13,4726,1076,2011,465,0]An[4725,13,4726,1076,3346,465,0]An[4725,13,4726,1076,3982,465,0]An[4725,13,4726,2144,4000,465,0]An[4725,13,4726,2144,4392,465,0]An[4725,13,4726,2144,4114,465,0]An[4725,13,4726,3703,4486,465,0]0An[4725,13,4726,3703,3133,465,0]An[4725,13,4726,3703,4358,465,0]An[4725,13,4726,3703,5010,465,0]An[4725,13,4726,3703,1342,465,0]0An[4725,13,4726,1067,4500,465,0]An[4725,13,4726,3283,4507,465,0]An[4725,13,4726,3283,4792,465,0]An[4725,13,4726,3283,4807,465,0]An[4725,13,4726,3283,3984,465,0]0An[4725,13,4726,1838,4368,465,0]An[4725,13,4726,1838,4154,465,0]An[4725,13,4726,1838,4984,465,0]An[4725,13,4726,1838,4106,465,0]Do[4725,13,4726,1838,3974,465,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ed[4725,13,4726,1838,3974,465,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[4725,13,4726,1838,3974,465,0]An[4725,13,4726,1512,3121,465,0]An[4725,13,4726,1512,4373,465,0]An[4725,13,4726,1512,3313,465,0]An[4725,13,4726,1512,3335,465,0]An[4725,13,4726,1512,4121,465,0]An[4725,13,4726,1512,4484,465,0]0An[4725,13,4726,1512,4672,465,0]An[4725,13,4726,1512,2385,465,0]An[4725,13,4726,2437,4676,465,0]An[4725,13,4726,2437,2396,465,0]An[4725,13,4726,2437,4117,465,0]0An[4725,13,4726,2437,3968,465,0]An[4725,13,4726,2437,3329,465,0]An[4725,13,4726,1903,4275,465,0]An[4725,13,4726,1903,4498,465,0]An[4725,13,4726,1903,5013,465,0]An[4725,13,4726,1903,2398,465,0]An[4725,13,4726,1903,4766,465,0]0An[4725,13,4726,1930,4488,465,0]An[4725,13,4726,1930,4252,465,0]An[4725,13,4726,1930,4120,465,0]An[4725,13,4726,1930,4125,465,0]Ak[4725,13,4726,0,4396,465,0]0Ak[4725,13,4726,0,4826,465,0]0An[4725,13,4726,4293,3310,465,0]An[4725,13,4726,4293,5106,465,0]An[4725,13,4726,4856,4702,465,0]Am[4725,13,4726,4726,482,465,0]Am[4725,13,4726,4726,118,465,0]An[4725,13,4726,4726,2415,465,0]0An[4725,13,4726,4726,2424,465,0]An[4725,13,4726,4726,3119,465,0]An[4725,13,1836,1836,4286,465,0]An[4725,13,4968,4968,4867,465,0]An[4725,13,4968,4968,1339,465,0]0An[4725,13,1841,1841,1980,465,0]Am[4725,13,1841,1841,469,465,0]An[4725,13,1841,1841,5073,465,0]An[4725,13,1841,1841,3977,465,0]0An[4725,13,1841,1841,4832,465,0]An[4725,13,1920,1920,3341,465,0]An[4725,13,1920,1920,4256,465,0]An[4725,13,1920,1920,4698,465,0]An[4725,13,1920,1920,4700,465,0]An[4725,13,1920,1920,4923,465,0]Dg[4725,13,1920,0,1338,465,0,\"impl-From%3CCertificateError%3E-for-Error\"]Df[4725,13,1920,0,1338,465,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4725,13,1920,0,1338,465,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Ak[4725,13,1920,0,1338,465,0]E`[4725,13,1920,0,1338,465,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]E`[4725,13,1920,0,1338,465,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Da[4725,13,1920,0,1338,465,0,\"impl-From%3COtherError%3E-for-Error\"]Dn[4725,13,1920,0,1338,465,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]Dg[4725,13,1920,0,1338,465,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[4725,13,1920,0,1338,465,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]De[4725,13,1920,0,1338,465,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[4725,13,1920,0,1338,465,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Al[4725,13,3698,133,112,465,0]Am[4725,13,3698,133,3109,465,0]An[4725,13,3698,1507,1325,465,0]An[4725,13,3698,1507,3979,465,0]An[4725,13,3698,2485,1985,465,0]An[4725,13,3698,2485,4273,465,0]An[4725,13,3698,3698,4760,465,0]An[4725,13,2115,2115,1340,465,0]An[4725,13,2115,2115,1343,465,0]An[4431,13,4432,4432,3344,465,0]0Am[4431,13,4432,389,4397,465,0]An[4431,13,4432,4432,2412,465,0]An[4431,13,4432,4432,2425,465,0]An[4431,13,4432,4432,4126,465,0]An[1046,13,1047,1047,3112,465,0]An[1046,13,1047,1047,4860,465,0]Am[1046,13,1830,1830,126,465,0]An[1046,13,1537,1537,1358,465,0]An[1046,13,1537,1537,4002,465,0]An[1046,13,1537,1537,4494,465,0]An[1046,13,4211,4211,5058,465,0]An[1046,13,4211,4211,5059,465,0]Am[1046,13,1925,1925,127,465,0]Am[3958,13,3959,3959,116,465,0]An[3958,13,3959,3959,2382,465,0]Ak[3938,13,3939,0,1599,466,0]Ak[3938,13,3939,0,1582,466,0]Ak[3938,13,3939,0,3983,466,0]Ak[3938,13,3939,0,4119,466,0]Aj[3938,13,3939,0,481,466,0]Aj[3938,13,3939,0,117,466,0]Ak[3938,13,3939,0,2414,466,0]An[4725,13,4726,1952,1600,466,0]An[4725,13,4726,3165,4768,466,0]An[4725,13,4726,1522,1583,466,0]An[4725,13,4726,1071,3141,466,0]An[4725,13,4726,1071,4280,466,0]An[4725,13,4726,1076,3346,466,0]An[4725,13,4726,3283,3984,466,0]An[4725,13,4726,2437,4676,466,0]An[4725,13,4726,1903,4766,466,0]An[4725,13,4726,1930,4120,466,0]Am[4725,13,4726,4726,482,466,0]Am[4725,13,4726,4726,118,466,0]An[4725,13,4726,4726,2415,466,0]An[4725,13,4726,4726,3119,466,0]Am[1361,13,1362,1362,125,467,0]An[1361,13,1362,1362,3970,467,0]An[1361,13,1536,1536,4001,467,0]An[1361,13,1536,1536,1357,467,0]An[1361,13,1536,1536,4393,467,0]Ak[491,13,492,492,3971,467,0]An[4007,13,4008,4008,5014,467,0]An[3938,13,3939,3702,4511,467,0]An[3938,13,3939,3702,2405,467,0]Ak[3938,13,3939,0,4244,467,0]An[3938,13,3939,1045,2406,467,0]An[3938,13,3939,1045,3111,467,0]An[3938,13,3939,1045,4668,467,0]An[3938,13,3939,3702,3298,467,0]An[3938,13,3939,3702,3300,467,0]An[3938,13,3939,3702,1987,467,0]An[3938,13,3939,3702,1575,467,0]An[3938,13,3939,3702,3101,467,0]An[3938,13,3939,3702,2379,467,0]An[3938,13,3939,3702,4144,467,0]An[3938,13,3939,3702,4149,467,0]An[3938,13,3939,3702,4929,467,0]An[3938,13,3939,3702,4767,467,0]An[3938,13,3939,2436,2391,467,0]An[3938,13,3939,2436,4253,467,0]An[3938,13,3939,2436,1554,467,0]An[3938,13,3939,2463,2395,467,0]An[3938,13,3939,2463,3328,467,0]An[3938,13,3939,2463,4372,467,0]Am[3938,13,3939,1045,474,467,0]An[3938,13,3939,4074,3325,467,0]An[3938,13,3939,4074,4868,467,0]An[3938,13,3939,4074,4391,467,0]An[3938,13,3939,4074,3140,467,0]An[3938,13,3939,4074,3327,467,0]An[3938,13,3939,4074,4390,467,0]An[3938,13,3939,1548,4159,467,0]An[3938,13,3939,1088,3996,467,0]An[3938,13,3939,4079,3331,467,0]An[3938,13,3939,4079,3345,467,0]An[3938,13,3939,4079,4158,467,0]Ak[3938,13,3939,0,3322,467,0]Ak[3938,13,3939,0,4284,467,0]Ak[3938,13,3939,0,4503,467,0]Ak[3938,13,3939,0,4136,467,0]Ak[3938,13,3939,0,4381,467,0]Ak[3938,13,3939,0,1599,467,0]Ak[3938,13,3939,0,4926,467,0]Ak[3938,13,3939,0,1320,467,0]Ak[3938,13,3939,0,1582,467,0]Ak[3938,13,3939,0,2426,467,0]Ak[3938,13,3939,0,3306,467,0]Ak[3938,13,3939,0,3137,467,0]Ak[3938,13,3939,0,4489,467,0]Ak[3938,13,3939,0,2010,467,0]Ak[3938,13,3939,0,4110,467,0]Ak[3938,13,3939,0,4485,467,0]Ak[3938,13,3939,0,4499,467,0]Ak[3938,13,3939,0,4506,467,0]Ak[3938,13,3939,0,4791,467,0]Ak[3938,13,3939,0,4806,467,0]Ak[3938,13,3939,0,3983,467,0]Ak[3938,13,3939,0,4367,467,0]Ak[3938,13,3939,0,4152,467,0]Ak[3938,13,3939,0,4983,467,0]Ak[3938,13,3939,0,4104,467,0]Ak[3938,13,3939,0,3973,467,0]Ak[3938,13,3939,0,3120,467,0]Ak[3938,13,3939,0,4482,467,0]Ak[3938,13,3939,0,4670,467,0]Ak[3938,13,3939,0,4116,467,0]Ak[3938,13,3939,0,4119,467,0]Ak[3938,13,3939,0,4124,467,0]Ak[3938,13,3939,0,4825,467,0]Ak[3938,13,3939,0,3309,467,0]Ak[3938,13,3939,0,4701,467,0]Aj[3938,13,3939,0,481,467,0]Aj[3938,13,3939,0,117,467,0]Ak[3938,13,3939,0,2414,467,0]Ak[3938,13,3939,0,2423,467,0]Aj[3938,13,3697,0,111,467,0]Ak[3938,13,3697,0,3108,467,0]Ak[3938,13,3697,0,1324,467,0]Ak[3938,13,3697,0,3978,467,0]Ak[3938,13,3697,0,1984,467,0]Ak[3938,13,3697,0,4272,467,0]Ak[3938,13,3697,0,4759,467,0]Ak[3938,13,1840,0,1979,467,0]Aj[3938,13,1840,0,468,467,0]Ak[3938,13,1840,0,5072,467,0]Ak[3938,13,1840,0,3976,467,0]Ak[3938,13,1840,0,4829,467,0]Ak[3938,13,1919,0,3340,467,0]Ak[3938,13,1919,0,4255,467,0]Ak[3938,13,1919,0,4697,467,0]Ak[3938,13,1919,0,4699,467,0]Ak[3938,13,1919,0,4922,467,0]Ak[3938,13,1919,0,1337,467,0]An[4725,13,4726,4516,4245,467,0]An[4725,13,4726,3165,3115,467,0]An[4725,13,4726,1071,1562,467,0]An[4725,13,4726,2144,2004,467,0]An[4725,13,4726,2144,3326,467,0]An[4725,13,4726,2144,3332,467,0]An[4725,13,4726,3703,3301,467,0]An[4725,13,4726,1067,3323,467,0]An[4725,13,4726,1838,4285,467,0]An[4725,13,4726,2437,2392,467,0]An[4725,13,4726,2437,4251,467,0]An[4725,13,4726,1903,5131,467,0]An[4725,13,4726,4293,5101,467,0]An[4725,13,4726,1547,4504,467,0]An[4725,13,4726,1547,4137,467,0]An[4725,13,4726,1547,4382,467,0]An[4725,13,4726,1952,1600,467,0]An[4725,13,4726,1952,4927,467,0]An[4725,13,4726,3156,1321,467,0]An[4725,13,4726,3165,4768,467,0]An[4725,13,4726,1522,1583,467,0]An[4725,13,4726,1522,4148,467,0]An[4725,13,4726,1522,3321,467,0]An[4725,13,4726,1522,3136,467,0]An[4725,13,4726,1522,4822,467,0]An[4725,13,4726,1522,3296,467,0]An[4725,13,4726,1538,2427,467,0]An[4725,13,4726,1538,3307,467,0]An[4725,13,4726,1538,3138,467,0]An[4725,13,4726,1538,4490,467,0]An[4725,13,4726,1071,3141,467,0]An[4725,13,4726,1071,4280,467,0]An[4725,13,4726,1076,2011,467,0]An[4725,13,4726,1076,3346,467,0]An[4725,13,4726,1076,3982,467,0]An[4725,13,4726,2144,4000,467,0]An[4725,13,4726,2144,4392,467,0]An[4725,13,4726,2144,4114,467,0]An[4725,13,4726,3703,4486,467,0]An[4725,13,4726,3703,3133,467,0]An[4725,13,4726,3703,4358,467,0]An[4725,13,4726,3703,5010,467,0]An[4725,13,4726,3703,1342,467,0]An[4725,13,4726,1067,4500,467,0]An[4725,13,4726,3283,4507,467,0]An[4725,13,4726,3283,4792,467,0]An[4725,13,4726,3283,4807,467,0]An[4725,13,4726,3283,3984,467,0]An[4725,13,4726,1838,4368,467,0]An[4725,13,4726,1838,4154,467,0]An[4725,13,4726,1838,4984,467,0]An[4725,13,4726,1838,4106,467,0]An[4725,13,4726,1838,3974,467,0]An[4725,13,4726,1512,3121,467,0]An[4725,13,4726,1512,4373,467,0]An[4725,13,4726,1512,3313,467,0]An[4725,13,4726,1512,3335,467,0]An[4725,13,4726,1512,4121,467,0]An[4725,13,4726,1512,4484,467,0]An[4725,13,4726,1512,4672,467,0]An[4725,13,4726,1512,2385,467,0]An[4725,13,4726,2437,4676,467,0]An[4725,13,4726,2437,2396,467,0]An[4725,13,4726,2437,4117,467,0]An[4725,13,4726,2437,3968,467,0]An[4725,13,4726,2437,3329,467,0]An[4725,13,4726,1903,4275,467,0]An[4725,13,4726,1903,4498,467,0]An[4725,13,4726,1903,5013,467,0]An[4725,13,4726,1903,2398,467,0]An[4725,13,4726,1903,4766,467,0]An[4725,13,4726,1930,4488,467,0]An[4725,13,4726,1930,4252,467,0]An[4725,13,4726,1930,4120,467,0]An[4725,13,4726,1930,4125,467,0]Ak[4725,13,4726,0,4396,467,0]Ak[4725,13,4726,0,4826,467,0]An[4725,13,4726,4293,3310,467,0]An[4725,13,4726,4293,5106,467,0]An[4725,13,4726,4856,4702,467,0]Am[4725,13,4726,4726,482,467,0]Am[4725,13,4726,4726,118,467,0]An[4725,13,4726,4726,2415,467,0]An[4725,13,4726,4726,2424,467,0]An[4725,13,4726,4726,3119,467,0]An[4725,13,1836,1836,4286,467,0]An[4725,13,4968,4968,4867,467,0]An[4725,13,4968,4968,1339,467,0]An[4725,13,1841,1841,1980,467,0]Am[4725,13,1841,1841,469,467,0]An[4725,13,1841,1841,5073,467,0]An[4725,13,1841,1841,3977,467,0]An[4725,13,1841,1841,4832,467,0]An[4725,13,1920,1920,3341,467,0]An[4725,13,1920,1920,4256,467,0]An[4725,13,1920,1920,4698,467,0]An[4725,13,1920,1920,4700,467,0]An[4725,13,1920,1920,4923,467,0]Ak[4725,13,1920,0,1338,467,0]Al[4725,13,3698,133,112,467,0]Am[4725,13,3698,133,3109,467,0]An[4725,13,3698,1507,1325,467,0]An[4725,13,3698,1507,3979,467,0]An[4725,13,3698,2485,1985,467,0]An[4725,13,3698,2485,4273,467,0]An[4725,13,3698,3698,4760,467,0]An[4725,13,2115,2115,1340,467,0]An[4725,13,2115,2115,1343,467,0]An[4431,13,4432,4432,3344,467,0]Am[4431,13,4432,389,4397,467,0]An[4431,13,4432,4432,2412,467,0]An[4431,13,4432,4432,2425,467,0]An[4431,13,4432,4432,4126,467,0]An[1046,13,1047,1047,3112,467,0]An[1046,13,1047,1047,4860,467,0]Am[1046,13,1830,1830,126,467,0]An[1046,13,1537,1537,1358,467,0]An[1046,13,1537,1537,4002,467,0]An[1046,13,1537,1537,4494,467,0]An[1046,13,4211,4211,5058,467,0]An[1046,13,4211,4211,5059,467,0]Am[1046,13,1925,1925,127,467,0]Am[3958,13,3959,3959,116,467,0]An[3958,13,3959,3959,2382,467,0]Ac[491,7,492,0,0,0,0]Ae[4007,7,4008,0,0,0,0]Ae[4296,7,4297,0,0,0,0]Ae[3958,7,3959,0,0,0,0]Ae[4786,7,4787,0,0,0,0]Al[4725,13,4726,1071,1562,0,0]Ai[3938,12,1840,0,1963,0,0]Al[4725,13,4726,2144,2004,0,0]Al[4725,12,1841,1841,1964,0,0]Al[3938,13,3939,3702,3300,0,0]Ab[1046,3,0,0,0,0,0]Ak[3938,13,3939,1045,474,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,13,4726,3703,3301,0,0]Al[4725,14,4726,1538,3307,0,0]Al[3938,12,3939,2136,3289,0,0]Ao[3938,13,3939,3702,4511,1572,0]Ao[3938,13,3939,3702,2405,1572,0]Ao[3938,13,3939,1045,2406,1572,0]Ao[3938,13,3939,1045,3111,1572,0]Ao[3938,13,3939,3702,3298,1572,0]Ao[3938,13,3939,3702,1987,1572,0]Ao[3938,13,3939,3702,1575,1572,0]Ao[3938,13,3939,3702,3101,1572,0]Ao[3938,13,3939,3702,2379,1572,0]Ao[3938,13,3939,3702,4144,1572,0]Ao[3938,13,3939,3702,4149,1572,0]Ao[3938,13,3939,2436,1554,1572,0]Ao[3938,13,3939,2463,4372,1572,0]?Al[3938,13,3939,4074,3325,0,0]Al[4725,13,4726,2144,3326,0,0]Al[4725,13,4968,4968,4867,0,0]Al[4725,13,4726,3165,3115,0,0]Ak[4431,13,4432,389,4397,0,0]Ai[3938,12,1840,0,1963,0,0]Al[4725,12,1841,1841,1964,0,0]32Ai[3938,13,3939,0,4983,0,0]Al[4725,13,4726,1838,4984,0,0]Al[3938,13,3939,3702,3300,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,12,1840,0,1975,0,0]Al[4725,12,1841,1841,1976,0,0]Al[1361,14,1536,1536,4393,0,0]Ai[3938,14,3939,0,2423,0,0]Al[4725,14,4726,4726,2424,0,0]Ai[4725,14,4497,0,1354,0,0]Al[3938,13,3939,4074,3325,0,0]Al[3938,13,3939,4074,4868,0,0]Al[4725,13,4726,2144,3326,0,0]Ai[3938,12,3939,0,3342,0,0]Al[3938,13,3939,0,4381,3342,0]Al[3938,13,3939,0,4701,3342,0]Al[4725,12,4726,1547,3343,0,0]Ao[4725,13,4726,1547,4382,3343,0]Ao[4725,13,4726,4856,4702,3343,0]Am[1361,13,1362,1362,125,484,0]An[1361,13,1362,1362,3970,484,0]An[1361,13,1536,1536,4001,484,0]An[1361,13,1536,1536,1357,484,0]An[1361,13,1536,1536,4393,484,0]Ak[491,13,492,492,3971,484,0]An[4007,13,4008,4008,5014,484,0]An[3938,13,3939,3702,4511,484,0]An[3938,13,3939,3702,2405,484,0]Ak[3938,13,3939,0,4244,484,0]An[3938,13,3939,1045,2406,484,0]An[3938,13,3939,1045,3111,484,0]An[3938,13,3939,1045,4668,484,0]An[3938,13,3939,3702,3298,484,0]An[3938,13,3939,3702,3300,484,0]An[3938,13,3939,3702,1987,484,0]An[3938,13,3939,3702,1575,484,0]An[3938,13,3939,3702,3101,484,0]An[3938,13,3939,3702,2379,484,0]An[3938,13,3939,3702,4144,484,0]An[3938,13,3939,3702,4149,484,0]An[3938,13,3939,3702,4929,484,0]An[3938,13,3939,3702,4767,484,0]An[3938,13,3939,2436,2391,484,0]An[3938,13,3939,2436,4253,484,0]An[3938,13,3939,2436,1554,484,0]An[3938,13,3939,2463,2395,484,0]An[3938,13,3939,2463,3328,484,0]An[3938,13,3939,2463,4372,484,0]Am[3938,13,3939,1045,474,484,0]An[3938,13,3939,4074,3325,484,0]An[3938,13,3939,4074,4868,484,0]An[3938,13,3939,4074,4391,484,0]An[3938,13,3939,4074,3140,484,0]An[3938,13,3939,4074,3327,484,0]An[3938,13,3939,4074,4390,484,0]An[3938,13,3939,1548,4159,484,0]An[3938,13,3939,1088,3996,484,0]An[3938,13,3939,4079,3331,484,0]An[3938,13,3939,4079,3345,484,0]An[3938,13,3939,4079,4158,484,0]Ak[3938,13,3939,0,3322,484,0]Ak[3938,13,3939,0,4284,484,0]Ak[3938,13,3939,0,4503,484,0]Ak[3938,13,3939,0,4136,484,0]Ak[3938,13,3939,0,4381,484,0]Ak[3938,13,3939,0,1599,484,0]Ak[3938,13,3939,0,4926,484,0]Ak[3938,13,3939,0,1320,484,0]Ak[3938,13,3939,0,1582,484,0]Ak[3938,13,3939,0,2426,484,0]Ak[3938,13,3939,0,3306,484,0]Ak[3938,13,3939,0,3137,484,0]Ak[3938,13,3939,0,4489,484,0]Ak[3938,13,3939,0,2010,484,0]Ak[3938,13,3939,0,4110,484,0]Ak[3938,13,3939,0,4485,484,0]Ak[3938,13,3939,0,4499,484,0]Ak[3938,13,3939,0,4506,484,0]Ak[3938,13,3939,0,4791,484,0]Ak[3938,13,3939,0,4806,484,0]Ak[3938,13,3939,0,3983,484,0]Ak[3938,13,3939,0,4367,484,0]Ak[3938,13,3939,0,4152,484,0]Ak[3938,13,3939,0,4983,484,0]Ak[3938,13,3939,0,4104,484,0]Ak[3938,13,3939,0,3973,484,0]Ak[3938,13,3939,0,3120,484,0]Ak[3938,13,3939,0,4482,484,0]Ak[3938,13,3939,0,4670,484,0]Ak[3938,13,3939,0,4116,484,0]Ak[3938,13,3939,0,4119,484,0]Ak[3938,13,3939,0,4124,484,0]Ak[3938,13,3939,0,4825,484,0]Ak[3938,13,3939,0,3309,484,0]Ak[3938,13,3939,0,4701,484,0]Aj[3938,13,3939,0,481,484,0]Aj[3938,13,3939,0,117,484,0]Ak[3938,13,3939,0,2414,484,0]Ak[3938,13,3939,0,2423,484,0]Aj[3938,13,3697,0,111,484,0]Ak[3938,13,3697,0,3108,484,0]Ak[3938,13,3697,0,1324,484,0]Ak[3938,13,3697,0,3978,484,0]Ak[3938,13,3697,0,1984,484,0]Ak[3938,13,3697,0,4272,484,0]Ak[3938,13,3697,0,4759,484,0]Ak[3938,13,1840,0,1979,484,0]Aj[3938,13,1840,0,468,484,0]Ak[3938,13,1840,0,5072,484,0]Ak[3938,13,1840,0,3976,484,0]Ak[3938,13,1840,0,4829,484,0]Ak[3938,13,1919,0,3340,484,0]Ak[3938,13,1919,0,4255,484,0]Ak[3938,13,1919,0,4697,484,0]Ak[3938,13,1919,0,4699,484,0]Ak[3938,13,1919,0,4922,484,0]Ak[3938,13,1919,0,1337,484,0]An[4725,13,4726,4516,4245,484,0]An[4725,13,4726,3165,3115,484,0]An[4725,13,4726,1071,1562,484,0]An[4725,13,4726,2144,2004,484,0]An[4725,13,4726,2144,3326,484,0]An[4725,13,4726,2144,3332,484,0]An[4725,13,4726,3703,3301,484,0]An[4725,13,4726,1067,3323,484,0]An[4725,13,4726,1838,4285,484,0]An[4725,13,4726,2437,2392,484,0]An[4725,13,4726,2437,4251,484,0]An[4725,13,4726,1903,5131,484,0]An[4725,13,4726,4293,5101,484,0]An[4725,13,4726,1547,4504,484,0]An[4725,13,4726,1547,4137,484,0]An[4725,13,4726,1547,4382,484,0]An[4725,13,4726,1952,1600,484,0]An[4725,13,4726,1952,4927,484,0]An[4725,13,4726,3156,1321,484,0]An[4725,13,4726,3165,4768,484,0]An[4725,13,4726,1522,1583,484,0]An[4725,13,4726,1522,4148,484,0]An[4725,13,4726,1522,3321,484,0]An[4725,13,4726,1522,3136,484,0]An[4725,13,4726,1522,4822,484,0]An[4725,13,4726,1522,3296,484,0]An[4725,13,4726,1538,2427,484,0]An[4725,13,4726,1538,3307,484,0]An[4725,13,4726,1538,3138,484,0]An[4725,13,4726,1538,4490,484,0]An[4725,13,4726,1071,3141,484,0]An[4725,13,4726,1071,4280,484,0]An[4725,13,4726,1076,2011,484,0]An[4725,13,4726,1076,3346,484,0]An[4725,13,4726,1076,3982,484,0]An[4725,13,4726,2144,4000,484,0]An[4725,13,4726,2144,4392,484,0]An[4725,13,4726,2144,4114,484,0]An[4725,13,4726,3703,4486,484,0]An[4725,13,4726,3703,3133,484,0]An[4725,13,4726,3703,4358,484,0]An[4725,13,4726,3703,5010,484,0]An[4725,13,4726,3703,1342,484,0]An[4725,13,4726,1067,4500,484,0]An[4725,13,4726,3283,4507,484,0]An[4725,13,4726,3283,4792,484,0]An[4725,13,4726,3283,4807,484,0]An[4725,13,4726,3283,3984,484,0]An[4725,13,4726,1838,4368,484,0]An[4725,13,4726,1838,4154,484,0]An[4725,13,4726,1838,4984,484,0]An[4725,13,4726,1838,4106,484,0]An[4725,13,4726,1838,3974,484,0]An[4725,13,4726,1512,3121,484,0]An[4725,13,4726,1512,4373,484,0]An[4725,13,4726,1512,3313,484,0]An[4725,13,4726,1512,3335,484,0]An[4725,13,4726,1512,4121,484,0]An[4725,13,4726,1512,4484,484,0]An[4725,13,4726,1512,4672,484,0]An[4725,13,4726,1512,2385,484,0]An[4725,13,4726,2437,4676,484,0]An[4725,13,4726,2437,2396,484,0]An[4725,13,4726,2437,4117,484,0]An[4725,13,4726,2437,3968,484,0]An[4725,13,4726,2437,3329,484,0]An[4725,13,4726,1903,4275,484,0]An[4725,13,4726,1903,4498,484,0]An[4725,13,4726,1903,5013,484,0]An[4725,13,4726,1903,2398,484,0]An[4725,13,4726,1903,4766,484,0]An[4725,13,4726,1930,4488,484,0]An[4725,13,4726,1930,4252,484,0]An[4725,13,4726,1930,4120,484,0]An[4725,13,4726,1930,4125,484,0]Ak[4725,13,4726,0,4396,484,0]Ak[4725,13,4726,0,4826,484,0]An[4725,13,4726,4293,3310,484,0]An[4725,13,4726,4293,5106,484,0]An[4725,13,4726,4856,4702,484,0]Am[4725,13,4726,4726,482,484,0]Am[4725,13,4726,4726,118,484,0]An[4725,13,4726,4726,2415,484,0]An[4725,13,4726,4726,2424,484,0]An[4725,13,4726,4726,3119,484,0]An[4725,13,1836,1836,4286,484,0]An[4725,13,4968,4968,4867,484,0]An[4725,13,4968,4968,1339,484,0]An[4725,13,1841,1841,1980,484,0]Am[4725,13,1841,1841,469,484,0]An[4725,13,1841,1841,5073,484,0]An[4725,13,1841,1841,3977,484,0]An[4725,13,1841,1841,4832,484,0]An[4725,13,1920,1920,3341,484,0]An[4725,13,1920,1920,4256,484,0]An[4725,13,1920,1920,4698,484,0]An[4725,13,1920,1920,4700,484,0]An[4725,13,1920,1920,4923,484,0]Ak[4725,13,1920,0,1338,484,0]Al[4725,13,3698,133,112,484,0]Am[4725,13,3698,133,3109,484,0]An[4725,13,3698,1507,1325,484,0]An[4725,13,3698,1507,3979,484,0]An[4725,13,3698,2485,1985,484,0]An[4725,13,3698,2485,4273,484,0]An[4725,13,3698,3698,4760,484,0]An[4725,13,2115,2115,1340,484,0]An[4725,13,2115,2115,1343,484,0]An[1046,13,1047,1047,3112,484,0]An[1046,13,1047,1047,4860,484,0]Am[1046,13,1830,1830,126,484,0]An[1046,13,1537,1537,1358,484,0]An[1046,13,1537,1537,4002,484,0]An[1046,13,1537,1537,4494,484,0]An[1046,13,4211,4211,5058,484,0]An[1046,13,4211,4211,5059,484,0]Am[1046,13,1925,1925,127,484,0]Am[3958,13,3959,3959,116,484,0]An[3958,13,3959,3959,2382,484,0]Ah[3938,5,3939,3156,0,0,0]Ah[4725,5,4726,3156,0,0,0]Ah[3938,5,3697,1507,0,0,0]Ah[4725,5,3698,1507,0,0,0]Al[1046,15,1047,1047,4860,0,0]Ah[3938,6,1919,1510,0,0,0]Ah[4725,6,1920,1510,0,0,0]Ae[4725,6,4968,0,0,0,0]Ae[4725,6,2115,0,0,0,0]Ah[4725,6,4726,3703,0,0,0]1Al[4725,15,4726,1512,3313,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]Al[3938,15,3939,4074,3140,0,0]Ai[3938,15,3939,0,4485,0,0]Al[4725,15,4726,1071,3141,0,0]Al[4725,15,4726,3703,4486,0,0]Al[4725,15,4726,1903,4498,0,0]0Ae[1361,5,1536,0,0,0,0]Ae[1046,5,1537,0,0,0,0]Ab[1361,3,0,0,0,0,0]Ai[3938,12,3697,0,4757,0,0]Al[3938,13,3697,0,3108,4757,0]Al[3938,13,3697,0,3978,4757,0]Al[3938,13,3697,0,4272,4757,0]Al[4725,12,3698,3698,4758,0,0]An[4725,13,3698,133,3109,4758,0]Ao[4725,13,3698,1507,3979,4758,0]Ao[4725,13,3698,2485,4273,4758,0]Al[1046,13,1047,1047,4860,0,0]Ai[3938,14,3939,0,1320,0,0]Ai[3938,14,3939,0,2426,0,0]Ai[3938,14,3939,0,2010,0,0]Al[4725,14,4726,3156,1321,0,0]Al[4725,14,4726,1538,2427,0,0]Al[4725,14,4726,1076,2011,0,0]Al[4725,14,4726,2437,3968,0,0]An[1361,13,1362,1362,125,1322,0]Ao[1361,13,1362,1362,3970,1322,0]Ao[3938,13,3939,3702,3300,1322,0]Ao[3938,13,3939,3702,4929,1322,0]Ao[3938,13,3939,3702,4767,1322,0]Ao[3938,13,3939,2436,2391,1322,0]Ao[3938,13,3939,2436,4253,1322,0]Ao[3938,13,3939,4074,4391,1322,0]Ao[3938,13,3939,4074,3140,1322,0]Ao[3938,13,3939,4074,3327,1322,0]Ao[3938,13,3939,4074,4390,1322,0]Ao[3938,13,3939,4079,3345,1322,0]Ao[3938,13,3939,4079,4158,1322,0]Al[3938,13,3939,0,1599,1322,0]Al[3938,13,3939,0,4926,1322,0]Al[3938,13,3939,0,1582,1322,0]Al[3938,13,3939,0,2426,1322,0]Al[3938,13,3939,0,3306,1322,0]Al[3938,13,3939,0,3137,1322,0]Al[3938,13,3939,0,4489,1322,0]Al[3938,13,3939,0,2010,1322,0]Al[3938,13,3939,0,4110,1322,0]Al[3938,13,3939,0,4485,1322,0]Al[3938,13,3939,0,4506,1322,0]Al[3938,13,3939,0,4791,1322,0]Al[3938,13,3939,0,4806,1322,0]Al[3938,13,3939,0,3983,1322,0]Al[3938,13,3939,0,4367,1322,0]Al[3938,13,3939,0,4152,1322,0]Al[3938,13,3939,0,4983,1322,0]Al[3938,13,3939,0,4104,1322,0]Al[3938,13,3939,0,3973,1322,0]Al[3938,13,3939,0,3120,1322,0]Al[3938,13,3939,0,4482,1322,0]Al[3938,13,3939,0,4670,1322,0]Al[3938,13,3939,0,4116,1322,0]Al[3938,13,3939,0,4119,1322,0]Al[3938,13,3939,0,4124,1322,0]Al[3938,13,3939,0,4825,1322,0]Al[3938,13,3939,0,3309,1322,0]Ak[3938,13,3939,0,481,1322,0]Ak[3938,13,3939,0,117,1322,0]Al[3938,13,3939,0,2414,1322,0]Ak[3938,13,3697,0,111,1322,0]Al[3938,13,3697,0,3108,1322,0]Al[3938,13,3697,0,1324,1322,0]Al[3938,13,3697,0,3978,1322,0]Al[3938,13,3697,0,1984,1322,0]Al[3938,13,3697,0,4272,1322,0]Al[3938,13,1919,0,4922,1322,0]Al[3938,13,1919,0,1337,1322,0]Ao[4725,13,4726,1952,1600,1322,0]Ao[4725,13,4726,1952,4927,1322,0]Ao[4725,13,4726,3165,4768,1322,0]Ao[4725,13,4726,1522,1583,1322,0]Ao[4725,13,4726,1522,4148,1322,0]Ao[4725,13,4726,1522,3321,1322,0]Ao[4725,13,4726,1522,3136,1322,0]Ao[4725,13,4726,1522,4822,1322,0]Ao[4725,13,4726,1522,3296,1322,0]Ao[4725,13,4726,1538,2427,1322,0]Ao[4725,13,4726,1538,3307,1322,0]Ao[4725,13,4726,1538,3138,1322,0]Ao[4725,13,4726,1538,4490,1322,0]Ao[4725,13,4726,1071,3141,1322,0]Ao[4725,13,4726,1071,4280,1322,0]Ao[4725,13,4726,1076,2011,1322,0]Ao[4725,13,4726,1076,3346,1322,0]Ao[4725,13,4726,1076,3982,1322,0]Ao[4725,13,4726,2144,4392,1322,0]Ao[4725,13,4726,2144,4114,1322,0]Ao[4725,13,4726,3703,4486,1322,0]Ao[4725,13,4726,3703,3133,1322,0]Ao[4725,13,4726,3703,4358,1322,0]Ao[4725,13,4726,3703,5010,1322,0]Ao[4725,13,4726,3283,4507,1322,0]Ao[4725,13,4726,3283,4792,1322,0]Ao[4725,13,4726,3283,4807,1322,0]Ao[4725,13,4726,3283,3984,1322,0]Ao[4725,13,4726,1838,4368,1322,0]Ao[4725,13,4726,1838,4154,1322,0]Ao[4725,13,4726,1838,4984,1322,0]Ao[4725,13,4726,1838,4106,1322,0]Ao[4725,13,4726,1838,3974,1322,0]Ao[4725,13,4726,1512,3121,1322,0]Ao[4725,13,4726,1512,3313,1322,0]Ao[4725,13,4726,1512,3335,1322,0]Ao[4725,13,4726,1512,4121,1322,0]Ao[4725,13,4726,1512,4484,1322,0]Ao[4725,13,4726,1512,4672,1322,0]Ao[4725,13,4726,1512,2385,1322,0]Ao[4725,13,4726,2437,4676,1322,0]Ao[4725,13,4726,2437,4117,1322,0]Ao[4725,13,4726,1903,4498,1322,0]Ao[4725,13,4726,1903,5013,1322,0]Ao[4725,13,4726,1903,2398,1322,0]Ao[4725,13,4726,1903,4766,1322,0]Ao[4725,13,4726,1930,4120,1322,0]Ao[4725,13,4726,1930,4125,1322,0]Al[4725,13,4726,0,4396,1322,0]Al[4725,13,4726,0,4826,1322,0]Ao[4725,13,4726,4293,3310,1322,0]Ao[4725,13,4726,4293,5106,1322,0]An[4725,13,4726,4726,482,1322,0]An[4725,13,4726,4726,118,1322,0]Ao[4725,13,4726,4726,2415,1322,0]Ao[4725,13,4726,4726,3119,1322,0]Ao[4725,13,1836,1836,4286,1322,0]Ao[4725,13,4968,4968,4867,1322,0]Ao[4725,13,4968,4968,1339,1322,0]Ao[4725,13,1920,1920,4923,1322,0]Al[4725,13,1920,0,1338,1322,0]Am[4725,13,3698,133,112,1322,0]An[4725,13,3698,133,3109,1322,0]Ao[4725,13,3698,1507,1325,1322,0]Ao[4725,13,3698,1507,3979,1322,0]Ao[4725,13,3698,2485,1985,1322,0]Ao[4725,13,3698,2485,4273,1322,0]Ao[4725,13,2115,2115,1343,1322,0]Ao[4431,13,4432,4432,2412,1322,0]Ao[4431,13,4432,4432,2425,1322,0]Ao[4431,13,4432,4432,4126,1322,0]Ao[1046,13,1047,1047,4860,1322,0]Ao[1046,13,1537,1537,4494,1322,0]Al[3938,13,3939,3702,3300,0,0]Al[3938,13,3939,2436,2391,0,0]Ak[3938,13,3939,1045,474,0,0]Al[4725,13,4726,3703,3301,0,0]Al[3938,13,3939,0,4119,1328,0]Ao[4725,13,4726,1930,4120,1328,0]Ai[4725,14,3134,0,4263,0,0]Ai[4725,14,4726,0,4396,0,0]Ai[3938,13,3939,0,2414,0,0]Al[4725,13,4726,4726,2415,0,0]Ai[3938,13,3939,0,4124,0,0]Al[4725,13,4726,1930,4125,0,0]Ai[3938,14,1840,0,1979,0,0]Al[4725,14,1841,1841,1980,0,0]Al[4725,12,4726,1903,4797,0,0]Ao[4725,13,4726,1903,5131,4797,0]=Al[4725,13,4968,4968,4867,0,0]Ah[3938,5,3939,2436,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Al[1361,15,1362,1362,3970,0,0]0Al[4725,15,4726,1522,3321,0,0]Ah[4725,5,4726,1071,0,0,0]Ah[3938,15,3939,0,481,0,0]Ak[4725,15,4726,4726,482,0,0]Al[3958,15,3959,3959,2382,0,0]Al[4725,15,4726,1522,3136,0,0]Al[4725,15,2115,2115,1340,0,0]98Ah[3938,5,3939,3702,0,0,0]2Al[4725,15,4726,2144,4000,0,0]Ah[3938,5,3939,1522,0,0,0]Ah[4725,5,4726,1522,0,0,0]Ae[4725,8,1836,0,0,0,0]987>Al[4725,15,4726,3703,1342,0,0]>Ah[3938,5,3939,1952,0,0,0]Ah[4725,5,4726,1952,0,0,0]Al[3938,13,3939,2436,2391,0,0]Al[3938,13,3939,2463,2395,0,0]Al[4725,13,4726,2144,2004,0,0]Al[4725,13,4726,2437,2392,0,0]An[1361,13,1362,1362,125,1560,0]Ao[1361,13,1362,1362,3970,1560,0]Ao[1361,13,1536,1536,4001,1560,0]Ao[1361,13,1536,1536,1357,1560,0]Ao[1361,13,1536,1536,4393,1560,0]Al[491,13,492,492,3971,1560,0]Ao[4007,13,4008,4008,5014,1560,0]Ao[3938,13,3939,3702,4511,1560,0]Ao[3938,13,3939,3702,2405,1560,0]Al[3938,13,3939,0,4244,1560,0]Ao[3938,13,3939,1045,2406,1560,0]Ao[3938,13,3939,1045,3111,1560,0]Ao[3938,13,3939,1045,4668,1560,0]Ao[3938,13,3939,3702,3298,1560,0]Ao[3938,13,3939,3702,3300,1560,0]Ao[3938,13,3939,3702,1987,1560,0]Ao[3938,13,3939,3702,1575,1560,0]Ao[3938,13,3939,3702,3101,1560,0]Ao[3938,13,3939,3702,2379,1560,0]Ao[3938,13,3939,3702,4144,1560,0]Ao[3938,13,3939,3702,4149,1560,0]Ao[3938,13,3939,3702,4929,1560,0]Ao[3938,13,3939,3702,4767,1560,0]Ao[3938,13,3939,2436,2391,1560,0]Ao[3938,13,3939,2436,4253,1560,0]Ao[3938,13,3939,2436,1554,1560,0]Ao[3938,13,3939,2463,2395,1560,0]Ao[3938,13,3939,2463,3328,1560,0]Ao[3938,13,3939,2463,4372,1560,0]An[3938,13,3939,1045,474,1560,0]Ao[3938,13,3939,4074,3325,1560,0]Ao[3938,13,3939,4074,4868,1560,0]Ao[3938,13,3939,4074,4391,1560,0]Ao[3938,13,3939,4074,3140,1560,0]Ao[3938,13,3939,4074,3327,1560,0]Ao[3938,13,3939,4074,4390,1560,0]Ao[3938,13,3939,1548,4159,1560,0]Ao[3938,13,3939,1088,3996,1560,0]Ao[3938,13,3939,4079,3331,1560,0]Ao[3938,13,3939,4079,3345,1560,0]Ao[3938,13,3939,4079,4158,1560,0]Al[3938,13,3939,0,3322,1560,0]Al[3938,13,3939,0,4284,1560,0]Al[3938,13,3939,0,4503,1560,0]Al[3938,13,3939,0,4136,1560,0]Al[3938,13,3939,0,4381,1560,0]Al[3938,13,3939,0,1599,1560,0]Al[3938,13,3939,0,4926,1560,0]Al[3938,13,3939,0,1320,1560,0]Al[3938,13,3939,0,1582,1560,0]Al[3938,13,3939,0,2426,1560,0]Al[3938,13,3939,0,3306,1560,0]Al[3938,13,3939,0,3137,1560,0]Al[3938,13,3939,0,4489,1560,0]Al[3938,13,3939,0,2010,1560,0]Al[3938,13,3939,0,4110,1560,0]Al[3938,13,3939,0,4485,1560,0]Al[3938,13,3939,0,4499,1560,0]Al[3938,13,3939,0,4506,1560,0]Al[3938,13,3939,0,4791,1560,0]Al[3938,13,3939,0,4806,1560,0]Al[3938,13,3939,0,3983,1560,0]Al[3938,13,3939,0,4367,1560,0]Al[3938,13,3939,0,4152,1560,0]Al[3938,13,3939,0,4983,1560,0]Al[3938,13,3939,0,4104,1560,0]Al[3938,13,3939,0,3973,1560,0]Al[3938,13,3939,0,3120,1560,0]Al[3938,13,3939,0,4482,1560,0]Al[3938,13,3939,0,4670,1560,0]Al[3938,13,3939,0,4116,1560,0]Al[3938,13,3939,0,4119,1560,0]Al[3938,13,3939,0,4124,1560,0]Al[3938,13,3939,0,4825,1560,0]Al[3938,13,3939,0,3309,1560,0]Al[3938,13,3939,0,4701,1560,0]Ak[3938,13,3939,0,481,1560,0]Ak[3938,13,3939,0,117,1560,0]Al[3938,13,3939,0,2414,1560,0]Al[3938,13,3939,0,2423,1560,0]Ak[3938,13,3697,0,111,1560,0]Al[3938,13,3697,0,3108,1560,0]Al[3938,13,3697,0,1324,1560,0]Al[3938,13,3697,0,3978,1560,0]Al[3938,13,3697,0,1984,1560,0]Al[3938,13,3697,0,4272,1560,0]Al[3938,13,3697,0,4759,1560,0]Al[3938,13,1840,0,1979,1560,0]Ak[3938,13,1840,0,468,1560,0]Al[3938,13,1840,0,5072,1560,0]Al[3938,13,1840,0,3976,1560,0]Al[3938,13,1840,0,4829,1560,0]Al[3938,13,1919,0,3340,1560,0]Al[3938,13,1919,0,4255,1560,0]Al[3938,13,1919,0,4697,1560,0]Al[3938,13,1919,0,4699,1560,0]Al[3938,13,1919,0,4922,1560,0]Al[3938,13,1919,0,1337,1560,0]Ao[4725,13,4726,4516,4245,1560,0]Ao[4725,13,4726,3165,3115,1560,0]Ao[4725,13,4726,1071,1562,1560,0]Ao[4725,13,4726,2144,2004,1560,0]Ao[4725,13,4726,2144,3326,1560,0]Ao[4725,13,4726,2144,3332,1560,0]Ao[4725,13,4726,3703,3301,1560,0]Ao[4725,13,4726,1067,3323,1560,0]Ao[4725,13,4726,1838,4285,1560,0]Ao[4725,13,4726,2437,2392,1560,0]Ao[4725,13,4726,2437,4251,1560,0]Ao[4725,13,4726,1903,5131,1560,0]Ao[4725,13,4726,4293,5101,1560,0]Ao[4725,13,4726,1547,4504,1560,0]Ao[4725,13,4726,1547,4137,1560,0]Ao[4725,13,4726,1547,4382,1560,0]Ao[4725,13,4726,1952,1600,1560,0]Ao[4725,13,4726,1952,4927,1560,0]Ao[4725,13,4726,3156,1321,1560,0]Ao[4725,13,4726,3165,4768,1560,0]Ao[4725,13,4726,1522,1583,1560,0]Ao[4725,13,4726,1522,4148,1560,0]Ao[4725,13,4726,1522,3321,1560,0]Ao[4725,13,4726,1522,3136,1560,0]Ao[4725,13,4726,1522,4822,1560,0]Ao[4725,13,4726,1522,3296,1560,0]Ao[4725,13,4726,1538,2427,1560,0]Ao[4725,13,4726,1538,3307,1560,0]Ao[4725,13,4726,1538,3138,1560,0]Ao[4725,13,4726,1538,4490,1560,0]Ao[4725,13,4726,1071,3141,1560,0]Ao[4725,13,4726,1071,4280,1560,0]Ao[4725,13,4726,1076,2011,1560,0]Ao[4725,13,4726,1076,3346,1560,0]Ao[4725,13,4726,1076,3982,1560,0]Ao[4725,13,4726,2144,4000,1560,0]Ao[4725,13,4726,2144,4392,1560,0]Ao[4725,13,4726,2144,4114,1560,0]Ao[4725,13,4726,3703,4486,1560,0]Ao[4725,13,4726,3703,3133,1560,0]Ao[4725,13,4726,3703,4358,1560,0]Ao[4725,13,4726,3703,5010,1560,0]Ao[4725,13,4726,3703,1342,1560,0]Ao[4725,13,4726,1067,4500,1560,0]Ao[4725,13,4726,3283,4507,1560,0]Ao[4725,13,4726,3283,4792,1560,0]Ao[4725,13,4726,3283,4807,1560,0]Ao[4725,13,4726,3283,3984,1560,0]Ao[4725,13,4726,1838,4368,1560,0]Ao[4725,13,4726,1838,4154,1560,0]Ao[4725,13,4726,1838,4984,1560,0]Ao[4725,13,4726,1838,4106,1560,0]Ao[4725,13,4726,1838,3974,1560,0]Ao[4725,13,4726,1512,3121,1560,0]Ao[4725,13,4726,1512,4373,1560,0]Ao[4725,13,4726,1512,3313,1560,0]Ao[4725,13,4726,1512,3335,1560,0]Ao[4725,13,4726,1512,4121,1560,0]Ao[4725,13,4726,1512,4484,1560,0]Ao[4725,13,4726,1512,4672,1560,0]Ao[4725,13,4726,1512,2385,1560,0]Ao[4725,13,4726,2437,4676,1560,0]Ao[4725,13,4726,2437,2396,1560,0]Ao[4725,13,4726,2437,4117,1560,0]Ao[4725,13,4726,2437,3968,1560,0]Ao[4725,13,4726,2437,3329,1560,0]Ao[4725,13,4726,1903,4275,1560,0]Ao[4725,13,4726,1903,4498,1560,0]Ao[4725,13,4726,1903,5013,1560,0]Ao[4725,13,4726,1903,2398,1560,0]Ao[4725,13,4726,1903,4766,1560,0]Ao[4725,13,4726,1930,4488,1560,0]Ao[4725,13,4726,1930,4252,1560,0]Ao[4725,13,4726,1930,4120,1560,0]Ao[4725,13,4726,1930,4125,1560,0]Al[4725,13,4726,0,4396,1560,0]Al[4725,13,4726,0,4826,1560,0]Ao[4725,13,4726,4293,3310,1560,0]Ao[4725,13,4726,4293,5106,1560,0]Ao[4725,13,4726,4856,4702,1560,0]An[4725,13,4726,4726,482,1560,0]An[4725,13,4726,4726,118,1560,0]Ao[4725,13,4726,4726,2415,1560,0]Ao[4725,13,4726,4726,2424,1560,0]Ao[4725,13,4726,4726,3119,1560,0]Ao[4725,13,1836,1836,4286,1560,0]Ao[4725,13,4968,4968,4867,1560,0]Ao[4725,13,4968,4968,1339,1560,0]Ao[4725,13,1841,1841,1980,1560,0]An[4725,13,1841,1841,469,1560,0]Ao[4725,13,1841,1841,5073,1560,0]Ao[4725,13,1841,1841,3977,1560,0]Ao[4725,13,1841,1841,4832,1560,0]Ao[4725,13,1920,1920,3341,1560,0]Ao[4725,13,1920,1920,4256,1560,0]Ao[4725,13,1920,1920,4698,1560,0]Ao[4725,13,1920,1920,4700,1560,0]Ao[4725,13,1920,1920,4923,1560,0]Al[4725,13,1920,0,1338,1560,0]Am[4725,13,3698,133,112,1560,0]An[4725,13,3698,133,3109,1560,0]Ao[4725,13,3698,1507,1325,1560,0]Ao[4725,13,3698,1507,3979,1560,0]Ao[4725,13,3698,2485,1985,1560,0]Ao[4725,13,3698,2485,4273,1560,0]Ao[4725,13,3698,3698,4760,1560,0]Ao[4725,13,2115,2115,1340,1560,0]Ao[4725,13,2115,2115,1343,1560,0]Ao[4431,13,4432,4432,3344,1560,0]An[4431,13,4432,389,4397,1560,0]Ao[4431,13,4432,4432,2412,1560,0]Ao[4431,13,4432,4432,2425,1560,0]Ao[4431,13,4432,4432,4126,1560,0]Ao[1046,13,1047,1047,3112,1560,0]Ao[1046,13,1047,1047,4860,1560,0]An[1046,13,1830,1830,126,1560,0]Ao[1046,13,1537,1537,1358,1560,0]Ao[1046,13,1537,1537,4002,1560,0]Ao[1046,13,1537,1537,4494,1560,0]Ao[1046,13,4211,4211,5058,1560,0]Ao[1046,13,4211,4211,5059,1560,0]An[1046,13,1925,1925,127,1560,0]An[3958,13,3959,3959,116,1560,0]Ao[3958,13,3959,3959,2382,1560,0]Ai[3938,14,3939,0,2010,0,0]Al[4725,14,4726,1076,2011,0,0]Ak[1361,14,1362,1362,125,0,0]Ac[491,7,492,0,0,0,0]Al[3938,13,3939,2436,2391,0,0]Ae[1046,2,1047,0,0,0,0]Ae[4786,7,4787,0,0,0,0]Al[3938,13,3939,3702,3300,0,0]Ae[4725,2,4726,0,0,0,0]Al[4725,13,4726,2437,2392,0,0]Ah[3938,2,3939,4726,0,0,0]Ai[3938,13,3939,0,4825,0,0]Ai[3938,14,3939,0,3306,0,0]Ai[3938,14,3939,0,4152,0,0]5Ai[4725,13,4726,0,4826,0,0]Al[4725,14,4726,1538,3307,0,0]Al[4725,14,4726,1838,4154,0,0]Ai[4725,14,4726,0,4396,0,0]Al[3938,13,3939,0,1599,3116,0]Al[3938,13,3939,0,1582,3116,0]Al[3938,13,3939,0,3120,3116,0]Al[3938,13,3939,0,4825,3116,0]Al[3938,13,3939,0,2414,3116,0]Ao[4725,13,4726,1952,1600,3116,0]Ao[4725,13,4726,1522,1583,3116,0]Ao[4725,13,4726,1512,3121,3116,0]Ao[4725,13,4726,1512,3313,3116,0]Al[4725,13,4726,1903,4498,0,0]Al[4725,13,4726,0,4826,3116,0]Ao[4725,13,4726,4726,2415,3116,0]Al[4725,12,1836,1836,3116,0,0]Al[4431,14,4432,4432,2412,0,0]Al[3938,13,3939,0,1599,3117,0]Al[3938,13,3939,0,1582,3117,0]Al[3938,13,3939,0,3120,3117,0]Al[3938,13,3939,0,4825,3117,0]Al[3938,13,3939,0,2414,3117,0]Ao[4725,13,4726,1952,1600,3117,0]Ao[4725,13,4726,1522,1583,3117,0]Ao[4725,13,4726,1512,3121,3117,0]Ao[4725,13,4726,1512,3313,3117,0]Ao[4725,13,4726,1512,2385,3117,0]Al[4725,13,4726,0,4826,3117,0]Ao[4725,13,4726,4726,2415,3117,0]Al[4725,12,1836,1836,3117,0,0]Al[3938,13,3939,4079,3331,0,0]Al[4725,13,4726,2144,3332,0,0]Al[1046,13,1537,1537,4002,0,0]Al[4725,13,4726,2437,2392,0,0]Ah[3938,14,1840,0,468,0,0]Ak[4725,14,1841,1841,469,0,0]Al[3938,13,3939,2463,2395,0,0]3Ai[3938,12,3939,0,3342,0,0]Al[3938,13,3939,0,4381,3342,0]Al[3938,13,3939,0,4701,3342,0]Al[4725,12,4726,1547,3343,0,0]Ao[4725,13,4726,1547,4382,3343,0]Ao[4725,13,4726,4856,4702,3343,0]Al[1046,14,1047,1047,3112,0,0]Ai[3938,14,3939,0,1320,0,0]Al[4725,14,4726,3156,1321,0,0]Ai[3938,13,3697,0,3302,0,0]Ak[3938,13,3697,0,111,3302,0]Al[3938,13,3697,0,1324,3302,0]Al[3938,13,3697,0,1984,3302,0]Al[4725,13,3698,3698,3303,0,0]Am[4725,13,3698,133,112,3303,0]Ao[4725,13,3698,1507,1325,3303,0]Ao[4725,13,3698,2485,1985,3303,0]Ah[3938,14,1840,0,468,0,0]Ak[4725,14,1841,1841,469,0,0]Ai[4725,14,4497,0,4705,0,0]Ai[3938,14,3939,0,4482,0,0]Ai[3938,14,3939,0,4670,0,0]Al[4725,14,4726,1512,4484,0,0]Al[4725,14,4726,1512,4672,0,0]Ai[4725,14,4726,0,4396,0,0]Ai[4725,14,1341,0,4360,0,0]Al[3938,13,3939,2436,2391,0,0]Al[3938,13,3939,2463,2395,0,0]Al[4725,13,4726,2437,2392,0,0]Ai[3938,14,1840,0,1979,0,0]Al[4725,14,4726,4726,3119,0,0]Al[4725,14,1841,1841,1980,0,0]Ae[3938,4,3939,0,0,0,0]Ah[3938,2,1840,1841,0,0,0]Ae[4725,4,4726,0,0,0,0]Ae[4725,2,1841,0,0,0,0]Ac[491,7,492,0,0,0,0]:Ae[1046,2,1047,0,0,0,0]Ae[4786,7,4787,0,0,0,0]Ao[3938,13,3939,3702,4767,1330,0]Ao[3938,13,3939,4074,4391,1330,0]Ao[3938,13,3939,4074,3140,1330,0]Ao[3938,13,3939,4074,3327,1330,0]Ao[3938,13,3939,4074,4390,1330,0]Ao[3938,13,3939,4079,3345,1330,0]Ao[3938,13,3939,4079,4158,1330,0]Al[3938,13,3939,0,4485,1330,0]Ao[4725,13,4726,3703,4486,1330,0]Ai[3938,14,3939,0,2423,0,0]Al[4725,14,4726,4726,2424,0,0]Al[4431,14,4432,4432,2425,0,0]Ak[3938,13,3939,1045,474,0,0]Ai[4725,14,3134,0,4145,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Al[4431,14,4432,4432,2412,0,0]Ai[3938,14,3939,0,4489,0,0]Al[4725,14,4726,1538,4490,0,0]10Ai[3938,12,1840,0,1975,0,0]Al[4725,12,1841,1841,1976,0,0]Ai[3938,12,3697,0,3302,0,0]Ak[3938,13,3697,0,111,3302,0]Al[3938,13,3697,0,1324,3302,0]Al[3938,13,3697,0,1984,3302,0]Al[4725,12,3698,3698,3303,0,0]Am[4725,13,3698,133,112,3303,0]Ao[4725,13,3698,1507,1325,3303,0]Ao[4725,13,3698,2485,1985,3303,0]Ai[3938,10,1840,1841,0,0,0]Af[4725,10,1841,0,0,0,0]Al[4725,15,4726,3165,4768,0,0]Al[4725,15,4726,1071,3141,0,0]Al[4725,15,4726,1076,3346,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]65Al[4725,15,4726,1903,4498,0,0]Ah[3938,5,1840,1841,0,0,0]Ae[4725,5,1841,0,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Ah[3938,5,3697,2485,0,0,0]Ah[4725,5,3698,2485,0,0,0]Al[1046,15,1047,1047,4860,0,0]Ah[3938,5,3939,3702,0,0,0]5454Ai[3938,10,3939,2136,0,0,0]Ai[3938,10,1840,1841,0,0,0]Af[4725,10,1841,0,0,0,0]Al[3938,15,3939,4079,3345,0,0]?Al[4725,15,4726,1076,3982,0,0]Al[4725,15,4726,2144,4000,0,0]Ah[4725,5,4726,2144,0,0,0]Al[3938,15,3939,4074,4391,0,0]Al[4725,15,4726,1512,3313,0,0]Ah[3938,5,3939,1076,0,0,0]Ah[4725,5,4726,1076,0,0,0]Al[4725,15,4726,1903,2398,0,0]Ai[3938,12,3939,0,4974,0,0]Al[4725,12,4726,4293,4975,0,0]Ao[4725,13,4726,4293,5101,4975,0]Ao[4725,13,4726,4293,5106,4975,0]Ak[1361,14,1362,1362,125,0,0]An[1361,13,1362,1362,125,4359,0]Ao[1046,13,1047,1047,3112,4359,0]An[1046,13,1830,1830,126,4359,0]An[1046,13,1925,1925,127,4359,0]An[3958,13,3959,3959,116,4359,0]Ak[3958,14,3959,3959,116,0,0]Al[3938,13,3939,0,1599,3297,0]Al[3938,13,3939,0,1582,3297,0]Al[3938,13,3939,0,3983,3297,0]Al[3938,13,3939,0,4119,3297,0]Ak[3938,13,3939,0,481,3297,0]Ak[3938,13,3939,0,117,3297,0]Al[3938,13,3939,0,2414,3297,0]Ao[4725,13,4726,1952,1600,3297,0]Ao[4725,13,4726,3165,4768,3297,0]Ao[4725,13,4726,1522,1583,3297,0]Ao[4725,13,4726,1071,3141,3297,0]Ao[4725,13,4726,1071,4280,3297,0]Ao[4725,13,4726,1076,3346,3297,0]Ao[4725,13,4726,3283,3984,3297,0]Ao[4725,13,4726,2437,4676,3297,0]Ao[4725,13,4726,1903,4766,3297,0]Ao[4725,13,4726,1930,4120,3297,0]An[4725,13,4726,4726,482,3297,0]An[4725,13,4726,4726,118,3297,0]Ao[4725,13,4726,4726,2415,3297,0]Al[3938,13,3939,2436,2391,0,0]Al[4725,13,4726,2437,2392,0,0]Ai[3938,12,1840,0,3130,0,0]Ai[3938,12,1840,0,3123,0,0]Al[4725,12,1841,1841,3131,0,0]Al[4725,12,1841,1841,3124,0,0]Ao[1361,13,1536,1536,4001,1970,0]Ao[1361,13,1536,1536,1357,1970,0]Al[491,13,492,492,3971,1970,0]Al[3938,13,3939,0,4244,1970,0]Ao[3938,13,3939,2436,4253,1970,0]Al[3938,13,3939,0,1599,1970,0]Al[3938,13,3939,0,1582,1970,0]Al[3938,13,3939,0,2426,1970,0]Al[3938,13,3939,0,3306,1970,0]Al[3938,13,3939,0,3137,1970,0]Al[3938,13,3939,0,4489,1970,0]Al[3938,13,3939,0,2010,1970,0]Al[3938,13,3939,0,4110,1970,0]Al[3938,13,3939,0,4506,1970,0]Al[3938,13,3939,0,4791,1970,0]Al[3938,13,3939,0,4806,1970,0]Al[3938,13,3939,0,3983,1970,0]Al[3938,13,3939,0,4367,1970,0]Al[3938,13,3939,0,4983,1970,0]Al[3938,13,3939,0,4701,1970,0]Al[3938,13,3697,0,3108,1970,0]Al[3938,13,3697,0,3978,1970,0]Al[3938,13,3697,0,4272,1970,0]Al[3938,13,3697,0,4759,1970,0]Ao[4725,13,4726,4516,4245,1970,0]Ao[4725,13,4726,4293,5101,1970,0]Ao[4725,13,4726,1952,1600,1970,0]Ao[4725,13,4726,1522,1583,1970,0]Ao[4725,13,4726,1522,3321,1970,0]Ao[4725,13,4726,1522,3296,1970,0]Ao[4725,13,4726,1538,2427,1970,0]Ao[4725,13,4726,1538,3307,1970,0]Ao[4725,13,4726,1538,3138,1970,0]Ao[4725,13,4726,1538,4490,1970,0]Ao[4725,13,4726,1076,2011,1970,0]Ao[4725,13,4726,2144,4392,1970,0]Ao[4725,13,4726,2144,4114,1970,0]Ao[4725,13,4726,3283,4507,1970,0]Ao[4725,13,4726,3283,4792,1970,0]Ao[4725,13,4726,3283,4807,1970,0]Ao[4725,13,4726,3283,3984,1970,0]Ao[4725,13,4726,1838,4368,1970,0]Ao[4725,13,4726,1838,4984,1970,0]Ao[4725,13,4726,4293,5106,1970,0]Ao[4725,13,4726,4856,4702,1970,0]An[4725,13,3698,133,3109,1970,0]Ao[4725,13,3698,1507,3979,1970,0]Ao[4725,13,3698,2485,4273,1970,0]Ao[4725,13,3698,3698,4760,1970,0]Ao[4431,13,4432,4432,2412,1970,0]Ao[1046,13,1537,1537,1358,1970,0]Ao[1046,13,1537,1537,4494,1970,0]Al[4725,13,4726,1930,4252,0,0]Al[4725,13,4726,1903,4275,0,0]Al[4725,13,4726,1903,4498,0,0]Al[4725,14,4726,1903,5013,0,0]Ai[4725,14,4765,0,4827,0,0]Ah[4725,14,4497,0,470,0,0]Ai[4725,14,4497,0,1352,0,0]Ai[4725,14,4497,0,1354,0,0]Ai[4725,14,4497,0,4705,0,0]Ai[3938,12,1840,0,3130,0,0]Ai[3938,12,1840,0,3123,0,0]Al[4725,12,1841,1841,3131,0,0]Al[4725,12,1841,1841,3124,0,0]Ae[4725,2,4726,0,0,0,0]Al[3938,13,3939,4074,3325,0,0]Al[3938,13,3939,4074,4868,0,0]Ai[3938,13,3697,0,3302,0,0]Ak[3938,13,3697,0,111,3302,0]Al[3938,13,3697,0,1324,3302,0]Al[3938,13,3697,0,1984,3302,0]Al[4725,13,3698,3698,3303,0,0]Am[4725,13,3698,133,112,3303,0]Ao[4725,13,3698,1507,1325,3303,0]Ao[4725,13,3698,2485,1985,3303,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]9854Al[3938,13,3939,3702,3300,0,0]Al[3938,13,3939,1045,2406,0,0]Al[4725,14,4726,1903,5013,0,0]Ai[4725,14,4765,0,4827,0,0]Ah[4725,14,4497,0,470,0,0]Ai[4725,14,4497,0,1352,0,0]Ai[4725,14,4497,0,4705,0,0]Al[3938,13,3939,4079,3331,0,0]Al[4725,13,4726,2144,3332,0,0]Ak[1361,14,1362,1362,125,0,0]Al[1361,14,1536,1536,1357,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,12,1840,0,3130,0,0]Al[4725,12,1841,1841,3131,0,0]Am[1361,13,1362,1362,125,109,0]An[1361,13,1362,1362,3970,109,0]An[1361,13,1536,1536,4001,109,0]An[1361,13,1536,1536,1357,109,0]An[1361,13,1536,1536,4393,109,0]Ak[491,13,492,492,3971,109,0]An[4007,13,4008,4008,5014,109,0]An[3938,13,3939,3702,4511,109,0]An[3938,13,3939,3702,2405,109,0]Ak[3938,13,3939,0,4244,109,0]An[3938,13,3939,1045,2406,109,0]An[3938,13,3939,1045,3111,109,0]An[3938,13,3939,1045,4668,109,0]An[3938,13,3939,3702,3298,109,0]An[3938,13,3939,3702,3300,109,0]An[3938,13,3939,3702,1987,109,0]An[3938,13,3939,3702,1575,109,0]An[3938,13,3939,3702,3101,109,0]An[3938,13,3939,3702,2379,109,0]An[3938,13,3939,3702,4144,109,0]An[3938,13,3939,3702,4149,109,0]An[3938,13,3939,3702,4929,109,0]An[3938,13,3939,3702,4767,109,0]An[3938,13,3939,2436,2391,109,0]An[3938,13,3939,2436,4253,109,0]An[3938,13,3939,2436,1554,109,0]An[3938,13,3939,2463,2395,109,0]An[3938,13,3939,2463,3328,109,0]An[3938,13,3939,2463,4372,109,0]Am[3938,13,3939,1045,474,109,0]An[3938,13,3939,4074,3325,109,0]An[3938,13,3939,4074,4868,109,0]An[3938,13,3939,4074,4391,109,0]An[3938,13,3939,4074,3140,109,0]An[3938,13,3939,4074,3327,109,0]An[3938,13,3939,4074,4390,109,0]An[3938,13,3939,1548,4159,109,0]An[3938,13,3939,1088,3996,109,0]An[3938,13,3939,4079,3331,109,0]An[3938,13,3939,4079,3345,109,0]An[3938,13,3939,4079,4158,109,0]Ak[3938,13,3939,0,3322,109,0]Ak[3938,13,3939,0,4284,109,0]Ak[3938,13,3939,0,4503,109,0]Ak[3938,13,3939,0,4136,109,0]Ak[3938,13,3939,0,4381,109,0]Ak[3938,13,3939,0,1599,109,0]Ak[3938,13,3939,0,4926,109,0]Ak[3938,13,3939,0,1320,109,0]Ak[3938,13,3939,0,1582,109,0]Ak[3938,13,3939,0,2426,109,0]Ak[3938,13,3939,0,3306,109,0]Ak[3938,13,3939,0,3137,109,0]Ak[3938,13,3939,0,4489,109,0]Ak[3938,13,3939,0,2010,109,0]Ak[3938,13,3939,0,4110,109,0]Ak[3938,13,3939,0,4485,109,0]Ak[3938,13,3939,0,4499,109,0]Ak[3938,13,3939,0,4506,109,0]Ak[3938,13,3939,0,4791,109,0]Ak[3938,13,3939,0,4806,109,0]Ak[3938,13,3939,0,3983,109,0]Ak[3938,13,3939,0,4367,109,0]Ak[3938,13,3939,0,4152,109,0]Ak[3938,13,3939,0,4983,109,0]Ak[3938,13,3939,0,4104,109,0]Ak[3938,13,3939,0,3973,109,0]Ak[3938,13,3939,0,3120,109,0]Ak[3938,13,3939,0,4482,109,0]Ak[3938,13,3939,0,4670,109,0]Ak[3938,13,3939,0,4116,109,0]Ak[3938,13,3939,0,4119,109,0]Ak[3938,13,3939,0,4124,109,0]Ak[3938,13,3939,0,4825,109,0]Ak[3938,13,3939,0,3309,109,0]Ak[3938,13,3939,0,4701,109,0]Aj[3938,13,3939,0,481,109,0]Aj[3938,13,3939,0,117,109,0]Ak[3938,13,3939,0,2414,109,0]Ak[3938,13,3939,0,2423,109,0]Aj[3938,13,3697,0,111,109,0]Ak[3938,13,3697,0,3108,109,0]Ak[3938,13,3697,0,1324,109,0]Ak[3938,13,3697,0,3978,109,0]Ak[3938,13,3697,0,1984,109,0]Ak[3938,13,3697,0,4272,109,0]Ak[3938,13,3697,0,4759,109,0]Ak[3938,13,1840,0,1979,109,0]Aj[3938,13,1840,0,468,109,0]Ak[3938,13,1840,0,5072,109,0]Ak[3938,13,1840,0,3976,109,0]Ak[3938,13,1840,0,4829,109,0]Ak[3938,13,1919,0,3340,109,0]Ak[3938,13,1919,0,4255,109,0]Ak[3938,13,1919,0,4697,109,0]Ak[3938,13,1919,0,4699,109,0]Ak[3938,13,1919,0,4922,109,0]Ak[3938,13,1919,0,1337,109,0]An[4725,13,4726,4516,4245,109,0]An[4725,13,4726,3165,3115,109,0]An[4725,13,4726,1071,1562,109,0]An[4725,13,4726,2144,2004,109,0]An[4725,13,4726,2144,3326,109,0]An[4725,13,4726,2144,3332,109,0]An[4725,13,4726,3703,3301,109,0]An[4725,13,4726,1067,3323,109,0]An[4725,13,4726,1838,4285,109,0]An[4725,13,4726,2437,2392,109,0]An[4725,13,4726,2437,4251,109,0]An[4725,13,4726,1903,5131,109,0]An[4725,13,4726,4293,5101,109,0]An[4725,13,4726,1547,4504,109,0]An[4725,13,4726,1547,4137,109,0]An[4725,13,4726,1547,4382,109,0]An[4725,13,4726,1952,1600,109,0]An[4725,13,4726,1952,4927,109,0]An[4725,13,4726,3156,1321,109,0]An[4725,13,4726,3165,4768,109,0]An[4725,13,4726,1522,1583,109,0]An[4725,13,4726,1522,4148,109,0]An[4725,13,4726,1522,3321,109,0]An[4725,13,4726,1522,3136,109,0]An[4725,13,4726,1522,4822,109,0]An[4725,13,4726,1522,3296,109,0]An[4725,13,4726,1538,2427,109,0]An[4725,13,4726,1538,3307,109,0]An[4725,13,4726,1538,3138,109,0]An[4725,13,4726,1538,4490,109,0]An[4725,13,4726,1071,3141,109,0]An[4725,13,4726,1071,4280,109,0]An[4725,13,4726,1076,2011,109,0]An[4725,13,4726,1076,3346,109,0]An[4725,13,4726,1076,3982,109,0]An[4725,13,4726,2144,4000,109,0]An[4725,13,4726,2144,4392,109,0]An[4725,13,4726,2144,4114,109,0]An[4725,13,4726,3703,4486,109,0]An[4725,13,4726,3703,3133,109,0]An[4725,13,4726,3703,4358,109,0]An[4725,13,4726,3703,5010,109,0]An[4725,13,4726,3703,1342,109,0]An[4725,13,4726,1067,4500,109,0]An[4725,13,4726,3283,4507,109,0]An[4725,13,4726,3283,4792,109,0]An[4725,13,4726,3283,4807,109,0]An[4725,13,4726,3283,3984,109,0]An[4725,13,4726,1838,4368,109,0]An[4725,13,4726,1838,4154,109,0]An[4725,13,4726,1838,4984,109,0]An[4725,13,4726,1838,4106,109,0]An[4725,13,4726,1838,3974,109,0]An[4725,13,4726,1512,3121,109,0]An[4725,13,4726,1512,4373,109,0]An[4725,13,4726,1512,3313,109,0]An[4725,13,4726,1512,3335,109,0]An[4725,13,4726,1512,4121,109,0]An[4725,13,4726,1512,4484,109,0]An[4725,13,4726,1512,4672,109,0]An[4725,13,4726,1512,2385,109,0]An[4725,13,4726,2437,4676,109,0]An[4725,13,4726,2437,2396,109,0]An[4725,13,4726,2437,4117,109,0]An[4725,13,4726,2437,3968,109,0]An[4725,13,4726,2437,3329,109,0]An[4725,13,4726,1903,4275,109,0]An[4725,13,4726,1903,4498,109,0]An[4725,13,4726,1903,5013,109,0]An[4725,13,4726,1903,2398,109,0]An[4725,13,4726,1903,4766,109,0]An[4725,13,4726,1930,4488,109,0]An[4725,13,4726,1930,4252,109,0]An[4725,13,4726,1930,4120,109,0]An[4725,13,4726,1930,4125,109,0]Ak[4725,13,4726,0,4396,109,0]Ak[4725,13,4726,0,4826,109,0]An[4725,13,4726,4293,3310,109,0]An[4725,13,4726,4293,5106,109,0]An[4725,13,4726,4856,4702,109,0]Am[4725,13,4726,4726,482,109,0]Am[4725,13,4726,4726,118,109,0]An[4725,13,4726,4726,2415,109,0]An[4725,13,4726,4726,2424,109,0]An[4725,13,4726,4726,3119,109,0]An[4725,13,1836,1836,4286,109,0]An[4725,13,4968,4968,4867,109,0]An[4725,13,4968,4968,1339,109,0]An[4725,13,1841,1841,1980,109,0]Am[4725,13,1841,1841,469,109,0]An[4725,13,1841,1841,5073,109,0]An[4725,13,1841,1841,3977,109,0]An[4725,13,1841,1841,4832,109,0]An[4725,13,1920,1920,3341,109,0]An[4725,13,1920,1920,4256,109,0]An[4725,13,1920,1920,4698,109,0]An[4725,13,1920,1920,4700,109,0]An[4725,13,1920,1920,4923,109,0]Ak[4725,13,1920,0,1338,109,0]Al[4725,13,3698,133,112,109,0]Am[4725,13,3698,133,3109,109,0]An[4725,13,3698,1507,1325,109,0]An[4725,13,3698,1507,3979,109,0]An[4725,13,3698,2485,1985,109,0]An[4725,13,3698,2485,4273,109,0]An[4725,13,3698,3698,4760,109,0]An[4725,13,2115,2115,1340,109,0]An[4725,13,2115,2115,1343,109,0]An[4431,13,4432,4432,3344,109,0]Am[4431,13,4432,389,4397,109,0]An[4431,13,4432,4432,2412,109,0]An[4431,13,4432,4432,2425,109,0]An[4431,13,4432,4432,4126,109,0]An[1046,13,1047,1047,3112,109,0]An[1046,13,1047,1047,4860,109,0]Am[1046,13,1830,1830,126,109,0]An[1046,13,1537,1537,1358,109,0]An[1046,13,1537,1537,4002,109,0]An[1046,13,1537,1537,4494,109,0]An[1046,13,4211,4211,5058,109,0]An[1046,13,4211,4211,5059,109,0]Am[1046,13,1925,1925,127,109,0]Am[3958,13,3959,3959,116,109,0]An[3958,13,3959,3959,2382,109,0]Al[3938,13,3939,3702,4929,0,0]Ai[3938,13,3939,0,4104,0,0]Al[4725,13,4726,1838,4106,0,0]Al[4725,14,4726,1903,5013,0,0]Ai[4725,14,4765,0,4827,0,0]Ah[4725,14,4497,0,470,0,0]Ai[4725,14,4497,0,1352,0,0]Ah[3938,5,3939,3702,0,0,0]Al[1361,15,1362,1362,3970,0,0]Ae[3958,6,3959,0,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Ah[4725,5,4726,1512,0,0,0]Al[3938,15,3939,3702,4767,0,0]Al[4725,15,4726,3165,4768,0,0]Ah[3938,5,3939,2436,0,0,0]Ah[4725,5,4726,2437,0,0,0]Al[4725,15,4726,2144,4000,0,0]Ah[3938,5,3939,2463,0,0,0]2Ah[4725,6,4726,1903,0,0,0]Ai[3938,19,3939,0,1599,0,0]Al[4725,19,4726,1952,1600,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]=Ai[3938,12,1840,0,4153,0,0]>Al[3938,13,1919,0,4699,4153,0]>Al[4725,12,1841,1841,4155,0,0]>Ao[4725,13,1920,1920,4700,4155,0]Ao[1046,13,4211,4211,5059,4155,0]An[3958,13,3959,3959,116,3985,0]Ao[3958,13,3959,3959,2382,3985,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,14,4726,1538,3138,0,0]Ai[3938,13,3939,0,4152,0,0]Al[4725,13,4726,1838,4154,0,0]Al[3938,13,3939,2136,4355,0,0]0>=Al[1046,13,1537,1537,4494,0,0]Ae[1046,2,1047,0,0,0,0]Al[3938,13,3939,2463,2395,0,0]Al[4725,13,4726,2437,2396,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]43Ai[3938,14,3939,0,2423,0,0]Al[4725,14,4726,4726,2424,0,0]Al[4431,14,4432,4432,2425,0,0]Al[4725,13,4726,2144,2004,0,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,14,4726,1538,3138,0,0]Al[4725,13,4726,4293,5101,0,0]Al[4725,13,4726,4293,5106,0,0]Al[3938,13,3939,4079,3331,0,0]Al[4725,13,4726,2144,3332,0,0]>=Al[1361,14,1536,1536,1357,0,0]Ai[491,14,492,492,3971,0,0]Ai[3938,13,1919,0,4697,0,0]Ai[3938,13,1919,0,4699,0,0]Al[4725,13,1920,1920,4698,0,0]Al[4725,13,1920,1920,4700,0,0]76Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]Ah[3938,5,3939,4516,0,0,0]Ah[4725,5,4726,4516,0,0,0]Ai[3938,15,3939,0,4485,0,0]Ai[3938,15,3939,0,4116,0,0]Al[4725,15,4726,3703,4486,0,0]Al[4725,15,4726,2437,4117,0,0]Ah[4725,6,4726,2437,0,0,0]Ah[4725,5,4726,1930,0,0,0]Ah[3938,5,3939,2436,0,0,0]Al[3938,15,3939,4074,4390,0,0]Ah[3938,5,1919,1920,0,0,0]Ae[4725,5,1920,0,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Al[4725,15,4726,1903,4766,0,0]>Al[4725,15,4726,1522,3136,0,0]=5454Al[4725,15,4726,2437,4251,0,0]Ah[3938,5,3697,2485,0,0,0]Ah[4725,5,3698,2485,0,0,0]Ah[4725,5,4726,1903,0,0,0]98Ah[4725,6,4726,1071,0,0,0]:9Ah[3938,5,3939,1838,0,0,0]Ah[4725,5,4726,1838,0,0,0]Ae[4725,5,1836,0,0,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Al[1046,14,1047,1047,3112,0,0]Ae[4725,2,4726,0,0,0,0]Ai[3938,13,3939,0,4367,0,0]Al[4725,13,4726,1838,4368,0,0]Al[3938,12,3939,2136,4355,0,0]Ab[4296,3,0,0,0,0,0]Ak[1361,14,1362,1362,125,0,0]Al[1361,14,1536,1536,4001,0,0]98Al[3938,13,3939,2463,3328,0,0]Al[4725,13,4726,2437,3329,0,0]Ai[3938,13,3939,0,4791,0,0]Al[4725,13,4726,3283,4792,0,0];=Al[3938,13,3939,4074,4868,0,0]Al[4725,13,4726,2144,3326,0,0]Al[3938,13,3939,3702,3298,0,0]Al[3938,13,3939,3702,3300,0,0]Al[3938,13,3939,2463,2395,0,0]Ak[3938,13,3939,1045,474,0,0]Al[4725,13,4726,2437,2396,0,0]<;Ai[3938,13,3939,0,1599,0,0]Ai[3938,13,3939,0,1582,0,0]Al[4725,13,4726,1952,1600,0,0]Al[4725,13,4726,1522,1583,0,0]20Ae[1361,7,1536,0,0,0,0]?>Al[1361,14,1536,1536,1357,0,0]Ao[1046,13,1047,1047,4860,3145,0]Ai[3938,15,3939,0,3120,0,0]Ah[3938,5,3939,1512,0,0,0]Al[4725,15,4726,1512,3121,0,0]Ah[4725,5,4726,1512,0,0,0]Ah[3938,6,3939,3703,0,0,0]Ah[4725,6,4726,3703,0,0,0]Al[4725,15,4726,2437,4251,0,0]Ah[4725,5,4726,1930,0,0,0]Ah[3938,5,3939,1538,0,0,0]Ah[4725,5,4726,1538,0,0,0]97Ae[1046,5,1537,0,0,0,0]:8Ah[4725,6,4726,1903,0,0,0]Ah[3938,5,3939,1067,0,0,0]Ah[4725,5,4726,1067,0,0,0]Ah[3938,5,3939,1547,0,0,0]Ah[4725,5,4726,1547,0,0,0]Ah[3938,5,3939,3283,0,0,0]Ah[4725,5,4726,3283,0,0,0]Ai[3938,15,3939,0,4485,0,0]Al[4725,15,4726,3703,4486,0,0]Ah[3938,5,3939,3702,0,0,0]Al[3938,15,3939,4074,3140,0,0]Al[3938,15,3939,4074,3327,0,0]Al[3938,15,3939,4079,3345,0,0]Al[3938,15,3939,4079,4158,0,0]An[1361,13,1362,1362,125,4250,0]Ao[1361,13,1362,1362,3970,4250,0]Ao[3938,13,3939,3702,3300,4250,0]Ao[3938,13,3939,3702,4929,4250,0]Ao[3938,13,3939,3702,4767,4250,0]Ao[3938,13,3939,2436,2391,4250,0]Ao[3938,13,3939,2436,4253,4250,0]Ao[3938,13,3939,4074,4391,4250,0]Ao[3938,13,3939,4074,3140,4250,0]Ao[3938,13,3939,4074,3327,4250,0]Ao[3938,13,3939,4074,4390,4250,0]Ao[3938,13,3939,4079,3345,4250,0]Ao[3938,13,3939,4079,4158,4250,0]Al[3938,13,3939,0,1599,4250,0]Al[3938,13,3939,0,4926,4250,0]Al[3938,13,3939,0,1582,4250,0]Al[3938,13,3939,0,2426,4250,0]Al[3938,13,3939,0,3306,4250,0]Al[3938,13,3939,0,3137,4250,0]Al[3938,13,3939,0,4489,4250,0]Al[3938,13,3939,0,2010,4250,0]Al[3938,13,3939,0,4110,4250,0]Al[3938,13,3939,0,4485,4250,0]Al[3938,13,3939,0,4506,4250,0]Al[3938,13,3939,0,4791,4250,0]Al[3938,13,3939,0,4806,4250,0]Al[3938,13,3939,0,3983,4250,0]Al[3938,13,3939,0,4367,4250,0]Al[3938,13,3939,0,4152,4250,0]Al[3938,13,3939,0,4983,4250,0]Al[3938,13,3939,0,4104,4250,0]Al[3938,13,3939,0,3973,4250,0]Al[3938,13,3939,0,3120,4250,0]Al[3938,13,3939,0,4482,4250,0]Al[3938,13,3939,0,4670,4250,0]Al[3938,13,3939,0,4116,4250,0]Al[3938,13,3939,0,4119,4250,0]Al[3938,13,3939,0,4124,4250,0]Al[3938,13,3939,0,4825,4250,0]Al[3938,13,3939,0,3309,4250,0]Ak[3938,13,3939,0,481,4250,0]Ak[3938,13,3939,0,117,4250,0]Al[3938,13,3939,0,2414,4250,0]Ak[3938,13,3697,0,111,4250,0]Al[3938,13,3697,0,3108,4250,0]Al[3938,13,3697,0,1324,4250,0]Al[3938,13,3697,0,3978,4250,0]Al[3938,13,3697,0,1984,4250,0]Al[3938,13,3697,0,4272,4250,0]Al[3938,13,1919,0,4922,4250,0]Al[3938,13,1919,0,1337,4250,0]Ao[4725,13,4726,1952,1600,4250,0]Ao[4725,13,4726,1952,4927,4250,0]Ao[4725,13,4726,3165,4768,4250,0]Ao[4725,13,4726,1522,1583,4250,0]Ao[4725,13,4726,1522,4148,4250,0]Ao[4725,13,4726,1522,3321,4250,0]Ao[4725,13,4726,1522,3136,4250,0]Ao[4725,13,4726,1522,4822,4250,0]Ao[4725,13,4726,1522,3296,4250,0]Ao[4725,13,4726,1538,2427,4250,0]Ao[4725,13,4726,1538,3307,4250,0]Ao[4725,13,4726,1538,3138,4250,0]Ao[4725,13,4726,1538,4490,4250,0]Ao[4725,13,4726,1071,3141,4250,0]Ao[4725,13,4726,1071,4280,4250,0]Ao[4725,13,4726,1076,2011,4250,0]Ao[4725,13,4726,1076,3346,4250,0]Ao[4725,13,4726,1076,3982,4250,0]Ao[4725,13,4726,2144,4392,4250,0]Ao[4725,13,4726,2144,4114,4250,0]Ao[4725,13,4726,3703,4486,4250,0]Ao[4725,13,4726,3703,3133,4250,0]Ao[4725,13,4726,3703,4358,4250,0]Ao[4725,13,4726,3703,5010,4250,0]Ao[4725,13,4726,3283,4507,4250,0]Ao[4725,13,4726,3283,4792,4250,0]Ao[4725,13,4726,3283,4807,4250,0]Ao[4725,13,4726,3283,3984,4250,0]Ao[4725,13,4726,1838,4368,4250,0]Ao[4725,13,4726,1838,4154,4250,0]Ao[4725,13,4726,1838,4984,4250,0]Ao[4725,13,4726,1838,4106,4250,0]Ao[4725,13,4726,1838,3974,4250,0]Ao[4725,13,4726,1512,3121,4250,0]Ao[4725,13,4726,1512,3313,4250,0]Ao[4725,13,4726,1512,3335,4250,0]Ao[4725,13,4726,1512,4121,4250,0]Ao[4725,13,4726,1512,4484,4250,0]Ao[4725,13,4726,1512,4672,4250,0]Ao[4725,13,4726,1512,2385,4250,0]Ao[4725,13,4726,2437,4676,4250,0]Ao[4725,13,4726,2437,4117,4250,0]Ao[4725,13,4726,1903,4498,4250,0]Ao[4725,13,4726,1903,5013,4250,0]Ao[4725,13,4726,1903,2398,4250,0]Ao[4725,13,4726,1903,4766,4250,0]Ao[4725,13,4726,1930,4120,4250,0]Ao[4725,13,4726,1930,4125,4250,0]Al[4725,13,4726,0,4396,4250,0]Al[4725,13,4726,0,4826,4250,0]Ao[4725,13,4726,4293,3310,4250,0]Ao[4725,13,4726,4293,5106,4250,0]An[4725,13,4726,4726,482,4250,0]An[4725,13,4726,4726,118,4250,0]Ao[4725,13,4726,4726,2415,4250,0]Ao[4725,13,4726,4726,3119,4250,0]Ao[4725,13,1836,1836,4286,4250,0]Ao[4725,13,4968,4968,4867,4250,0]Ao[4725,13,4968,4968,1339,4250,0]Ao[4725,13,1920,1920,4923,4250,0]Al[4725,13,1920,0,1338,4250,0]Am[4725,13,3698,133,112,4250,0]An[4725,13,3698,133,3109,4250,0]Ao[4725,13,3698,1507,1325,4250,0]Ao[4725,13,3698,1507,3979,4250,0]Ao[4725,13,3698,2485,1985,4250,0]Ao[4725,13,3698,2485,4273,4250,0]Ao[4725,13,2115,2115,1343,4250,0]Ao[4431,13,4432,4432,2412,4250,0]Ao[4431,13,4432,4432,2425,4250,0]Ao[4431,13,4432,4432,4126,4250,0]Ao[1046,13,1047,1047,4860,4250,0]Ao[1046,13,1537,1537,4494,4250,0]Ah[3938,7,3939,2136,0,0,0]Ai[3938,13,3939,0,4367,0,0]Al[4725,13,4726,1838,4368,0,0]Ai[3938,12,1840,0,3130,0,0]Al[4725,12,1841,1841,3131,0,0]Al[4431,14,4432,4432,2412,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]10Ae[1046,7,1047,0,0,0,0]Al[3938,13,3939,2436,2391,0,0]Ae[4296,7,4297,0,0,0,0]Ae[1361,7,1362,0,0,0,0]Al[4725,13,4726,3703,3301,0,0]Al[3938,13,3939,3702,3300,0,0]1Al[4725,13,4726,2144,4392,0,0]Al[1361,13,1536,1536,1357,0,0]Al[1361,14,1536,1536,4001,0,0]Al[3938,12,3939,2136,1996,0,0]Ao[3938,13,3939,1548,4159,1996,0]Ao[3938,13,3939,1088,3996,1996,0]Ai[3938,12,1840,0,1997,0,0]Al[3938,13,1919,0,3340,1997,0]Al[4725,12,1841,1841,1998,0,0]Ao[4725,13,1920,1920,3341,1998,0]Ah[3938,5,3939,1045,0,0,0]Ai[3938,15,3939,0,3120,0,0]Ah[3938,5,3939,1512,0,0,0]Al[4725,15,4726,1512,3121,0,0]Ah[4725,5,4726,1512,0,0,0]Ai[3938,15,3939,0,4485,0,0]Al[4725,15,4726,3703,4486,0,0]Ah[4725,5,4726,2437,0,0,0]Al[4725,15,4726,3703,1342,0,0]Ai[3938,15,3939,0,4116,0,0]Al[4725,15,4726,2437,4117,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]32Al[4725,15,4726,3703,3133,0,0]><><><21Ah[3938,5,1919,1920,0,0,0]Ae[4725,5,1920,0,0,0,0]10Ah[3938,5,3939,4856,0,0,0]Ah[4725,5,4726,4856,0,0,0]Al[4725,15,2115,2115,1340,0,0]5Al[4725,15,4726,1903,4498,0,0]Ai[3938,12,3939,0,2419,0,0]Al[3938,13,3939,0,4244,2419,0]Al[3938,13,3939,0,4136,2419,0]Al[4725,12,4726,1547,2420,0,0]Ao[4725,13,4726,4516,4245,2420,0]Ao[4725,13,4726,1547,4137,2420,0]Ae[4007,7,4008,0,0,0,0]Al[3938,13,3939,3702,3300,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]32An[1361,13,1362,1362,125,4369,0]Ao[1046,13,1047,1047,3112,4369,0]An[1046,13,1830,1830,126,4369,0]An[1046,13,1925,1925,127,4369,0]An[3958,13,3959,3959,116,4369,0]Ao[3958,13,3959,3959,2382,4369,0]Ab[4725,3,0,0,0,0,0]Ak[4431,13,4432,389,4397,0,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0];:Al[3938,13,3939,2436,2391,0,0]Al[4725,13,4726,2437,2392,0,0]?>32Ae[4296,7,4297,0,0,0,0]5Al[1046,14,1047,1047,3112,0,0]60Ae[1361,7,1362,0,0,0,0]Ai[3938,13,3939,0,4152,0,0]Ai[3938,13,3939,0,4104,0,0]Al[4725,13,4726,1838,4154,0,0]Al[4725,13,4726,1838,4106,0,0]Ai[3938,14,3939,0,4152,0,0]Al[4725,14,4726,1838,4154,0,0]53Al[3938,13,3939,4079,3331,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Ai[3938,15,3939,0,4485,0,0]Al[4725,15,4726,3703,4486,0,0]Ai[3938,10,3697,3698,0,0,0]Af[4725,10,3698,0,0,0,0]Ah[3938,5,3697,3698,0,0,0]Ae[4725,5,3698,0,0,0,0]Ai[3938,10,1840,1841,0,0,0]Af[4725,10,1841,0,0,0,0]Ai[3938,15,3939,0,4116,0,0]Al[4725,15,4726,2437,4117,0,0]Ah[4725,6,4726,1903,0,0,0]Ah[3938,6,3939,3702,0,0,0]Ah[4725,6,4726,3165,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]10Al[3938,15,3939,3702,4767,0,0]Al[4725,15,4726,3165,4768,0,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,14,4726,1538,3138,0,0]Ai[3938,14,3697,0,4759,0,0]Al[4725,14,3698,3698,4760,0,0]Al[3938,13,3939,3702,3300,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]10Al[4725,13,4726,3165,3115,0,0]Al[3938,13,3939,2436,2391,0,0]Al[4725,13,4726,2437,2392,0,0]Ab[4786,3,0,0,0,0,0]Ao[1046,13,1047,1047,4860,3145,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Ah[3938,5,3939,3283,0,0,0]Ah[4725,5,4726,3283,0,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]54Ai[4725,10,4726,1903,0,0,0]Al[4725,15,4726,3703,1342,0,0]7600Al[4725,15,4726,1071,4280,0,0]4365Al[4725,15,2115,2115,1340,0,0]Ae[1046,9,1047,0,0,0,0]:9Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]<;<;Ah[4725,6,4726,1522,0,0,0]21Ae[3938,5,3939,0,0,0,0]Ae[4725,5,4726,0,0,0,0]Ai[3938,15,3939,0,4116,0,0]Ah[3938,5,1840,1841,0,0,0]Al[4725,15,4726,2437,4117,0,0]Al[4725,15,4726,1903,4766,0,0]Ae[4725,5,1841,0,0,0,0]An[1361,13,1362,1362,125,4359,0]Ao[1046,13,1047,1047,3112,4359,0]An[1046,13,1830,1830,126,4359,0]An[1046,13,1925,1925,127,4359,0]An[3958,13,3959,3959,116,4359,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]Ai[3938,13,3939,0,1599,0,0]Al[4725,13,4726,1952,1600,0,0]Al[3938,14,3939,2436,4253,0,0]Ai[3938,13,3939,0,4367,0,0]Al[4725,13,4726,1838,4368,0,0]Ai[3938,13,3939,0,4244,0,0]Al[4725,13,4726,4516,4245,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]6Al[4725,13,4726,3703,3301,0,0]Al[3938,13,3939,3702,3300,0,0]0176Ao[4007,13,4008,4008,5014,4820,0]Ai[3938,13,3939,0,4499,0,0]Al[4725,13,4726,1067,4500,0,0]Ae[1046,6,1047,0,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]10Al[4725,15,4726,3703,3133,0,0]Ae[4725,5,4968,0,0,0,0]Ah[3938,5,3939,4074,0,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]Al[4725,15,2115,2115,1340,0,0]Al[3938,14,3939,2436,4253,0,0]Ak[3938,13,3939,0,481,3102,0]Ak[3938,13,3939,0,117,3102,0]Al[3938,13,3939,0,2414,3102,0]An[4725,13,4726,4726,482,3102,0]An[4725,13,4726,4726,118,3102,0]Ao[4725,13,4726,4726,2415,3102,0]Ao[3958,13,3959,3959,2382,3336,0]Ai[3938,13,3939,0,4806,0,0]Al[4725,13,4726,3283,4807,0,0]Ai[3938,12,1840,0,1997,0,0]Al[3938,13,1919,0,3340,1997,0]Al[4725,12,1841,1841,1998,0,0]Ao[4725,13,1920,1920,3341,1998,0]Al[3938,13,3939,3702,3300,0,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]Ai[3938,14,3939,0,3137,0,0]Al[4725,14,4726,1538,3138,0,0]Ai[3938,12,3697,0,3302,0,0]Ak[3938,13,3697,0,111,3302,0]Al[3938,13,3697,0,1324,3302,0]Al[3938,13,3697,0,1984,3302,0]Al[4725,12,3698,3698,3303,0,0]Am[4725,13,3698,133,112,3303,0]Ao[4725,13,3698,1507,1325,3303,0]Ao[4725,13,3698,2485,1985,3303,0]Al[3938,14,3939,2436,4253,0,0]Al[4725,13,4726,2144,2004,0,0]Ah[4725,2,4291,4078,0,0,0]Ae[1361,7,1362,0,0,0,0]==Ak[3938,13,3939,1045,474,0,0]Ak[4431,13,4432,389,4397,0,0]032Ai[3938,12,1840,0,1997,0,0]Al[3938,13,1919,0,3340,1997,0]Ae[4725,2,4726,0,0,0,0]Al[4725,12,1841,1841,1998,0,0]Ao[4725,13,1920,1920,3341,1998,0]Ai[3938,10,3939,4293,0,0,0]Ai[4725,10,4726,4293,0,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]Ai[3938,15,3939,0,4116,0,0]Al[4725,15,4726,2437,4117,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Ah[3938,5,3939,1838,0,0,0]Ah[4725,5,4726,1838,0,0,0]Ai[3938,12,1840,0,3130,0,0]Al[4725,12,1841,1841,3131,0,0]Al[4725,13,4726,2437,2392,0,0]Al[4725,13,4726,4726,3119,0,0]Al[4725,13,4726,3703,3301,0,0]Ai[3938,13,3697,0,4272,0,0]Al[4725,13,3698,2485,4273,0,0]Al[3938,13,3939,2136,3143,0,0]0Al[3938,13,3939,3702,3300,0,0]4Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]Al[1046,14,1047,1047,3112,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Al[4725,15,2115,2115,1340,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]10Al[4725,15,4726,1522,4822,0,0]Al[4725,15,4726,3703,3133,0,0]Ah[4725,5,4726,3703,0,0,0]Ai[3938,15,3939,0,3120,0,0]Al[4725,15,4726,1512,3121,0,0]Ah[4725,5,4726,1903,0,0,0]Ae[4007,5,4008,0,0,0,0]Al[1046,13,1047,1047,3112,0,0]Al[3938,13,3939,3702,3300,0,0]Ai[3938,12,1840,0,1997,0,0]Al[3938,13,1919,0,3340,1997,0]Al[4725,12,1841,1841,1998,0,0]Ao[4725,13,1920,1920,3341,1998,0]Ah[4725,7,4902,4901,0,0,0]0Al[4725,13,4726,3703,3301,0,0]6Ak[3938,13,3939,1045,474,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]Ao[4007,13,4008,4008,5014,4820,0]0Ai[3938,13,3939,0,4104,0,0]Al[4725,13,4726,1838,4106,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]10Ai[3938,13,3939,0,4791,0,0]Al[4725,13,4726,3283,4792,0,0]Am[1361,13,1362,1362,125,456,0]An[1046,13,1047,1047,3112,456,0]Am[1046,13,1830,1830,126,456,0]Am[1046,13,1925,1925,127,456,0]Am[3958,13,3959,3959,116,456,0]??>An[1361,13,1362,1362,125,4369,0]Ao[1046,13,1047,1047,3112,4369,0]An[1046,13,1830,1830,126,4369,0]An[1046,13,1925,1925,127,4369,0]An[3958,13,3959,3959,116,4369,0]Ao[3958,13,3959,3959,2382,4369,0]Ai[3938,13,3939,0,4152,0,0]Al[4725,13,4726,1838,4154,0,0]Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]Ae[1046,5,4211,0,0,0,0]0Ai[3938,13,3939,0,4367,0,0]Al[4725,13,4726,1838,4368,0,0]Ai[3938,13,3939,0,4104,0,0]Al[4725,13,4726,1838,4106,0,0]Al[3938,13,3939,2436,2391,0,0]Al[3938,13,3939,2463,2395,0,0]Al[4725,13,4726,2437,2396,0,0]Al[4725,13,4726,3703,3301,0,0]Ao[4007,13,4008,4008,5014,4820,0];:Ah[3938,5,1840,1841,0,0,0]Ae[4725,5,1841,0,0,0,0]Ai[3938,15,1919,0,1337,0,0]Ai[4725,15,1920,0,1338,0,0]?>?>Al[3938,13,3939,3702,3300,0,0]6Ai[3938,13,3939,0,4506,0,0]Al[4725,13,4726,3283,4507,0,0]Ae[4007,7,4008,0,0,0,0]Ae[4296,7,4297,0,0,0,0]Ai[3938,14,3939,0,3306,0,0]Al[4725,14,4726,1538,3307,0,0]?=Ai[3938,19,3939,0,4825,0,0]Ai[4725,19,4726,0,4826,0,0]2=<==<>=>=>=<;<;Al[4725,14,4968,4968,4867,0,0]?>Ai[4725,18,4726,3283,0,0,0]") \ No newline at end of file diff --git a/pr/323/docs/search.index/entry/c07d808995d5.js b/pr/323/docs/search.index/entry/c07d808995d5.js new file mode 100644 index 000000000..7f3531760 --- /dev/null +++ b/pr/323/docs/search.index/entry/c07d808995d5.js @@ -0,0 +1 @@ +rd_("Ah[3940,15,3941,0,117,0,0]Ak[4727,15,4728,4728,118,0,0]Ai[3940,15,3941,0,4126,0,0]Al[4727,15,4728,1931,4127,0,0]Al[4433,15,4434,4434,4128,0,0]Al[4727,15,1837,1837,1590,0,0]Ao[1362,13,1363,1363,3972,3133,0]Ao[3940,13,3941,3704,4769,3133,0]Ao[3940,13,3941,4076,4393,3133,0]Ao[3940,13,3941,4076,3141,3133,0]Ao[3940,13,3941,4076,3329,3133,0]Ao[3940,13,3941,4076,4392,3133,0]Ao[3940,13,3941,4081,3347,3133,0]Ao[3940,13,3941,4081,4160,3133,0]Al[3940,13,3941,0,1600,3133,0]Al[3940,13,3941,0,4928,3133,0]Al[3940,13,3941,0,1321,3133,0]Al[3940,13,3941,0,1583,3133,0]Al[3940,13,3941,0,3138,3133,0]Al[3940,13,3941,0,2011,3133,0]Al[3940,13,3941,0,4112,3133,0]Al[3940,13,3941,0,4487,3133,0]Al[3940,13,3941,0,3985,3133,0]Al[3940,13,3941,0,3975,3133,0]Al[3940,13,3941,0,3121,3133,0]Al[3940,13,3941,0,4484,3133,0]Al[3940,13,3941,0,4672,3133,0]Al[3940,13,3941,0,4118,3133,0]Al[3940,13,3941,0,4121,3133,0]Al[3940,13,3941,0,4126,3133,0]Al[3940,13,3941,0,4827,3133,0]Ak[3940,13,3941,0,482,3133,0]Ak[3940,13,3941,0,117,3133,0]Al[3940,13,3941,0,2415,3133,0]Al[3940,13,1841,0,5074,3133,0]Al[3940,13,1920,0,1338,3133,0]Ao[4727,13,4728,1953,1601,3133,0]Ao[4727,13,4728,1953,4929,3133,0]Ao[4727,13,4728,3157,1322,3133,0]Ao[4727,13,4728,3166,4770,3133,0]Ao[4727,13,4728,1523,1584,3133,0]Ao[4727,13,4728,1523,3323,3133,0]Ao[4727,13,4728,1523,3137,3133,0]Ao[4727,13,4728,1523,4824,3133,0]Ao[4727,13,4728,1523,3298,3133,0]Ao[4727,13,4728,1538,3139,3133,0]Ao[4727,13,4728,1072,3142,3133,0]Ao[4727,13,4728,1072,4282,3133,0]Ao[4727,13,4728,1077,2012,3133,0]Ao[4727,13,4728,1077,3348,3133,0]Ao[4727,13,4728,1077,3984,3133,0]Ao[4727,13,4728,2145,4002,3133,0]Ao[4727,13,4728,2145,4394,3133,0]Ao[4727,13,4728,2145,4116,3133,0]Ao[4727,13,4728,3705,4488,3133,0]Ao[4727,13,4728,3705,3134,3133,0]Ao[4727,13,4728,3705,4360,3133,0]Ao[4727,13,4728,3285,3986,3133,0]Ao[4727,13,4728,1839,3976,3133,0]Ao[4727,13,4728,1513,3122,3133,0]Ao[4727,13,4728,1513,4375,3133,0]Ao[4727,13,4728,1513,3315,3133,0]Ao[4727,13,4728,1513,3337,3133,0]Ao[4727,13,4728,1513,4123,3133,0]Ao[4727,13,4728,1513,4486,3133,0]Ao[4727,13,4728,1513,4674,3133,0]Ao[4727,13,4728,2438,4678,3133,0]Ao[4727,13,4728,2438,4119,3133,0]Ao[4727,13,4728,1904,2399,3133,0]Ao[4727,13,4728,1904,4768,3133,0]Ao[4727,13,4728,1931,4122,3133,0]Ao[4727,13,4728,1931,4127,3133,0]Al[4727,13,4728,0,4398,3133,0]Al[4727,13,4728,0,4828,3133,0]An[4727,13,4728,4728,483,3133,0]An[4727,13,4728,4728,118,3133,0]Ao[4727,13,4728,4728,2416,3133,0]Ao[4727,13,4728,4728,3120,3133,0]Ao[4727,13,1837,1837,4288,3133,0]Ao[4727,13,4970,4970,4869,3133,0]Ao[4727,13,4970,4970,1340,3133,0]Ao[4727,13,1842,1842,5075,3133,0]Al[4727,13,1921,0,1339,3133,0]Ao[4433,13,4434,4434,4128,3133,0]Ak[3940,13,3941,1046,475,0,0]Al[3940,13,3941,4076,3327,0,0]Al[3940,13,3941,4076,4870,0,0]Al[3940,13,3941,4081,3333,0,0]Ai[4727,14,3135,0,1577,0,0]Ai[4727,14,3135,0,1567,0,0]Ai[4727,14,3135,0,3100,0,0]Ai[4727,14,3135,0,4265,0,0]Ai[4727,14,3135,0,4147,0,0]Ai[4727,14,3135,0,4140,0,0]Ai[4727,14,4001,0,2409,0,0]Ai[4727,14,4001,0,2430,0,0]Ai[4727,14,4001,0,2394,0,0]Ai[4727,14,4001,0,2000,0,0]Ae[1362,7,1363,0,0,0,0]Ah[4727,14,4499,0,471,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Ag[3940,5,3699,133,0,0,0]Ag[4727,5,3700,133,0,0,0]Al[1047,15,1048,1048,4862,0,0]Ae[3960,5,3961,0,0,0,0]Ah[3940,6,3941,4728,0,0,0]Ae[4727,6,4728,0,0,0,0]Al[4727,15,1837,1837,1590,0,0]Ai[3940,19,3941,0,1600,0,0]Ai[3940,19,3941,0,1583,0,0]Al[4727,19,4728,1953,1601,0,0]Al[4727,19,4728,1523,1584,0,0]Ae[1362,5,1363,0,0,0,0]Ae[1047,5,1831,0,0,0,0]Ae[1047,5,1926,0,0,0,0]Ah[3940,15,3941,0,117,0,0]Ak[4727,15,4728,4728,118,0,0]Ak[3940,13,3941,0,1600,128,0]Ak[3940,13,3941,0,1583,128,0]Ak[3940,13,3941,0,3985,128,0]Ak[3940,13,3941,0,4121,128,0]Aj[3940,13,3941,0,482,128,0]Aj[3940,13,3941,0,117,128,0]Ak[3940,13,3941,0,2415,128,0]An[4727,13,4728,1953,1601,128,0]An[4727,13,4728,3166,4770,128,0]An[4727,13,4728,1523,1584,128,0]An[4727,13,4728,1072,3142,128,0]An[4727,13,4728,1072,4282,128,0]An[4727,13,4728,1077,3348,128,0]An[4727,13,4728,3285,3986,128,0]An[4727,13,4728,2438,4678,128,0]An[4727,13,4728,1904,4768,128,0]An[4727,13,4728,1931,4122,128,0]Am[4727,13,4728,4728,483,128,0]Am[4727,13,4728,4728,118,128,0]An[4727,13,4728,4728,2416,128,0]Ai[3940,13,3941,0,2415,0,0]Al[4727,13,4728,4728,2416,0,0]Ai[4727,14,4001,0,1578,0,0]Ai[4727,14,4001,0,3106,0,0]Ai[3940,14,3941,0,2424,0,0]Al[4727,14,4728,4728,2425,0,0]Al[4433,14,4434,4434,2413,0,0]Al[4433,14,4434,4434,2426,0,0]An[1362,13,1363,1363,125,1328,0]Ao[1362,13,1363,1363,3972,1328,0]Ao[1362,13,1537,1537,4395,1328,0]Ao[4009,13,4010,4010,5016,1328,0]Ao[3940,13,3941,3704,3300,1328,0]Ao[3940,13,3941,3704,3302,1328,0]Ao[3940,13,3941,3704,4931,1328,0]Dd[3940,13,3941,3704,4769,1972,0,\"impl-Display-for-SendDatagramError\"]Db[3940,13,3941,3704,4769,1328,0,\"impl-Debug-for-SendDatagramError\"]Ao[3940,13,3941,2437,2392,1328,0]Ao[3940,13,3941,2437,4255,1328,0]Ao[3940,13,3941,2464,2396,1328,0]Cm[3940,13,3941,2464,3330,1972,0,\"impl-Display-for-RetryError\"]Ck[3940,13,3941,2464,3330,1328,0,\"impl-Debug-for-RetryError\"]Ao[3940,13,3941,2464,4374,1328,0]An[3940,13,3941,1046,475,1328,0]Ao[3940,13,3941,4076,3327,1328,0]Ao[3940,13,3941,4076,4870,1328,0]Da[3940,13,3941,4076,4393,1972,0,\"impl-Display-for-ReadToEndError\"]Co[3940,13,3941,4076,4393,1328,0,\"impl-Debug-for-ReadToEndError\"]Cj[3940,13,3941,4076,3141,1328,0,\"impl-Debug-for-ReadError\"]Cl[3940,13,3941,4076,3141,1972,0,\"impl-Display-for-ReadError\"]Ck[3940,13,3941,4076,3329,1328,0,\"impl-Debug-for-ResetError\"]Cm[3940,13,3941,4076,3329,1972,0,\"impl-Display-for-ResetError\"]Da[3940,13,3941,4076,4392,1972,0,\"impl-Display-for-ReadExactError\"]Co[3940,13,3941,4076,4392,1328,0,\"impl-Debug-for-ReadExactError\"]Ao[3940,13,3941,1549,4161,1328,0]Ao[3940,13,3941,1089,3998,1328,0]Ao[3940,13,3941,4081,3333,1328,0]Ck[3940,13,3941,4081,3347,1328,0,\"impl-Debug-for-WriteError\"]Cm[3940,13,3941,4081,3347,1972,0,\"impl-Display-for-WriteError\"]Cm[3940,13,3941,4081,4160,1328,0,\"impl-Debug-for-StoppedError\"]Co[3940,13,3941,4081,4160,1972,0,\"impl-Display-for-StoppedError\"]Cf[3940,13,3941,0,1600,1972,0,\"impl-Display-for-VarInt\"]Cd[3940,13,3941,0,1600,1328,0,\"impl-Debug-for-VarInt\"]Dd[3940,13,3941,0,4928,1972,0,\"impl-Display-for-VarIntBoundsExceeded\"]Db[3940,13,3941,0,4928,1328,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Al[3940,13,3941,0,1321,1328,0]Cd[3940,13,3941,0,1583,1328,0,\"impl-Debug-for-PathId\"]Cf[3940,13,3941,0,1583,1972,0,\"impl-Display-for-PathId\"]Al[3940,13,3941,0,2427,1328,0]Al[3940,13,3941,0,3308,1328,0]Al[3940,13,3941,0,3138,1328,0]Al[3940,13,3941,0,4491,1328,0]Al[3940,13,3941,0,2011,1328,0]Cj[3940,13,3941,0,4112,1328,0,\"impl-Debug-for-ClosedStream\"]Cl[3940,13,3941,0,4112,1972,0,\"impl-Display-for-ClosedStream\"]Cm[3940,13,3941,0,4487,1328,0,\"impl-Debug-for-ConnectionError\"]Co[3940,13,3941,0,4487,1972,0,\"impl-Display-for-ConnectionError\"]Al[3940,13,3941,0,4501,1328,0]Al[3940,13,3941,0,4508,1328,0]Al[3940,13,3941,0,4793,1328,0]Al[3940,13,3941,0,4808,1328,0]Al[3940,13,3941,0,3985,1328,0]Al[3940,13,3941,0,4369,1328,0]Al[3940,13,3941,0,4154,1328,0]Al[3940,13,3941,0,4985,1328,0]Al[3940,13,3941,0,4106,1328,0]Ci[3940,13,3941,0,3975,1328,0,\"impl-Debug-for-ConfigError\"]Ck[3940,13,3941,0,3975,1972,0,\"impl-Display-for-ConfigError\"]Ci[3940,13,3941,0,3121,1972,0,\"impl-Display-for-FrameType\"]Cg[3940,13,3941,0,3121,1328,0,\"impl-Debug-for-FrameType\"]Co[3940,13,3941,0,4484,1972,0,\"impl-Display-for-ConnectionClose\"]Cm[3940,13,3941,0,4484,1328,0,\"impl-Debug-for-ConnectionClose\"]Cn[3940,13,3941,0,4672,1328,0,\"impl-Debug-for-ApplicationClose\"]D`[3940,13,3941,0,4672,1972,0,\"impl-Display-for-ApplicationClose\"]Cj[3940,13,3941,0,4118,1328,0,\"impl-Debug-for-ConnectError\"]Cl[3940,13,3941,0,4118,1972,0,\"impl-Display-for-ConnectError\"]Cl[3940,13,3941,0,4121,1972,0,\"impl-Display-for-ConnectionId\"]Cj[3940,13,3941,0,4121,1328,0,\"impl-Debug-for-ConnectionId\"]Al[3940,13,3941,0,4126,1328,0]Db[3940,13,3941,0,4827,1972,0,\"impl-Display-for-TransportErrorCode\"]D`[3940,13,3941,0,4827,1328,0,\"impl-Debug-for-TransportErrorCode\"]Al[3940,13,3941,0,3311,1328,0]Al[3940,13,3941,0,4703,1328,0]Ak[3940,13,3941,0,482,1328,0]C`[3940,13,3941,0,117,1328,0,\"impl-Debug-for-Dir\"]Cb[3940,13,3941,0,117,1972,0,\"impl-Display-for-Dir\"]Cf[3940,13,3941,0,2415,1328,0,\"impl-Debug-for-StreamId\"]Ch[3940,13,3941,0,2415,1972,0,\"impl-Display-for-StreamId\"]Al[3940,13,3941,0,2424,1328,0]Ak[3940,13,3699,0,111,1328,0]Al[3940,13,3699,0,3109,1328,0]Al[3940,13,3699,0,1325,1328,0]Al[3940,13,3699,0,3980,1328,0]Al[3940,13,3699,0,1985,1328,0]Al[3940,13,3699,0,4274,1328,0]Al[3940,13,3699,0,4761,1328,0]Al[3940,13,1841,0,5074,1328,0]Al[3940,13,1841,0,3978,1328,0]Al[3940,13,1841,0,4831,1328,0]Dd[3940,13,1920,0,4924,1972,0,\"impl-Display-for-NoInitialCipherSuite\"]Db[3940,13,1920,0,4924,1328,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cc[3940,13,1920,0,1338,1328,0,\"impl-Debug-for-Error\"]Ce[3940,13,1920,0,1338,1972,0,\"impl-Display-for-Error\"]Ao[4727,13,4728,3705,3303,1328,0]Ao[4727,13,4728,2438,2393,1328,0]Cg[4727,13,4728,1953,1601,1328,0,\"impl-Debug-for-VarInt\"]Ci[4727,13,4728,1953,1601,1972,0,\"impl-Display-for-VarInt\"]Dg[4727,13,4728,1953,4929,1972,0,\"impl-Display-for-VarIntBoundsExceeded\"]De[4727,13,4728,1953,4929,1328,0,\"impl-Debug-for-VarIntBoundsExceeded\"]Ao[4727,13,4728,3157,1322,1328,0]Dd[4727,13,4728,3166,4770,1972,0,\"impl-Display-for-SendDatagramError\"]Db[4727,13,4728,3166,4770,1328,0,\"impl-Debug-for-SendDatagramError\"]Ci[4727,13,4728,1523,1584,1972,0,\"impl-Display-for-PathId\"]Cg[4727,13,4728,1523,1584,1328,0,\"impl-Debug-for-PathId\"]Ao[4727,13,4728,1523,4150,1328,0]Ao[4727,13,4728,1523,3323,1328,0]Ao[4727,13,4728,1523,3137,1328,0]De[4727,13,4728,1523,4824,1972,0,\"impl-Display-for-SetPathStatusError\"]Dc[4727,13,4728,1523,4824,1328,0,\"impl-Debug-for-SetPathStatusError\"]Ck[4727,13,4728,1523,3298,1328,0,\"impl-Debug-for-ClosedPath\"]Cm[4727,13,4728,1523,3298,1972,0,\"impl-Display-for-ClosedPath\"]Ao[4727,13,4728,1538,2428,1328,0]Ao[4727,13,4728,1538,3309,1328,0]Ao[4727,13,4728,1538,3139,1328,0]Ao[4727,13,4728,1538,4492,1328,0]Cj[4727,13,4728,1072,3142,1328,0,\"impl-Debug-for-ReadError\"]Cl[4727,13,4728,1072,3142,1972,0,\"impl-Display-for-ReadError\"]D`[4727,13,4728,1072,4282,1972,0,\"impl-Display-for-ReadableError\"]Cn[4727,13,4728,1072,4282,1328,0,\"impl-Debug-for-ReadableError\"]Ao[4727,13,4728,1077,2012,1328,0]Cm[4727,13,4728,1077,3348,1972,0,\"impl-Display-for-WriteError\"]Ck[4727,13,4728,1077,3348,1328,0,\"impl-Debug-for-WriteError\"]Cn[4727,13,4728,1077,3984,1972,0,\"impl-Display-for-FinishError\"]Cl[4727,13,4728,1077,3984,1328,0,\"impl-Debug-for-FinishError\"]Ao[4727,13,4728,2145,4002,1328,0]Ao[4727,13,4728,2145,4394,1328,0]Co[4727,13,4728,2145,4116,1972,0,\"impl-Display-for-ClosedStream\"]Cm[4727,13,4728,2145,4116,1328,0,\"impl-Debug-for-ClosedStream\"]D`[4727,13,4728,3705,4488,1328,0,\"impl-Debug-for-ConnectionError\"]Db[4727,13,4728,3705,4488,1972,0,\"impl-Display-for-ConnectionError\"]Cj[4727,13,4728,3705,3134,1328,0,\"impl-Debug-for-PathError\"]Cl[4727,13,4728,3705,3134,1972,0,\"impl-Display-for-PathError\"]Da[4727,13,4728,3705,4360,1972,0,\"impl-Display-for-ClosePathError\"]Co[4727,13,4728,3705,4360,1328,0,\"impl-Debug-for-ClosePathError\"]Dg[4727,13,4728,3705,5012,1328,0,\"impl-Debug-for-MultipathNotNegotiated\"]Di[4727,13,4728,3705,5012,1972,0,\"impl-Display-for-MultipathNotNegotiated\"]Ao[4727,13,4728,3705,1343,1328,0]Ao[4727,13,4728,1068,4502,1328,0]Ao[4727,13,4728,3285,4509,1328,0]Ao[4727,13,4728,3285,4794,1328,0]Ao[4727,13,4728,3285,4809,1328,0]Ao[4727,13,4728,3285,3986,1328,0]Ao[4727,13,4728,1839,4370,1328,0]Ao[4727,13,4728,1839,4156,1328,0]Ao[4727,13,4728,1839,4986,1328,0]Ao[4727,13,4728,1839,4108,1328,0]Cn[4727,13,4728,1839,3976,1972,0,\"impl-Display-for-ConfigError\"]Cl[4727,13,4728,1839,3976,1328,0,\"impl-Debug-for-ConfigError\"]Cl[4727,13,4728,1513,3122,1972,0,\"impl-Display-for-FrameType\"]Cj[4727,13,4728,1513,3122,1328,0,\"impl-Debug-for-FrameType\"]Ao[4727,13,4728,1513,4375,1328,0]Ck[4727,13,4728,1513,3315,1328,0,\"impl-Debug-for-MaybeFrame\"]Cm[4727,13,4728,1513,3315,1972,0,\"impl-Display-for-MaybeFrame\"]Cm[4727,13,4728,1513,3337,1972,0,\"impl-Display-for-StreamInfo\"]Ck[4727,13,4728,1513,3337,1328,0,\"impl-Debug-for-StreamInfo\"]Co[4727,13,4728,1513,4123,1972,0,\"impl-Display-for-DatagramInfo\"]Cm[4727,13,4728,1513,4123,1328,0,\"impl-Debug-for-DatagramInfo\"]Db[4727,13,4728,1513,4486,1972,0,\"impl-Display-for-ConnectionClose\"]D`[4727,13,4728,1513,4486,1328,0,\"impl-Debug-for-ConnectionClose\"]Dc[4727,13,4728,1513,4674,1972,0,\"impl-Display-for-ApplicationClose\"]Da[4727,13,4728,1513,4674,1328,0,\"impl-Debug-for-ApplicationClose\"]Ck[4727,13,4728,1513,2386,1972,0,\"impl-Display-for-Datagram\"]Ci[4727,13,4728,1513,2386,1328,0,\"impl-Debug-for-Datagram\"]Ao[4727,13,4728,2438,4678,1328,0]Ao[4727,13,4728,2438,2397,1328,0]Co[4727,13,4728,2438,4119,1972,0,\"impl-Display-for-ConnectError\"]Cm[4727,13,4728,2438,4119,1328,0,\"impl-Debug-for-ConnectError\"]Ao[4727,13,4728,2438,3970,1328,0]Ck[4727,13,4728,2438,3331,1328,0,\"impl-Debug-for-RetryError\"]Cm[4727,13,4728,2438,3331,1972,0,\"impl-Display-for-RetryError\"]Ao[4727,13,4728,1904,4277,1328,0]Ao[4727,13,4728,1904,4500,1328,0]Ao[4727,13,4728,1904,5015,1328,0]Ao[4727,13,4728,1904,2399,1328,0]Dd[4727,13,4728,1904,4768,1972,0,\"impl-Display-for-PacketDecodeError\"]Db[4727,13,4728,1904,4768,1328,0,\"impl-Debug-for-PacketDecodeError\"]Ao[4727,13,4728,1931,4490,1328,0]Ao[4727,13,4728,1931,4254,1328,0]Cm[4727,13,4728,1931,4122,1328,0,\"impl-Debug-for-ConnectionId\"]Co[4727,13,4728,1931,4122,1972,0,\"impl-Display-for-ConnectionId\"]Ao[4727,13,4728,1931,4127,1328,0]Cc[4727,13,4728,0,4398,1328,0,\"impl-Debug-for-Error\"]Ce[4727,13,4728,0,4398,1972,0,\"impl-Display-for-Error\"]Cb[4727,13,4728,0,4828,1328,0,\"impl-Debug-for-Code\"]Cd[4727,13,4728,0,4828,1972,0,\"impl-Display-for-Code\"]Ao[4727,13,4728,4295,3312,1328,0]Ao[4727,13,4728,4295,5108,1328,0]Ao[4727,13,4728,4858,4704,1328,0]An[4727,13,4728,4728,483,1328,0]Cc[4727,13,4728,4728,118,1328,0,\"impl-Debug-for-Dir\"]Ce[4727,13,4728,4728,118,1972,0,\"impl-Display-for-Dir\"]Ci[4727,13,4728,4728,2416,1328,0,\"impl-Debug-for-StreamId\"]Ck[4727,13,4728,4728,2416,1972,0,\"impl-Display-for-StreamId\"]Ao[4727,13,4728,4728,2425,1328,0]Cj[4727,13,4728,4728,3120,1328,0,\"impl-Debug-for-FourTuple\"]Cl[4727,13,4728,4728,3120,1972,0,\"impl-Display-for-FourTuple\"]Cn[4727,13,1837,1837,4288,1328,0,\"impl-Debug-for-UnexpectedEnd\"]D`[4727,13,1837,1837,4288,1972,0,\"impl-Display-for-UnexpectedEnd\"]Ao[4727,13,4970,4970,4869,1328,0]Ch[4727,13,4970,4970,1340,1972,0,\"impl-Display-for-Error\"]Cf[4727,13,4970,4970,1340,1328,0,\"impl-Debug-for-Error\"]Ao[4727,13,1842,1842,5075,1328,0]Ao[4727,13,1842,1842,3979,1328,0]Ao[4727,13,1842,1842,4834,1328,0]Dg[4727,13,1921,1921,4925,1972,0,\"impl-Display-for-NoInitialCipherSuite\"]De[4727,13,1921,1921,4925,1328,0,\"impl-Debug-for-NoInitialCipherSuite\"]Cc[4727,13,1921,0,1339,1328,0,\"impl-Debug-for-Error\"]Ce[4727,13,1921,0,1339,1972,0,\"impl-Display-for-Error\"]Am[4727,13,3700,133,112,1328,0]An[4727,13,3700,133,3110,1328,0]Ao[4727,13,3700,1508,1326,1328,0]Ao[4727,13,3700,1508,3981,1328,0]Ao[4727,13,3700,2486,1986,1328,0]Ao[4727,13,3700,2486,4275,1328,0]Ao[4727,13,3700,3700,4762,1328,0]Ch[4727,13,2116,2116,1341,1972,0,\"impl-Display-for-Error\"]Cf[4727,13,2116,2116,1341,1328,0,\"impl-Debug-for-Error\"]Ao[4727,13,2116,2116,1344,1328,0]An[4433,13,4434,390,4399,1328,0]Ao[4433,13,4434,4434,2413,1328,0]Ao[4433,13,4434,4434,2426,1328,0]Ao[4433,13,4434,4434,4128,1328,0]Al[4727,13,4728,1523,4150,0,0]Ai[3940,14,3941,0,2427,0,0]Al[4727,14,4728,1538,2428,0,0]Al[4727,13,4728,1904,4277,0,0]Al[4727,14,4728,1904,5015,0,0]Ah[4727,14,4499,0,471,0,0]Al[4433,14,4434,4434,2413,0,0]Ai[3940,13,3941,0,4985,0,0]Al[4727,13,4728,1839,4986,0,0]8Al[1362,13,1537,1537,4395,0,0]Al[4009,13,4010,4010,5016,0,0]Ai[3940,13,3941,0,4246,0,0]Al[3940,13,3941,2437,2392,0,0]Ai[3940,13,3941,0,3324,0,0]Ai[3940,13,3941,0,4501,0,0]Ai[3940,13,3941,0,4369,0,0]Ai[3940,13,3941,0,4154,0,0]Ai[3940,13,3941,0,4106,0,0]Ai[3940,13,3941,0,4121,0,0]Ai[3940,13,3941,0,4703,0,0]Ai[3940,13,3941,0,2415,0,0]Ah[3940,13,3699,0,111,0,0]Ai[3940,13,3699,0,1325,0,0]Ai[3940,13,3699,0,1985,0,0]Al[4727,13,4728,4518,4247,0,0]Al[4727,13,4728,1068,3325,0,0]Al[4727,13,4728,2438,2393,0,0]Al[4727,13,4728,1904,5133,0,0]Al[4727,13,4728,4295,5103,0,0]Al[4727,13,4728,1068,4502,0,0]Al[4727,13,4728,1839,4370,0,0]Al[4727,13,4728,1839,4156,0,0]Al[4727,13,4728,1839,4108,0,0]Al[4727,13,4728,1904,4277,0,0]Al[4727,13,4728,1931,4122,0,0]Ai[4727,13,4728,0,4398,0,0]Al[4727,13,4728,4295,5108,0,0]Al[4727,13,4728,4858,4704,0,0]Al[4727,13,4728,4728,2416,0,0]Aj[4727,13,3700,133,112,0,0]Al[4727,13,3700,1508,1326,0,0]Al[4727,13,3700,2486,1986,0,0]Ak[4433,13,4434,390,4399,0,0]Al[1047,13,4213,4213,5060,0,0]Al[1047,13,4213,4213,5061,0,0]Aj[3940,13,3941,0,482,124,0]Am[4727,13,4728,4728,483,124,0]Ai[3940,12,3941,0,3340,0,0]Al[3940,13,3941,2137,1997,0,0]0Ao[3940,13,3941,1549,4161,1997,0]Al[3940,13,3941,0,4286,3340,0]Al[4727,12,4728,1839,3341,0,0]Ao[4727,13,4728,1839,4287,3341,0]Al[3940,13,3941,3704,3302,0,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,13,4728,3705,3303,0,0]Al[4727,14,4728,1538,3139,0,0]Ae[1047,7,1831,0,0,0,0]Ae[1047,7,1926,0,0,0,0]Ae[3940,4,3941,0,0,0,0]Ai[3940,15,3941,0,4126,0,0]Al[4727,15,4728,1931,4127,0,0]Al[4433,15,4434,4434,4128,0,0]210Ah[3940,5,1841,1842,0,0,0]Ae[4727,5,1842,0,0,0,0]Al[4727,15,4728,1904,4500,0,0]Al[4727,15,4728,1513,3315,0,0]Ah[3940,5,3941,1046,0,0,0]Al[4727,15,4728,3705,1343,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Al[3940,15,3941,4076,4393,0,0]Ah[3940,6,3941,4728,0,0,0]Ae[4727,6,4728,0,0,0,0]Ai[3940,19,3941,0,1583,0,0]Al[4727,19,4728,1523,1584,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Ai[4727,14,3135,0,4140,0,0]Al[4433,14,4434,4434,2413,0,0]Aa[492,3,0,0,0,0,0]Ai[4727,14,4728,0,4398,0,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,14,4728,1538,3139,0,0]Al[4727,14,4728,1513,2386,0,0]An[3940,13,3941,3704,2406,459,0]An[3940,13,3941,2464,2396,459,0]An[3940,13,3941,4076,3327,459,0]An[3940,13,3941,4081,3333,459,0]An[4727,13,4728,1072,1563,459,0]Am[1362,13,1363,1363,125,466,0]An[1362,13,1363,1363,3972,466,0]An[1362,13,1537,1537,4003,466,0]An[1362,13,1537,1537,1358,466,0]An[1362,13,1537,1537,4395,466,0]Ak[492,13,493,493,3973,466,0]An[4009,13,4010,4010,5016,466,0]An[3940,13,3941,3704,4513,466,0]An[3940,13,3941,3704,2406,466,0]Ak[3940,13,3941,0,4246,466,0]An[3940,13,3941,1046,2407,466,0]An[3940,13,3941,1046,3112,466,0]An[3940,13,3941,1046,4670,466,0]An[3940,13,3941,3704,3300,466,0]An[3940,13,3941,3704,3302,466,0]An[3940,13,3941,3704,1988,466,0]An[3940,13,3941,3704,1576,466,0]An[3940,13,3941,3704,3102,466,0]An[3940,13,3941,3704,2380,466,0]An[3940,13,3941,3704,4146,466,0]An[3940,13,3941,3704,4151,466,0]An[3940,13,3941,3704,4931,466,0]An[3940,13,3941,3704,4769,466,0]0An[3940,13,3941,2437,2392,466,0]An[3940,13,3941,2437,4255,466,0]An[3940,13,3941,2437,1555,466,0]An[3940,13,3941,2464,2396,466,0]An[3940,13,3941,2464,3330,466,0]An[3940,13,3941,2464,4374,466,0]Am[3940,13,3941,1046,475,466,0]An[3940,13,3941,4076,3327,466,0]An[3940,13,3941,4076,4870,466,0]An[3940,13,3941,4076,4393,466,0]0Dm[3940,13,3941,4076,3141,466,0,\"impl-From%3CConnectionError%3E-for-ReadError\"]Dh[3940,13,3941,4076,3141,466,0,\"impl-From%3CResetError%3E-for-ReadError\"]An[3940,13,3941,4076,3141,466,0]An[3940,13,3941,4076,3329,466,0]0An[3940,13,3941,4076,4392,466,0]0An[3940,13,3941,1549,4161,466,0]An[3940,13,3941,1089,3998,466,0]An[3940,13,3941,4081,3333,466,0]An[3940,13,3941,4081,3347,466,0]Dn[3940,13,3941,4081,3347,466,0,\"impl-From%3CConnectionError%3E-for-WriteError\"]Dk[3940,13,3941,4081,3347,466,0,\"impl-From%3CStoppedError%3E-for-WriteError\"]Dk[3940,13,3941,4081,3347,466,0,\"impl-From%3CClosedStream%3E-for-WriteError\"]An[3940,13,3941,4081,4160,466,0]0Ak[3940,13,3941,0,3324,466,0]Ak[3940,13,3941,0,4286,466,0]Ak[3940,13,3941,0,4505,466,0]Ak[3940,13,3941,0,4138,466,0]Ak[3940,13,3941,0,4383,466,0]Dj[3940,13,3941,0,1600,466,0,\"impl-From%3CTransportErrorCode%3E-for-VarInt\"]Ak[3940,13,3941,0,1600,466,0]Ck[3940,13,3941,0,1600,466,0,\"impl-From%3Cu32%3E-for-VarInt\"]D`[3940,13,3941,0,1600,466,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Cj[3940,13,3941,0,1600,466,0,\"impl-From%3Cu8%3E-for-VarInt\"]Ck[3940,13,3941,0,1600,466,0,\"impl-From%3Cu16%3E-for-VarInt\"]Ak[3940,13,3941,0,4928,466,0]Ak[3940,13,3941,0,1321,466,0]Ak[3940,13,3941,0,1583,466,0]0Ak[3940,13,3941,0,2427,466,0]Ak[3940,13,3941,0,3308,466,0]Ak[3940,13,3941,0,3138,466,0]Ak[3940,13,3941,0,4491,466,0]Ak[3940,13,3941,0,2011,466,0]Ak[3940,13,3941,0,4112,466,0]Dl[3940,13,3941,0,4487,466,0,\"impl-From%3CCloseReason%3E-for-ConnectionError\"]Do[3940,13,3941,0,4487,466,0,\"impl-From%3CTransportError%3E-for-ConnectionError\"]Df[3940,13,3941,0,4487,466,0,\"impl-From%3CClose%3E-for-ConnectionError\"]Ak[3940,13,3941,0,4487,466,0]Ak[3940,13,3941,0,4501,466,0]Ak[3940,13,3941,0,4508,466,0]Ak[3940,13,3941,0,4793,466,0]Ak[3940,13,3941,0,4808,466,0]Ak[3940,13,3941,0,3985,466,0]0Ak[3940,13,3941,0,4369,466,0]Ak[3940,13,3941,0,4154,466,0]Ak[3940,13,3941,0,4985,466,0]Ak[3940,13,3941,0,4106,466,0]Ea[3940,13,3941,0,3975,466,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]Ak[3940,13,3941,0,3975,466,0]Dl[3940,13,3941,0,3975,466,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]Ak[3940,13,3941,0,3121,466,0]Ak[3940,13,3941,0,4484,466,0]0Ak[3940,13,3941,0,4672,466,0]Ak[3940,13,3941,0,4118,466,0]0Ak[3940,13,3941,0,4121,466,0]Ak[3940,13,3941,0,4126,466,0]Ak[3940,13,3941,0,4827,466,0]0Ak[3940,13,3941,0,3311,466,0]Ak[3940,13,3941,0,4703,466,0]Aj[3940,13,3941,0,482,466,0]Aj[3940,13,3941,0,117,466,0]Ak[3940,13,3941,0,2415,466,0]0Ak[3940,13,3941,0,2424,466,0]Aj[3940,13,3699,0,111,466,0]Ak[3940,13,3699,0,3109,466,0]Ak[3940,13,3699,0,1325,466,0]Ak[3940,13,3699,0,3980,466,0]Ak[3940,13,3699,0,1985,466,0]Ak[3940,13,3699,0,4274,466,0]Ak[3940,13,3699,0,4761,466,0]Ak[3940,13,1841,0,1980,466,0]Aj[3940,13,1841,0,469,466,0]Ak[3940,13,1841,0,5074,466,0]Ak[3940,13,1841,0,3978,466,0]0Ak[3940,13,1841,0,4831,466,0]Ak[3940,13,1920,0,3342,466,0]Ak[3940,13,1920,0,4257,466,0]Ak[3940,13,1920,0,4699,466,0]Ak[3940,13,1920,0,4701,466,0]Ak[3940,13,1920,0,4924,466,0]Ak[3940,13,1920,0,1338,466,0]E`[3940,13,1920,0,1338,466,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Dg[3940,13,1920,0,1338,466,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[3940,13,1920,0,1338,466,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]De[3940,13,1920,0,1338,466,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Dg[3940,13,1920,0,1338,466,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Dg[3940,13,1920,0,1338,466,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dn[3940,13,1920,0,1338,466,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]E`[3940,13,1920,0,1338,466,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Df[3940,13,1920,0,1338,466,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]Df[3940,13,1920,0,1338,466,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Da[3940,13,1920,0,1338,466,0,\"impl-From%3COtherError%3E-for-Error\"]An[4727,13,4728,4518,4247,466,0]An[4727,13,4728,3166,3116,466,0]An[4727,13,4728,1072,1563,466,0]An[4727,13,4728,2145,2005,466,0]An[4727,13,4728,2145,3328,466,0]An[4727,13,4728,2145,3334,466,0]An[4727,13,4728,3705,3303,466,0]An[4727,13,4728,1068,3325,466,0]An[4727,13,4728,1839,4287,466,0]An[4727,13,4728,2438,2393,466,0]An[4727,13,4728,2438,4253,466,0]An[4727,13,4728,1904,5133,466,0]An[4727,13,4728,4295,5103,466,0]An[4727,13,4728,1548,4506,466,0]An[4727,13,4728,1548,4139,466,0]An[4727,13,4728,1548,4384,466,0]Cm[4727,13,4728,1953,1601,466,0,\"impl-From%3Cu8%3E-for-VarInt\"]Cn[4727,13,4728,1953,1601,466,0,\"impl-From%3Cu16%3E-for-VarInt\"]Cn[4727,13,4728,1953,1601,466,0,\"impl-From%3Cu32%3E-for-VarInt\"]An[4727,13,4728,1953,1601,466,0]Dc[4727,13,4728,1953,1601,466,0,\"impl-From%3CStreamId%3E-for-VarInt\"]Co[4727,13,4728,1953,1601,466,0,\"impl-From%3CCode%3E-for-VarInt\"]An[4727,13,4728,1953,4929,466,0]An[4727,13,4728,3157,1322,466,0]An[4727,13,4728,3166,4770,466,0]An[4727,13,4728,1523,1584,466,0]0An[4727,13,4728,1523,4150,466,0]An[4727,13,4728,1523,3323,466,0]An[4727,13,4728,1523,3137,466,0]An[4727,13,4728,1523,4824,466,0]An[4727,13,4728,1523,3298,466,0]An[4727,13,4728,1538,2428,466,0]An[4727,13,4728,1538,3309,466,0]An[4727,13,4728,1538,3139,466,0]An[4727,13,4728,1538,4492,466,0]An[4727,13,4728,1072,3142,466,0]An[4727,13,4728,1072,4282,466,0]An[4727,13,4728,1077,2012,466,0]An[4727,13,4728,1077,3348,466,0]An[4727,13,4728,1077,3984,466,0]An[4727,13,4728,2145,4002,466,0]An[4727,13,4728,2145,4394,466,0]An[4727,13,4728,2145,4116,466,0]An[4727,13,4728,3705,4488,466,0]0An[4727,13,4728,3705,3134,466,0]An[4727,13,4728,3705,4360,466,0]An[4727,13,4728,3705,5012,466,0]An[4727,13,4728,3705,1343,466,0]0An[4727,13,4728,1068,4502,466,0]An[4727,13,4728,3285,4509,466,0]An[4727,13,4728,3285,4794,466,0]An[4727,13,4728,3285,4809,466,0]An[4727,13,4728,3285,3986,466,0]0An[4727,13,4728,1839,4370,466,0]An[4727,13,4728,1839,4156,466,0]An[4727,13,4728,1839,4986,466,0]An[4727,13,4728,1839,4108,466,0]Ed[4727,13,4728,1839,3976,466,0,\"impl-From%3CVarIntBoundsExceeded%3E-for-ConfigError\"]An[4727,13,4728,1839,3976,466,0]Do[4727,13,4728,1839,3976,466,0,\"impl-From%3CTryFromIntError%3E-for-ConfigError\"]An[4727,13,4728,1513,3122,466,0]An[4727,13,4728,1513,4375,466,0]An[4727,13,4728,1513,3315,466,0]An[4727,13,4728,1513,3337,466,0]An[4727,13,4728,1513,4123,466,0]An[4727,13,4728,1513,4486,466,0]0An[4727,13,4728,1513,4674,466,0]An[4727,13,4728,1513,2386,466,0]An[4727,13,4728,2438,4678,466,0]An[4727,13,4728,2438,2397,466,0]An[4727,13,4728,2438,4119,466,0]0An[4727,13,4728,2438,3970,466,0]An[4727,13,4728,2438,3331,466,0]An[4727,13,4728,1904,4277,466,0]An[4727,13,4728,1904,4500,466,0]An[4727,13,4728,1904,5015,466,0]An[4727,13,4728,1904,2399,466,0]An[4727,13,4728,1904,4768,466,0]0An[4727,13,4728,1931,4490,466,0]An[4727,13,4728,1931,4254,466,0]An[4727,13,4728,1931,4122,466,0]An[4727,13,4728,1931,4127,466,0]Ak[4727,13,4728,0,4398,466,0]0Ak[4727,13,4728,0,4828,466,0]0An[4727,13,4728,4295,3312,466,0]An[4727,13,4728,4295,5108,466,0]An[4727,13,4728,4858,4704,466,0]Am[4727,13,4728,4728,483,466,0]Am[4727,13,4728,4728,118,466,0]An[4727,13,4728,4728,2416,466,0]0An[4727,13,4728,4728,2425,466,0]An[4727,13,4728,4728,3120,466,0]An[4727,13,1837,1837,4288,466,0]An[4727,13,4970,4970,4869,466,0]An[4727,13,4970,4970,1340,466,0]0An[4727,13,1842,1842,1981,466,0]Am[4727,13,1842,1842,470,466,0]An[4727,13,1842,1842,5075,466,0]An[4727,13,1842,1842,3979,466,0]0An[4727,13,1842,1842,4834,466,0]An[4727,13,1921,1921,3343,466,0]An[4727,13,1921,1921,4258,466,0]An[4727,13,1921,1921,4700,466,0]An[4727,13,1921,1921,4702,466,0]An[4727,13,1921,1921,4925,466,0]Dg[4727,13,1921,0,1339,466,0,\"impl-From%3CCertificateError%3E-for-Error\"]Dg[4727,13,1921,0,1339,466,0,\"impl-From%3CPeerIncompatible%3E-for-Error\"]Df[4727,13,1921,0,1339,466,0,\"impl-From%3CGetRandomFailed%3E-for-Error\"]Df[4727,13,1921,0,1339,466,0,\"impl-From%3CSystemTimeError%3E-for-Error\"]E`[4727,13,1921,0,1339,466,0,\"impl-From%3CEncryptedClientHelloError%3E-for-Error\"]Dn[4727,13,1921,0,1339,466,0,\"impl-From%3CCertRevocationListError%3E-for-Error\"]E`[4727,13,1921,0,1339,466,0,\"impl-From%3CUnsupportedOperationError%3E-for-Error\"]Ak[4727,13,1921,0,1339,466,0]Dg[4727,13,1921,0,1339,466,0,\"impl-From%3CInconsistentKeys%3E-for-Error\"]De[4727,13,1921,0,1339,466,0,\"impl-From%3CInvalidMessage%3E-for-Error\"]Da[4727,13,1921,0,1339,466,0,\"impl-From%3COtherError%3E-for-Error\"]De[4727,13,1921,0,1339,466,0,\"impl-From%3CPeerMisbehaved%3E-for-Error\"]Al[4727,13,3700,133,112,466,0]Am[4727,13,3700,133,3110,466,0]An[4727,13,3700,1508,1326,466,0]An[4727,13,3700,1508,3981,466,0]An[4727,13,3700,2486,1986,466,0]An[4727,13,3700,2486,4275,466,0]An[4727,13,3700,3700,4762,466,0]An[4727,13,2116,2116,1341,466,0]An[4727,13,2116,2116,1344,466,0]An[4433,13,4434,4434,3346,466,0]0Am[4433,13,4434,390,4399,466,0]An[4433,13,4434,4434,2413,466,0]An[4433,13,4434,4434,2426,466,0]An[4433,13,4434,4434,4128,466,0]An[1047,13,1048,1048,3113,466,0]An[1047,13,1048,1048,4862,466,0]Am[1047,13,1831,1831,126,466,0]An[1047,13,1539,1539,1359,466,0]An[1047,13,1539,1539,4004,466,0]An[1047,13,1539,1539,4496,466,0]An[1047,13,4213,4213,5060,466,0]An[1047,13,4213,4213,5061,466,0]Am[1047,13,1926,1926,127,466,0]Am[3960,13,3961,3961,116,466,0]An[3960,13,3961,3961,2383,466,0]Ak[3940,13,3941,0,1600,467,0]Ak[3940,13,3941,0,1583,467,0]Ak[3940,13,3941,0,3985,467,0]Ak[3940,13,3941,0,4121,467,0]Aj[3940,13,3941,0,482,467,0]Aj[3940,13,3941,0,117,467,0]Ak[3940,13,3941,0,2415,467,0]An[4727,13,4728,1953,1601,467,0]An[4727,13,4728,3166,4770,467,0]An[4727,13,4728,1523,1584,467,0]An[4727,13,4728,1072,3142,467,0]An[4727,13,4728,1072,4282,467,0]An[4727,13,4728,1077,3348,467,0]An[4727,13,4728,3285,3986,467,0]An[4727,13,4728,2438,4678,467,0]An[4727,13,4728,1904,4768,467,0]An[4727,13,4728,1931,4122,467,0]Am[4727,13,4728,4728,483,467,0]Am[4727,13,4728,4728,118,467,0]An[4727,13,4728,4728,2416,467,0]An[4727,13,4728,4728,3120,467,0]Am[1362,13,1363,1363,125,468,0]An[1362,13,1363,1363,3972,468,0]An[1362,13,1537,1537,4003,468,0]An[1362,13,1537,1537,1358,468,0]An[1362,13,1537,1537,4395,468,0]Ak[492,13,493,493,3973,468,0]An[4009,13,4010,4010,5016,468,0]An[3940,13,3941,3704,4513,468,0]An[3940,13,3941,3704,2406,468,0]Ak[3940,13,3941,0,4246,468,0]An[3940,13,3941,1046,2407,468,0]An[3940,13,3941,1046,3112,468,0]An[3940,13,3941,1046,4670,468,0]An[3940,13,3941,3704,3300,468,0]An[3940,13,3941,3704,3302,468,0]An[3940,13,3941,3704,1988,468,0]An[3940,13,3941,3704,1576,468,0]An[3940,13,3941,3704,3102,468,0]An[3940,13,3941,3704,2380,468,0]An[3940,13,3941,3704,4146,468,0]An[3940,13,3941,3704,4151,468,0]An[3940,13,3941,3704,4931,468,0]An[3940,13,3941,3704,4769,468,0]An[3940,13,3941,2437,2392,468,0]An[3940,13,3941,2437,4255,468,0]An[3940,13,3941,2437,1555,468,0]An[3940,13,3941,2464,2396,468,0]An[3940,13,3941,2464,3330,468,0]An[3940,13,3941,2464,4374,468,0]Am[3940,13,3941,1046,475,468,0]An[3940,13,3941,4076,3327,468,0]An[3940,13,3941,4076,4870,468,0]An[3940,13,3941,4076,4393,468,0]An[3940,13,3941,4076,3141,468,0]An[3940,13,3941,4076,3329,468,0]An[3940,13,3941,4076,4392,468,0]An[3940,13,3941,1549,4161,468,0]An[3940,13,3941,1089,3998,468,0]An[3940,13,3941,4081,3333,468,0]An[3940,13,3941,4081,3347,468,0]An[3940,13,3941,4081,4160,468,0]Ak[3940,13,3941,0,3324,468,0]Ak[3940,13,3941,0,4286,468,0]Ak[3940,13,3941,0,4505,468,0]Ak[3940,13,3941,0,4138,468,0]Ak[3940,13,3941,0,4383,468,0]Ak[3940,13,3941,0,1600,468,0]Ak[3940,13,3941,0,4928,468,0]Ak[3940,13,3941,0,1321,468,0]Ak[3940,13,3941,0,1583,468,0]Ak[3940,13,3941,0,2427,468,0]Ak[3940,13,3941,0,3308,468,0]Ak[3940,13,3941,0,3138,468,0]Ak[3940,13,3941,0,4491,468,0]Ak[3940,13,3941,0,2011,468,0]Ak[3940,13,3941,0,4112,468,0]Ak[3940,13,3941,0,4487,468,0]Ak[3940,13,3941,0,4501,468,0]Ak[3940,13,3941,0,4508,468,0]Ak[3940,13,3941,0,4793,468,0]Ak[3940,13,3941,0,4808,468,0]Ak[3940,13,3941,0,3985,468,0]Ak[3940,13,3941,0,4369,468,0]Ak[3940,13,3941,0,4154,468,0]Ak[3940,13,3941,0,4985,468,0]Ak[3940,13,3941,0,4106,468,0]Ak[3940,13,3941,0,3975,468,0]Ak[3940,13,3941,0,3121,468,0]Ak[3940,13,3941,0,4484,468,0]Ak[3940,13,3941,0,4672,468,0]Ak[3940,13,3941,0,4118,468,0]Ak[3940,13,3941,0,4121,468,0]Ak[3940,13,3941,0,4126,468,0]Ak[3940,13,3941,0,4827,468,0]Ak[3940,13,3941,0,3311,468,0]Ak[3940,13,3941,0,4703,468,0]Aj[3940,13,3941,0,482,468,0]Aj[3940,13,3941,0,117,468,0]Ak[3940,13,3941,0,2415,468,0]Ak[3940,13,3941,0,2424,468,0]Aj[3940,13,3699,0,111,468,0]Ak[3940,13,3699,0,3109,468,0]Ak[3940,13,3699,0,1325,468,0]Ak[3940,13,3699,0,3980,468,0]Ak[3940,13,3699,0,1985,468,0]Ak[3940,13,3699,0,4274,468,0]Ak[3940,13,3699,0,4761,468,0]Ak[3940,13,1841,0,1980,468,0]Aj[3940,13,1841,0,469,468,0]Ak[3940,13,1841,0,5074,468,0]Ak[3940,13,1841,0,3978,468,0]Ak[3940,13,1841,0,4831,468,0]Ak[3940,13,1920,0,3342,468,0]Ak[3940,13,1920,0,4257,468,0]Ak[3940,13,1920,0,4699,468,0]Ak[3940,13,1920,0,4701,468,0]Ak[3940,13,1920,0,4924,468,0]Ak[3940,13,1920,0,1338,468,0]An[4727,13,4728,4518,4247,468,0]An[4727,13,4728,3166,3116,468,0]An[4727,13,4728,1072,1563,468,0]An[4727,13,4728,2145,2005,468,0]An[4727,13,4728,2145,3328,468,0]An[4727,13,4728,2145,3334,468,0]An[4727,13,4728,3705,3303,468,0]An[4727,13,4728,1068,3325,468,0]An[4727,13,4728,1839,4287,468,0]An[4727,13,4728,2438,2393,468,0]An[4727,13,4728,2438,4253,468,0]An[4727,13,4728,1904,5133,468,0]An[4727,13,4728,4295,5103,468,0]An[4727,13,4728,1548,4506,468,0]An[4727,13,4728,1548,4139,468,0]An[4727,13,4728,1548,4384,468,0]An[4727,13,4728,1953,1601,468,0]An[4727,13,4728,1953,4929,468,0]An[4727,13,4728,3157,1322,468,0]An[4727,13,4728,3166,4770,468,0]An[4727,13,4728,1523,1584,468,0]An[4727,13,4728,1523,4150,468,0]An[4727,13,4728,1523,3323,468,0]An[4727,13,4728,1523,3137,468,0]An[4727,13,4728,1523,4824,468,0]An[4727,13,4728,1523,3298,468,0]An[4727,13,4728,1538,2428,468,0]An[4727,13,4728,1538,3309,468,0]An[4727,13,4728,1538,3139,468,0]An[4727,13,4728,1538,4492,468,0]An[4727,13,4728,1072,3142,468,0]An[4727,13,4728,1072,4282,468,0]An[4727,13,4728,1077,2012,468,0]An[4727,13,4728,1077,3348,468,0]An[4727,13,4728,1077,3984,468,0]An[4727,13,4728,2145,4002,468,0]An[4727,13,4728,2145,4394,468,0]An[4727,13,4728,2145,4116,468,0]An[4727,13,4728,3705,4488,468,0]An[4727,13,4728,3705,3134,468,0]An[4727,13,4728,3705,4360,468,0]An[4727,13,4728,3705,5012,468,0]An[4727,13,4728,3705,1343,468,0]An[4727,13,4728,1068,4502,468,0]An[4727,13,4728,3285,4509,468,0]An[4727,13,4728,3285,4794,468,0]An[4727,13,4728,3285,4809,468,0]An[4727,13,4728,3285,3986,468,0]An[4727,13,4728,1839,4370,468,0]An[4727,13,4728,1839,4156,468,0]An[4727,13,4728,1839,4986,468,0]An[4727,13,4728,1839,4108,468,0]An[4727,13,4728,1839,3976,468,0]An[4727,13,4728,1513,3122,468,0]An[4727,13,4728,1513,4375,468,0]An[4727,13,4728,1513,3315,468,0]An[4727,13,4728,1513,3337,468,0]An[4727,13,4728,1513,4123,468,0]An[4727,13,4728,1513,4486,468,0]An[4727,13,4728,1513,4674,468,0]An[4727,13,4728,1513,2386,468,0]An[4727,13,4728,2438,4678,468,0]An[4727,13,4728,2438,2397,468,0]An[4727,13,4728,2438,4119,468,0]An[4727,13,4728,2438,3970,468,0]An[4727,13,4728,2438,3331,468,0]An[4727,13,4728,1904,4277,468,0]An[4727,13,4728,1904,4500,468,0]An[4727,13,4728,1904,5015,468,0]An[4727,13,4728,1904,2399,468,0]An[4727,13,4728,1904,4768,468,0]An[4727,13,4728,1931,4490,468,0]An[4727,13,4728,1931,4254,468,0]An[4727,13,4728,1931,4122,468,0]An[4727,13,4728,1931,4127,468,0]Ak[4727,13,4728,0,4398,468,0]Ak[4727,13,4728,0,4828,468,0]An[4727,13,4728,4295,3312,468,0]An[4727,13,4728,4295,5108,468,0]An[4727,13,4728,4858,4704,468,0]Am[4727,13,4728,4728,483,468,0]Am[4727,13,4728,4728,118,468,0]An[4727,13,4728,4728,2416,468,0]An[4727,13,4728,4728,2425,468,0]An[4727,13,4728,4728,3120,468,0]An[4727,13,1837,1837,4288,468,0]An[4727,13,4970,4970,4869,468,0]An[4727,13,4970,4970,1340,468,0]An[4727,13,1842,1842,1981,468,0]Am[4727,13,1842,1842,470,468,0]An[4727,13,1842,1842,5075,468,0]An[4727,13,1842,1842,3979,468,0]An[4727,13,1842,1842,4834,468,0]An[4727,13,1921,1921,3343,468,0]An[4727,13,1921,1921,4258,468,0]An[4727,13,1921,1921,4700,468,0]An[4727,13,1921,1921,4702,468,0]An[4727,13,1921,1921,4925,468,0]Ak[4727,13,1921,0,1339,468,0]Al[4727,13,3700,133,112,468,0]Am[4727,13,3700,133,3110,468,0]An[4727,13,3700,1508,1326,468,0]An[4727,13,3700,1508,3981,468,0]An[4727,13,3700,2486,1986,468,0]An[4727,13,3700,2486,4275,468,0]An[4727,13,3700,3700,4762,468,0]An[4727,13,2116,2116,1341,468,0]An[4727,13,2116,2116,1344,468,0]An[4433,13,4434,4434,3346,468,0]Am[4433,13,4434,390,4399,468,0]An[4433,13,4434,4434,2413,468,0]An[4433,13,4434,4434,2426,468,0]An[4433,13,4434,4434,4128,468,0]An[1047,13,1048,1048,3113,468,0]An[1047,13,1048,1048,4862,468,0]Am[1047,13,1831,1831,126,468,0]An[1047,13,1539,1539,1359,468,0]An[1047,13,1539,1539,4004,468,0]An[1047,13,1539,1539,4496,468,0]An[1047,13,4213,4213,5060,468,0]An[1047,13,4213,4213,5061,468,0]Am[1047,13,1926,1926,127,468,0]Am[3960,13,3961,3961,116,468,0]An[3960,13,3961,3961,2383,468,0]Ac[492,7,493,0,0,0,0]Ae[4009,7,4010,0,0,0,0]Ae[4298,7,4299,0,0,0,0]Ae[3960,7,3961,0,0,0,0]Ae[4788,7,4789,0,0,0,0]Al[4727,13,4728,1072,1563,0,0]Ai[3940,12,1841,0,1964,0,0]Al[4727,13,4728,2145,2005,0,0]Al[4727,12,1842,1842,1965,0,0]Al[3940,13,3941,3704,3302,0,0]Ab[1047,3,0,0,0,0,0]Ak[3940,13,3941,1046,475,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,13,4728,3705,3303,0,0]Al[4727,14,4728,1538,3309,0,0]Al[3940,12,3941,2137,3291,0,0]Ao[3940,13,3941,3704,4513,1573,0]Ao[3940,13,3941,3704,2406,1573,0]Ao[3940,13,3941,1046,2407,1573,0]Ao[3940,13,3941,1046,3112,1573,0]Ao[3940,13,3941,3704,3300,1573,0]Ao[3940,13,3941,3704,1988,1573,0]Ao[3940,13,3941,3704,1576,1573,0]Ao[3940,13,3941,3704,3102,1573,0]Ao[3940,13,3941,3704,2380,1573,0]Ao[3940,13,3941,3704,4146,1573,0]Ao[3940,13,3941,3704,4151,1573,0]Ao[3940,13,3941,2437,1555,1573,0]Ao[3940,13,3941,2464,4374,1573,0]?Al[3940,13,3941,4076,3327,0,0]Al[4727,13,4728,2145,3328,0,0]Al[4727,13,4970,4970,4869,0,0]Al[4727,13,4728,3166,3116,0,0]Ak[4433,13,4434,390,4399,0,0]Ai[3940,12,1841,0,1964,0,0]Al[4727,12,1842,1842,1965,0,0]32Ai[3940,13,3941,0,4985,0,0]Al[4727,13,4728,1839,4986,0,0]Al[3940,13,3941,3704,3302,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,12,1841,0,1976,0,0]Al[4727,12,1842,1842,1977,0,0]Al[1362,14,1537,1537,4395,0,0]Ai[3940,14,3941,0,2424,0,0]Al[4727,14,4728,4728,2425,0,0]Ai[4727,14,4499,0,1355,0,0]Al[3940,13,3941,4076,3327,0,0]Al[3940,13,3941,4076,4870,0,0]Al[4727,13,4728,2145,3328,0,0]Ai[3940,12,3941,0,3344,0,0]Al[3940,13,3941,0,4383,3344,0]Al[3940,13,3941,0,4703,3344,0]Al[4727,12,4728,1548,3345,0,0]Ao[4727,13,4728,1548,4384,3345,0]Ao[4727,13,4728,4858,4704,3345,0]Am[1362,13,1363,1363,125,485,0]An[1362,13,1363,1363,3972,485,0]An[1362,13,1537,1537,4003,485,0]An[1362,13,1537,1537,1358,485,0]An[1362,13,1537,1537,4395,485,0]Ak[492,13,493,493,3973,485,0]An[4009,13,4010,4010,5016,485,0]An[3940,13,3941,3704,4513,485,0]An[3940,13,3941,3704,2406,485,0]Ak[3940,13,3941,0,4246,485,0]An[3940,13,3941,1046,2407,485,0]An[3940,13,3941,1046,3112,485,0]An[3940,13,3941,1046,4670,485,0]An[3940,13,3941,3704,3300,485,0]An[3940,13,3941,3704,3302,485,0]An[3940,13,3941,3704,1988,485,0]An[3940,13,3941,3704,1576,485,0]An[3940,13,3941,3704,3102,485,0]An[3940,13,3941,3704,2380,485,0]An[3940,13,3941,3704,4146,485,0]An[3940,13,3941,3704,4151,485,0]An[3940,13,3941,3704,4931,485,0]An[3940,13,3941,3704,4769,485,0]An[3940,13,3941,2437,2392,485,0]An[3940,13,3941,2437,4255,485,0]An[3940,13,3941,2437,1555,485,0]An[3940,13,3941,2464,2396,485,0]An[3940,13,3941,2464,3330,485,0]An[3940,13,3941,2464,4374,485,0]Am[3940,13,3941,1046,475,485,0]An[3940,13,3941,4076,3327,485,0]An[3940,13,3941,4076,4870,485,0]An[3940,13,3941,4076,4393,485,0]An[3940,13,3941,4076,3141,485,0]An[3940,13,3941,4076,3329,485,0]An[3940,13,3941,4076,4392,485,0]An[3940,13,3941,1549,4161,485,0]An[3940,13,3941,1089,3998,485,0]An[3940,13,3941,4081,3333,485,0]An[3940,13,3941,4081,3347,485,0]An[3940,13,3941,4081,4160,485,0]Ak[3940,13,3941,0,3324,485,0]Ak[3940,13,3941,0,4286,485,0]Ak[3940,13,3941,0,4505,485,0]Ak[3940,13,3941,0,4138,485,0]Ak[3940,13,3941,0,4383,485,0]Ak[3940,13,3941,0,1600,485,0]Ak[3940,13,3941,0,4928,485,0]Ak[3940,13,3941,0,1321,485,0]Ak[3940,13,3941,0,1583,485,0]Ak[3940,13,3941,0,2427,485,0]Ak[3940,13,3941,0,3308,485,0]Ak[3940,13,3941,0,3138,485,0]Ak[3940,13,3941,0,4491,485,0]Ak[3940,13,3941,0,2011,485,0]Ak[3940,13,3941,0,4112,485,0]Ak[3940,13,3941,0,4487,485,0]Ak[3940,13,3941,0,4501,485,0]Ak[3940,13,3941,0,4508,485,0]Ak[3940,13,3941,0,4793,485,0]Ak[3940,13,3941,0,4808,485,0]Ak[3940,13,3941,0,3985,485,0]Ak[3940,13,3941,0,4369,485,0]Ak[3940,13,3941,0,4154,485,0]Ak[3940,13,3941,0,4985,485,0]Ak[3940,13,3941,0,4106,485,0]Ak[3940,13,3941,0,3975,485,0]Ak[3940,13,3941,0,3121,485,0]Ak[3940,13,3941,0,4484,485,0]Ak[3940,13,3941,0,4672,485,0]Ak[3940,13,3941,0,4118,485,0]Ak[3940,13,3941,0,4121,485,0]Ak[3940,13,3941,0,4126,485,0]Ak[3940,13,3941,0,4827,485,0]Ak[3940,13,3941,0,3311,485,0]Ak[3940,13,3941,0,4703,485,0]Aj[3940,13,3941,0,482,485,0]Aj[3940,13,3941,0,117,485,0]Ak[3940,13,3941,0,2415,485,0]Ak[3940,13,3941,0,2424,485,0]Aj[3940,13,3699,0,111,485,0]Ak[3940,13,3699,0,3109,485,0]Ak[3940,13,3699,0,1325,485,0]Ak[3940,13,3699,0,3980,485,0]Ak[3940,13,3699,0,1985,485,0]Ak[3940,13,3699,0,4274,485,0]Ak[3940,13,3699,0,4761,485,0]Ak[3940,13,1841,0,1980,485,0]Aj[3940,13,1841,0,469,485,0]Ak[3940,13,1841,0,5074,485,0]Ak[3940,13,1841,0,3978,485,0]Ak[3940,13,1841,0,4831,485,0]Ak[3940,13,1920,0,3342,485,0]Ak[3940,13,1920,0,4257,485,0]Ak[3940,13,1920,0,4699,485,0]Ak[3940,13,1920,0,4701,485,0]Ak[3940,13,1920,0,4924,485,0]Ak[3940,13,1920,0,1338,485,0]An[4727,13,4728,4518,4247,485,0]An[4727,13,4728,3166,3116,485,0]An[4727,13,4728,1072,1563,485,0]An[4727,13,4728,2145,2005,485,0]An[4727,13,4728,2145,3328,485,0]An[4727,13,4728,2145,3334,485,0]An[4727,13,4728,3705,3303,485,0]An[4727,13,4728,1068,3325,485,0]An[4727,13,4728,1839,4287,485,0]An[4727,13,4728,2438,2393,485,0]An[4727,13,4728,2438,4253,485,0]An[4727,13,4728,1904,5133,485,0]An[4727,13,4728,4295,5103,485,0]An[4727,13,4728,1548,4506,485,0]An[4727,13,4728,1548,4139,485,0]An[4727,13,4728,1548,4384,485,0]An[4727,13,4728,1953,1601,485,0]An[4727,13,4728,1953,4929,485,0]An[4727,13,4728,3157,1322,485,0]An[4727,13,4728,3166,4770,485,0]An[4727,13,4728,1523,1584,485,0]An[4727,13,4728,1523,4150,485,0]An[4727,13,4728,1523,3323,485,0]An[4727,13,4728,1523,3137,485,0]An[4727,13,4728,1523,4824,485,0]An[4727,13,4728,1523,3298,485,0]An[4727,13,4728,1538,2428,485,0]An[4727,13,4728,1538,3309,485,0]An[4727,13,4728,1538,3139,485,0]An[4727,13,4728,1538,4492,485,0]An[4727,13,4728,1072,3142,485,0]An[4727,13,4728,1072,4282,485,0]An[4727,13,4728,1077,2012,485,0]An[4727,13,4728,1077,3348,485,0]An[4727,13,4728,1077,3984,485,0]An[4727,13,4728,2145,4002,485,0]An[4727,13,4728,2145,4394,485,0]An[4727,13,4728,2145,4116,485,0]An[4727,13,4728,3705,4488,485,0]An[4727,13,4728,3705,3134,485,0]An[4727,13,4728,3705,4360,485,0]An[4727,13,4728,3705,5012,485,0]An[4727,13,4728,3705,1343,485,0]An[4727,13,4728,1068,4502,485,0]An[4727,13,4728,3285,4509,485,0]An[4727,13,4728,3285,4794,485,0]An[4727,13,4728,3285,4809,485,0]An[4727,13,4728,3285,3986,485,0]An[4727,13,4728,1839,4370,485,0]An[4727,13,4728,1839,4156,485,0]An[4727,13,4728,1839,4986,485,0]An[4727,13,4728,1839,4108,485,0]An[4727,13,4728,1839,3976,485,0]An[4727,13,4728,1513,3122,485,0]An[4727,13,4728,1513,4375,485,0]An[4727,13,4728,1513,3315,485,0]An[4727,13,4728,1513,3337,485,0]An[4727,13,4728,1513,4123,485,0]An[4727,13,4728,1513,4486,485,0]An[4727,13,4728,1513,4674,485,0]An[4727,13,4728,1513,2386,485,0]An[4727,13,4728,2438,4678,485,0]An[4727,13,4728,2438,2397,485,0]An[4727,13,4728,2438,4119,485,0]An[4727,13,4728,2438,3970,485,0]An[4727,13,4728,2438,3331,485,0]An[4727,13,4728,1904,4277,485,0]An[4727,13,4728,1904,4500,485,0]An[4727,13,4728,1904,5015,485,0]An[4727,13,4728,1904,2399,485,0]An[4727,13,4728,1904,4768,485,0]An[4727,13,4728,1931,4490,485,0]An[4727,13,4728,1931,4254,485,0]An[4727,13,4728,1931,4122,485,0]An[4727,13,4728,1931,4127,485,0]Ak[4727,13,4728,0,4398,485,0]Ak[4727,13,4728,0,4828,485,0]An[4727,13,4728,4295,3312,485,0]An[4727,13,4728,4295,5108,485,0]An[4727,13,4728,4858,4704,485,0]Am[4727,13,4728,4728,483,485,0]Am[4727,13,4728,4728,118,485,0]An[4727,13,4728,4728,2416,485,0]An[4727,13,4728,4728,2425,485,0]An[4727,13,4728,4728,3120,485,0]An[4727,13,1837,1837,4288,485,0]An[4727,13,4970,4970,4869,485,0]An[4727,13,4970,4970,1340,485,0]An[4727,13,1842,1842,1981,485,0]Am[4727,13,1842,1842,470,485,0]An[4727,13,1842,1842,5075,485,0]An[4727,13,1842,1842,3979,485,0]An[4727,13,1842,1842,4834,485,0]An[4727,13,1921,1921,3343,485,0]An[4727,13,1921,1921,4258,485,0]An[4727,13,1921,1921,4700,485,0]An[4727,13,1921,1921,4702,485,0]An[4727,13,1921,1921,4925,485,0]Ak[4727,13,1921,0,1339,485,0]Al[4727,13,3700,133,112,485,0]Am[4727,13,3700,133,3110,485,0]An[4727,13,3700,1508,1326,485,0]An[4727,13,3700,1508,3981,485,0]An[4727,13,3700,2486,1986,485,0]An[4727,13,3700,2486,4275,485,0]An[4727,13,3700,3700,4762,485,0]An[4727,13,2116,2116,1341,485,0]An[4727,13,2116,2116,1344,485,0]An[1047,13,1048,1048,3113,485,0]An[1047,13,1048,1048,4862,485,0]Am[1047,13,1831,1831,126,485,0]An[1047,13,1539,1539,1359,485,0]An[1047,13,1539,1539,4004,485,0]An[1047,13,1539,1539,4496,485,0]An[1047,13,4213,4213,5060,485,0]An[1047,13,4213,4213,5061,485,0]Am[1047,13,1926,1926,127,485,0]Am[3960,13,3961,3961,116,485,0]An[3960,13,3961,3961,2383,485,0]Ah[3940,5,3941,3157,0,0,0]Ah[4727,5,4728,3157,0,0,0]Ah[3940,5,3699,1508,0,0,0]Ah[4727,5,3700,1508,0,0,0]Al[1047,15,1048,1048,4862,0,0]Ah[3940,6,1920,1511,0,0,0]Ah[4727,6,1921,1511,0,0,0]Ae[4727,6,4970,0,0,0,0]Ae[4727,6,2116,0,0,0,0]Ah[4727,6,4728,3705,0,0,0]1Al[4727,15,4728,1513,3315,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]Al[3940,15,3941,4076,3141,0,0]Ai[3940,15,3941,0,4487,0,0]Al[4727,15,4728,1072,3142,0,0]Al[4727,15,4728,3705,4488,0,0]Al[4727,15,4728,1904,4500,0,0]0Ae[1362,5,1537,0,0,0,0]Ae[1047,5,1539,0,0,0,0]Ab[1362,3,0,0,0,0,0]Ai[3940,12,3699,0,4759,0,0]Al[3940,13,3699,0,3109,4759,0]Al[3940,13,3699,0,3980,4759,0]Al[3940,13,3699,0,4274,4759,0]Al[4727,12,3700,3700,4760,0,0]An[4727,13,3700,133,3110,4760,0]Ao[4727,13,3700,1508,3981,4760,0]Ao[4727,13,3700,2486,4275,4760,0]Al[1047,13,1048,1048,4862,0,0]Ai[3940,14,3941,0,1321,0,0]Ai[3940,14,3941,0,2427,0,0]Ai[3940,14,3941,0,2011,0,0]Al[4727,14,4728,3157,1322,0,0]Al[4727,14,4728,1538,2428,0,0]Al[4727,14,4728,1077,2012,0,0]Al[4727,14,4728,2438,3970,0,0]An[1362,13,1363,1363,125,1323,0]Ao[1362,13,1363,1363,3972,1323,0]Ao[3940,13,3941,3704,3302,1323,0]Ao[3940,13,3941,3704,4931,1323,0]Ao[3940,13,3941,3704,4769,1323,0]Ao[3940,13,3941,2437,2392,1323,0]Ao[3940,13,3941,2437,4255,1323,0]Ao[3940,13,3941,4076,4393,1323,0]Ao[3940,13,3941,4076,3141,1323,0]Ao[3940,13,3941,4076,3329,1323,0]Ao[3940,13,3941,4076,4392,1323,0]Ao[3940,13,3941,4081,3347,1323,0]Ao[3940,13,3941,4081,4160,1323,0]Al[3940,13,3941,0,1600,1323,0]Al[3940,13,3941,0,4928,1323,0]Al[3940,13,3941,0,1583,1323,0]Al[3940,13,3941,0,2427,1323,0]Al[3940,13,3941,0,3308,1323,0]Al[3940,13,3941,0,3138,1323,0]Al[3940,13,3941,0,4491,1323,0]Al[3940,13,3941,0,2011,1323,0]Al[3940,13,3941,0,4112,1323,0]Al[3940,13,3941,0,4487,1323,0]Al[3940,13,3941,0,4508,1323,0]Al[3940,13,3941,0,4793,1323,0]Al[3940,13,3941,0,4808,1323,0]Al[3940,13,3941,0,3985,1323,0]Al[3940,13,3941,0,4369,1323,0]Al[3940,13,3941,0,4154,1323,0]Al[3940,13,3941,0,4985,1323,0]Al[3940,13,3941,0,4106,1323,0]Al[3940,13,3941,0,3975,1323,0]Al[3940,13,3941,0,3121,1323,0]Al[3940,13,3941,0,4484,1323,0]Al[3940,13,3941,0,4672,1323,0]Al[3940,13,3941,0,4118,1323,0]Al[3940,13,3941,0,4121,1323,0]Al[3940,13,3941,0,4126,1323,0]Al[3940,13,3941,0,4827,1323,0]Al[3940,13,3941,0,3311,1323,0]Ak[3940,13,3941,0,482,1323,0]Ak[3940,13,3941,0,117,1323,0]Al[3940,13,3941,0,2415,1323,0]Ak[3940,13,3699,0,111,1323,0]Al[3940,13,3699,0,3109,1323,0]Al[3940,13,3699,0,1325,1323,0]Al[3940,13,3699,0,3980,1323,0]Al[3940,13,3699,0,1985,1323,0]Al[3940,13,3699,0,4274,1323,0]Al[3940,13,1920,0,4924,1323,0]Al[3940,13,1920,0,1338,1323,0]Ao[4727,13,4728,1953,1601,1323,0]Ao[4727,13,4728,1953,4929,1323,0]Ao[4727,13,4728,3166,4770,1323,0]Ao[4727,13,4728,1523,1584,1323,0]Ao[4727,13,4728,1523,4150,1323,0]Ao[4727,13,4728,1523,3323,1323,0]Ao[4727,13,4728,1523,3137,1323,0]Ao[4727,13,4728,1523,4824,1323,0]Ao[4727,13,4728,1523,3298,1323,0]Ao[4727,13,4728,1538,2428,1323,0]Ao[4727,13,4728,1538,3309,1323,0]Ao[4727,13,4728,1538,3139,1323,0]Ao[4727,13,4728,1538,4492,1323,0]Ao[4727,13,4728,1072,3142,1323,0]Ao[4727,13,4728,1072,4282,1323,0]Ao[4727,13,4728,1077,2012,1323,0]Ao[4727,13,4728,1077,3348,1323,0]Ao[4727,13,4728,1077,3984,1323,0]Ao[4727,13,4728,2145,4394,1323,0]Ao[4727,13,4728,2145,4116,1323,0]Ao[4727,13,4728,3705,4488,1323,0]Ao[4727,13,4728,3705,3134,1323,0]Ao[4727,13,4728,3705,4360,1323,0]Ao[4727,13,4728,3705,5012,1323,0]Ao[4727,13,4728,3285,4509,1323,0]Ao[4727,13,4728,3285,4794,1323,0]Ao[4727,13,4728,3285,4809,1323,0]Ao[4727,13,4728,3285,3986,1323,0]Ao[4727,13,4728,1839,4370,1323,0]Ao[4727,13,4728,1839,4156,1323,0]Ao[4727,13,4728,1839,4986,1323,0]Ao[4727,13,4728,1839,4108,1323,0]Ao[4727,13,4728,1839,3976,1323,0]Ao[4727,13,4728,1513,3122,1323,0]Ao[4727,13,4728,1513,3315,1323,0]Ao[4727,13,4728,1513,3337,1323,0]Ao[4727,13,4728,1513,4123,1323,0]Ao[4727,13,4728,1513,4486,1323,0]Ao[4727,13,4728,1513,4674,1323,0]Ao[4727,13,4728,1513,2386,1323,0]Ao[4727,13,4728,2438,4678,1323,0]Ao[4727,13,4728,2438,4119,1323,0]Ao[4727,13,4728,1904,4500,1323,0]Ao[4727,13,4728,1904,5015,1323,0]Ao[4727,13,4728,1904,2399,1323,0]Ao[4727,13,4728,1904,4768,1323,0]Ao[4727,13,4728,1931,4122,1323,0]Ao[4727,13,4728,1931,4127,1323,0]Al[4727,13,4728,0,4398,1323,0]Al[4727,13,4728,0,4828,1323,0]Ao[4727,13,4728,4295,3312,1323,0]Ao[4727,13,4728,4295,5108,1323,0]An[4727,13,4728,4728,483,1323,0]An[4727,13,4728,4728,118,1323,0]Ao[4727,13,4728,4728,2416,1323,0]Ao[4727,13,4728,4728,3120,1323,0]Ao[4727,13,1837,1837,4288,1323,0]Ao[4727,13,4970,4970,4869,1323,0]Ao[4727,13,4970,4970,1340,1323,0]Ao[4727,13,1921,1921,4925,1323,0]Al[4727,13,1921,0,1339,1323,0]Am[4727,13,3700,133,112,1323,0]An[4727,13,3700,133,3110,1323,0]Ao[4727,13,3700,1508,1326,1323,0]Ao[4727,13,3700,1508,3981,1323,0]Ao[4727,13,3700,2486,1986,1323,0]Ao[4727,13,3700,2486,4275,1323,0]Ao[4727,13,2116,2116,1344,1323,0]Ao[4433,13,4434,4434,2413,1323,0]Ao[4433,13,4434,4434,2426,1323,0]Ao[4433,13,4434,4434,4128,1323,0]Ao[1047,13,1048,1048,4862,1323,0]Ao[1047,13,1539,1539,4496,1323,0]Al[3940,13,3941,3704,3302,0,0]Al[3940,13,3941,2437,2392,0,0]Ak[3940,13,3941,1046,475,0,0]Al[4727,13,4728,3705,3303,0,0]Al[3940,13,3941,0,4121,1329,0]Ao[4727,13,4728,1931,4122,1329,0]Ai[4727,14,3135,0,4265,0,0]Ai[4727,14,4728,0,4398,0,0]Ai[3940,13,3941,0,2415,0,0]Al[4727,13,4728,4728,2416,0,0]Ai[3940,13,3941,0,4126,0,0]Al[4727,13,4728,1931,4127,0,0]Ai[3940,14,1841,0,1980,0,0]Al[4727,14,1842,1842,1981,0,0]Al[4727,12,4728,1904,4799,0,0]Ao[4727,13,4728,1904,5133,4799,0]=Al[4727,13,4970,4970,4869,0,0]Ah[3940,5,3941,2437,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Al[1362,15,1363,1363,3972,0,0]0Al[4727,15,4728,1523,3323,0,0]Ah[4727,5,4728,1072,0,0,0]Ah[3940,15,3941,0,482,0,0]Ak[4727,15,4728,4728,483,0,0]Al[3960,15,3961,3961,2383,0,0]Al[4727,15,4728,1523,3137,0,0]Al[4727,15,2116,2116,1341,0,0]98Ah[3940,5,3941,3704,0,0,0]2Al[4727,15,4728,2145,4002,0,0]Ah[3940,5,3941,1523,0,0,0]Ah[4727,5,4728,1523,0,0,0]Ae[4727,8,1837,0,0,0,0]987>Al[4727,15,4728,3705,1343,0,0]>Ah[3940,5,3941,1953,0,0,0]Ah[4727,5,4728,1953,0,0,0]Al[3940,13,3941,2437,2392,0,0]Al[3940,13,3941,2464,2396,0,0]Al[4727,13,4728,2145,2005,0,0]Al[4727,13,4728,2438,2393,0,0]An[1362,13,1363,1363,125,1561,0]Ao[1362,13,1363,1363,3972,1561,0]Ao[1362,13,1537,1537,4003,1561,0]Ao[1362,13,1537,1537,1358,1561,0]Ao[1362,13,1537,1537,4395,1561,0]Al[492,13,493,493,3973,1561,0]Ao[4009,13,4010,4010,5016,1561,0]Ao[3940,13,3941,3704,4513,1561,0]Ao[3940,13,3941,3704,2406,1561,0]Al[3940,13,3941,0,4246,1561,0]Ao[3940,13,3941,1046,2407,1561,0]Ao[3940,13,3941,1046,3112,1561,0]Ao[3940,13,3941,1046,4670,1561,0]Ao[3940,13,3941,3704,3300,1561,0]Ao[3940,13,3941,3704,3302,1561,0]Ao[3940,13,3941,3704,1988,1561,0]Ao[3940,13,3941,3704,1576,1561,0]Ao[3940,13,3941,3704,3102,1561,0]Ao[3940,13,3941,3704,2380,1561,0]Ao[3940,13,3941,3704,4146,1561,0]Ao[3940,13,3941,3704,4151,1561,0]Ao[3940,13,3941,3704,4931,1561,0]Ao[3940,13,3941,3704,4769,1561,0]Ao[3940,13,3941,2437,2392,1561,0]Ao[3940,13,3941,2437,4255,1561,0]Ao[3940,13,3941,2437,1555,1561,0]Ao[3940,13,3941,2464,2396,1561,0]Ao[3940,13,3941,2464,3330,1561,0]Ao[3940,13,3941,2464,4374,1561,0]An[3940,13,3941,1046,475,1561,0]Ao[3940,13,3941,4076,3327,1561,0]Ao[3940,13,3941,4076,4870,1561,0]Ao[3940,13,3941,4076,4393,1561,0]Ao[3940,13,3941,4076,3141,1561,0]Ao[3940,13,3941,4076,3329,1561,0]Ao[3940,13,3941,4076,4392,1561,0]Ao[3940,13,3941,1549,4161,1561,0]Ao[3940,13,3941,1089,3998,1561,0]Ao[3940,13,3941,4081,3333,1561,0]Ao[3940,13,3941,4081,3347,1561,0]Ao[3940,13,3941,4081,4160,1561,0]Al[3940,13,3941,0,3324,1561,0]Al[3940,13,3941,0,4286,1561,0]Al[3940,13,3941,0,4505,1561,0]Al[3940,13,3941,0,4138,1561,0]Al[3940,13,3941,0,4383,1561,0]Al[3940,13,3941,0,1600,1561,0]Al[3940,13,3941,0,4928,1561,0]Al[3940,13,3941,0,1321,1561,0]Al[3940,13,3941,0,1583,1561,0]Al[3940,13,3941,0,2427,1561,0]Al[3940,13,3941,0,3308,1561,0]Al[3940,13,3941,0,3138,1561,0]Al[3940,13,3941,0,4491,1561,0]Al[3940,13,3941,0,2011,1561,0]Al[3940,13,3941,0,4112,1561,0]Al[3940,13,3941,0,4487,1561,0]Al[3940,13,3941,0,4501,1561,0]Al[3940,13,3941,0,4508,1561,0]Al[3940,13,3941,0,4793,1561,0]Al[3940,13,3941,0,4808,1561,0]Al[3940,13,3941,0,3985,1561,0]Al[3940,13,3941,0,4369,1561,0]Al[3940,13,3941,0,4154,1561,0]Al[3940,13,3941,0,4985,1561,0]Al[3940,13,3941,0,4106,1561,0]Al[3940,13,3941,0,3975,1561,0]Al[3940,13,3941,0,3121,1561,0]Al[3940,13,3941,0,4484,1561,0]Al[3940,13,3941,0,4672,1561,0]Al[3940,13,3941,0,4118,1561,0]Al[3940,13,3941,0,4121,1561,0]Al[3940,13,3941,0,4126,1561,0]Al[3940,13,3941,0,4827,1561,0]Al[3940,13,3941,0,3311,1561,0]Al[3940,13,3941,0,4703,1561,0]Ak[3940,13,3941,0,482,1561,0]Ak[3940,13,3941,0,117,1561,0]Al[3940,13,3941,0,2415,1561,0]Al[3940,13,3941,0,2424,1561,0]Ak[3940,13,3699,0,111,1561,0]Al[3940,13,3699,0,3109,1561,0]Al[3940,13,3699,0,1325,1561,0]Al[3940,13,3699,0,3980,1561,0]Al[3940,13,3699,0,1985,1561,0]Al[3940,13,3699,0,4274,1561,0]Al[3940,13,3699,0,4761,1561,0]Al[3940,13,1841,0,1980,1561,0]Ak[3940,13,1841,0,469,1561,0]Al[3940,13,1841,0,5074,1561,0]Al[3940,13,1841,0,3978,1561,0]Al[3940,13,1841,0,4831,1561,0]Al[3940,13,1920,0,3342,1561,0]Al[3940,13,1920,0,4257,1561,0]Al[3940,13,1920,0,4699,1561,0]Al[3940,13,1920,0,4701,1561,0]Al[3940,13,1920,0,4924,1561,0]Al[3940,13,1920,0,1338,1561,0]Ao[4727,13,4728,4518,4247,1561,0]Ao[4727,13,4728,3166,3116,1561,0]Ao[4727,13,4728,1072,1563,1561,0]Ao[4727,13,4728,2145,2005,1561,0]Ao[4727,13,4728,2145,3328,1561,0]Ao[4727,13,4728,2145,3334,1561,0]Ao[4727,13,4728,3705,3303,1561,0]Ao[4727,13,4728,1068,3325,1561,0]Ao[4727,13,4728,1839,4287,1561,0]Ao[4727,13,4728,2438,2393,1561,0]Ao[4727,13,4728,2438,4253,1561,0]Ao[4727,13,4728,1904,5133,1561,0]Ao[4727,13,4728,4295,5103,1561,0]Ao[4727,13,4728,1548,4506,1561,0]Ao[4727,13,4728,1548,4139,1561,0]Ao[4727,13,4728,1548,4384,1561,0]Ao[4727,13,4728,1953,1601,1561,0]Ao[4727,13,4728,1953,4929,1561,0]Ao[4727,13,4728,3157,1322,1561,0]Ao[4727,13,4728,3166,4770,1561,0]Ao[4727,13,4728,1523,1584,1561,0]Ao[4727,13,4728,1523,4150,1561,0]Ao[4727,13,4728,1523,3323,1561,0]Ao[4727,13,4728,1523,3137,1561,0]Ao[4727,13,4728,1523,4824,1561,0]Ao[4727,13,4728,1523,3298,1561,0]Ao[4727,13,4728,1538,2428,1561,0]Ao[4727,13,4728,1538,3309,1561,0]Ao[4727,13,4728,1538,3139,1561,0]Ao[4727,13,4728,1538,4492,1561,0]Ao[4727,13,4728,1072,3142,1561,0]Ao[4727,13,4728,1072,4282,1561,0]Ao[4727,13,4728,1077,2012,1561,0]Ao[4727,13,4728,1077,3348,1561,0]Ao[4727,13,4728,1077,3984,1561,0]Ao[4727,13,4728,2145,4002,1561,0]Ao[4727,13,4728,2145,4394,1561,0]Ao[4727,13,4728,2145,4116,1561,0]Ao[4727,13,4728,3705,4488,1561,0]Ao[4727,13,4728,3705,3134,1561,0]Ao[4727,13,4728,3705,4360,1561,0]Ao[4727,13,4728,3705,5012,1561,0]Ao[4727,13,4728,3705,1343,1561,0]Ao[4727,13,4728,1068,4502,1561,0]Ao[4727,13,4728,3285,4509,1561,0]Ao[4727,13,4728,3285,4794,1561,0]Ao[4727,13,4728,3285,4809,1561,0]Ao[4727,13,4728,3285,3986,1561,0]Ao[4727,13,4728,1839,4370,1561,0]Ao[4727,13,4728,1839,4156,1561,0]Ao[4727,13,4728,1839,4986,1561,0]Ao[4727,13,4728,1839,4108,1561,0]Ao[4727,13,4728,1839,3976,1561,0]Ao[4727,13,4728,1513,3122,1561,0]Ao[4727,13,4728,1513,4375,1561,0]Ao[4727,13,4728,1513,3315,1561,0]Ao[4727,13,4728,1513,3337,1561,0]Ao[4727,13,4728,1513,4123,1561,0]Ao[4727,13,4728,1513,4486,1561,0]Ao[4727,13,4728,1513,4674,1561,0]Ao[4727,13,4728,1513,2386,1561,0]Ao[4727,13,4728,2438,4678,1561,0]Ao[4727,13,4728,2438,2397,1561,0]Ao[4727,13,4728,2438,4119,1561,0]Ao[4727,13,4728,2438,3970,1561,0]Ao[4727,13,4728,2438,3331,1561,0]Ao[4727,13,4728,1904,4277,1561,0]Ao[4727,13,4728,1904,4500,1561,0]Ao[4727,13,4728,1904,5015,1561,0]Ao[4727,13,4728,1904,2399,1561,0]Ao[4727,13,4728,1904,4768,1561,0]Ao[4727,13,4728,1931,4490,1561,0]Ao[4727,13,4728,1931,4254,1561,0]Ao[4727,13,4728,1931,4122,1561,0]Ao[4727,13,4728,1931,4127,1561,0]Al[4727,13,4728,0,4398,1561,0]Al[4727,13,4728,0,4828,1561,0]Ao[4727,13,4728,4295,3312,1561,0]Ao[4727,13,4728,4295,5108,1561,0]Ao[4727,13,4728,4858,4704,1561,0]An[4727,13,4728,4728,483,1561,0]An[4727,13,4728,4728,118,1561,0]Ao[4727,13,4728,4728,2416,1561,0]Ao[4727,13,4728,4728,2425,1561,0]Ao[4727,13,4728,4728,3120,1561,0]Ao[4727,13,1837,1837,4288,1561,0]Ao[4727,13,4970,4970,4869,1561,0]Ao[4727,13,4970,4970,1340,1561,0]Ao[4727,13,1842,1842,1981,1561,0]An[4727,13,1842,1842,470,1561,0]Ao[4727,13,1842,1842,5075,1561,0]Ao[4727,13,1842,1842,3979,1561,0]Ao[4727,13,1842,1842,4834,1561,0]Ao[4727,13,1921,1921,3343,1561,0]Ao[4727,13,1921,1921,4258,1561,0]Ao[4727,13,1921,1921,4700,1561,0]Ao[4727,13,1921,1921,4702,1561,0]Ao[4727,13,1921,1921,4925,1561,0]Al[4727,13,1921,0,1339,1561,0]Am[4727,13,3700,133,112,1561,0]An[4727,13,3700,133,3110,1561,0]Ao[4727,13,3700,1508,1326,1561,0]Ao[4727,13,3700,1508,3981,1561,0]Ao[4727,13,3700,2486,1986,1561,0]Ao[4727,13,3700,2486,4275,1561,0]Ao[4727,13,3700,3700,4762,1561,0]Ao[4727,13,2116,2116,1341,1561,0]Ao[4727,13,2116,2116,1344,1561,0]Ao[4433,13,4434,4434,3346,1561,0]An[4433,13,4434,390,4399,1561,0]Ao[4433,13,4434,4434,2413,1561,0]Ao[4433,13,4434,4434,2426,1561,0]Ao[4433,13,4434,4434,4128,1561,0]Ao[1047,13,1048,1048,3113,1561,0]Ao[1047,13,1048,1048,4862,1561,0]An[1047,13,1831,1831,126,1561,0]Ao[1047,13,1539,1539,1359,1561,0]Ao[1047,13,1539,1539,4004,1561,0]Ao[1047,13,1539,1539,4496,1561,0]Ao[1047,13,4213,4213,5060,1561,0]Ao[1047,13,4213,4213,5061,1561,0]An[1047,13,1926,1926,127,1561,0]An[3960,13,3961,3961,116,1561,0]Ao[3960,13,3961,3961,2383,1561,0]Ai[3940,14,3941,0,2011,0,0]Al[4727,14,4728,1077,2012,0,0]Ak[1362,14,1363,1363,125,0,0]Ac[492,7,493,0,0,0,0]Al[3940,13,3941,2437,2392,0,0]Ae[1047,2,1048,0,0,0,0]Ae[4788,7,4789,0,0,0,0]Al[3940,13,3941,3704,3302,0,0]Ae[4727,2,4728,0,0,0,0]Al[4727,13,4728,2438,2393,0,0]Ah[3940,2,3941,4728,0,0,0]Ai[3940,13,3941,0,4827,0,0]Ai[3940,14,3941,0,3308,0,0]Ai[3940,14,3941,0,4154,0,0]5Ai[4727,13,4728,0,4828,0,0]Al[4727,14,4728,1538,3309,0,0]Al[4727,14,4728,1839,4156,0,0]Ai[4727,14,4728,0,4398,0,0]Al[3940,13,3941,0,1600,3117,0]Al[3940,13,3941,0,1583,3117,0]Al[3940,13,3941,0,3121,3117,0]Al[3940,13,3941,0,4827,3117,0]Al[3940,13,3941,0,2415,3117,0]Ao[4727,13,4728,1953,1601,3117,0]Ao[4727,13,4728,1523,1584,3117,0]Ao[4727,13,4728,1513,3122,3117,0]Ao[4727,13,4728,1513,3315,3117,0]Al[4727,13,4728,1904,4500,0,0]Al[4727,13,4728,0,4828,3117,0]Ao[4727,13,4728,4728,2416,3117,0]Al[4727,12,1837,1837,3117,0,0]Al[4433,14,4434,4434,2413,0,0]Al[3940,13,3941,0,1600,3118,0]Al[3940,13,3941,0,1583,3118,0]Al[3940,13,3941,0,3121,3118,0]Al[3940,13,3941,0,4827,3118,0]Al[3940,13,3941,0,2415,3118,0]Ao[4727,13,4728,1953,1601,3118,0]Ao[4727,13,4728,1523,1584,3118,0]Ao[4727,13,4728,1513,3122,3118,0]Ao[4727,13,4728,1513,3315,3118,0]Ao[4727,13,4728,1513,2386,3118,0]Al[4727,13,4728,0,4828,3118,0]Ao[4727,13,4728,4728,2416,3118,0]Al[4727,12,1837,1837,3118,0,0]Al[3940,13,3941,4081,3333,0,0]Al[4727,13,4728,2145,3334,0,0]Al[1047,13,1539,1539,4004,0,0]Al[4727,13,4728,2438,2393,0,0]Ah[3940,14,1841,0,469,0,0]Ak[4727,14,1842,1842,470,0,0]Al[3940,13,3941,2464,2396,0,0]3Ai[3940,12,3941,0,3344,0,0]Al[3940,13,3941,0,4383,3344,0]Al[3940,13,3941,0,4703,3344,0]Al[4727,12,4728,1548,3345,0,0]Ao[4727,13,4728,1548,4384,3345,0]Ao[4727,13,4728,4858,4704,3345,0]Al[1047,14,1048,1048,3113,0,0]Ai[3940,14,3941,0,1321,0,0]Al[4727,14,4728,3157,1322,0,0]Ai[3940,13,3699,0,3304,0,0]Ak[3940,13,3699,0,111,3304,0]Al[3940,13,3699,0,1325,3304,0]Al[3940,13,3699,0,1985,3304,0]Al[4727,13,3700,3700,3305,0,0]Am[4727,13,3700,133,112,3305,0]Ao[4727,13,3700,1508,1326,3305,0]Ao[4727,13,3700,2486,1986,3305,0]Ah[3940,14,1841,0,469,0,0]Ak[4727,14,1842,1842,470,0,0]Ai[4727,14,4499,0,4707,0,0]Ai[3940,14,3941,0,4484,0,0]Ai[3940,14,3941,0,4672,0,0]Al[4727,14,4728,1513,4486,0,0]Al[4727,14,4728,1513,4674,0,0]Ai[4727,14,4728,0,4398,0,0]Ai[4727,14,1342,0,4362,0,0]Al[3940,13,3941,2437,2392,0,0]Al[3940,13,3941,2464,2396,0,0]Al[4727,13,4728,2438,2393,0,0]Ai[3940,14,1841,0,1980,0,0]Al[4727,14,4728,4728,3120,0,0]Al[4727,14,1842,1842,1981,0,0]Ae[3940,4,3941,0,0,0,0]Ah[3940,2,1841,1842,0,0,0]Ae[4727,4,4728,0,0,0,0]Ae[4727,2,1842,0,0,0,0]Ac[492,7,493,0,0,0,0]:Ae[1047,2,1048,0,0,0,0]Ae[4788,7,4789,0,0,0,0]Ao[3940,13,3941,3704,4769,1331,0]Ao[3940,13,3941,4076,4393,1331,0]Ao[3940,13,3941,4076,3141,1331,0]Ao[3940,13,3941,4076,3329,1331,0]Ao[3940,13,3941,4076,4392,1331,0]Ao[3940,13,3941,4081,3347,1331,0]Ao[3940,13,3941,4081,4160,1331,0]Al[3940,13,3941,0,4487,1331,0]Ao[4727,13,4728,3705,4488,1331,0]Ai[3940,14,3941,0,2424,0,0]Al[4727,14,4728,4728,2425,0,0]Al[4433,14,4434,4434,2426,0,0]Ak[3940,13,3941,1046,475,0,0]Ai[4727,14,3135,0,4147,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Al[4433,14,4434,4434,2413,0,0]Ai[3940,14,3941,0,4491,0,0]Al[4727,14,4728,1538,4492,0,0]10Ai[3940,12,1841,0,1976,0,0]Al[4727,12,1842,1842,1977,0,0]Ai[3940,12,3699,0,3304,0,0]Ak[3940,13,3699,0,111,3304,0]Al[3940,13,3699,0,1325,3304,0]Al[3940,13,3699,0,1985,3304,0]Al[4727,12,3700,3700,3305,0,0]Am[4727,13,3700,133,112,3305,0]Ao[4727,13,3700,1508,1326,3305,0]Ao[4727,13,3700,2486,1986,3305,0]Ai[3940,10,1841,1842,0,0,0]Af[4727,10,1842,0,0,0,0]Al[4727,15,4728,3166,4770,0,0]Al[4727,15,4728,1072,3142,0,0]Al[4727,15,4728,1077,3348,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]65Al[4727,15,4728,1904,4500,0,0]Ah[3940,5,1841,1842,0,0,0]Ae[4727,5,1842,0,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Ah[3940,5,3699,2486,0,0,0]Ah[4727,5,3700,2486,0,0,0]Al[1047,15,1048,1048,4862,0,0]Ah[3940,5,3941,3704,0,0,0]5454Ai[3940,10,3941,2137,0,0,0]Ai[3940,10,1841,1842,0,0,0]Af[4727,10,1842,0,0,0,0]Al[3940,15,3941,4081,3347,0,0]?Al[4727,15,4728,1077,3984,0,0]Al[4727,15,4728,2145,4002,0,0]Ah[4727,5,4728,2145,0,0,0]Al[3940,15,3941,4076,4393,0,0]Al[4727,15,4728,1513,3315,0,0]Ah[3940,5,3941,1077,0,0,0]Ah[4727,5,4728,1077,0,0,0]Al[4727,15,4728,1904,2399,0,0]Ai[3940,12,3941,0,4976,0,0]Al[4727,12,4728,4295,4977,0,0]Ao[4727,13,4728,4295,5103,4977,0]Ao[4727,13,4728,4295,5108,4977,0]Ak[1362,14,1363,1363,125,0,0]An[1362,13,1363,1363,125,4361,0]Ao[1047,13,1048,1048,3113,4361,0]An[1047,13,1831,1831,126,4361,0]An[1047,13,1926,1926,127,4361,0]An[3960,13,3961,3961,116,4361,0]Ak[3960,14,3961,3961,116,0,0]Al[3940,13,3941,0,1600,3299,0]Al[3940,13,3941,0,1583,3299,0]Al[3940,13,3941,0,3985,3299,0]Al[3940,13,3941,0,4121,3299,0]Ak[3940,13,3941,0,482,3299,0]Ak[3940,13,3941,0,117,3299,0]Al[3940,13,3941,0,2415,3299,0]Ao[4727,13,4728,1953,1601,3299,0]Ao[4727,13,4728,3166,4770,3299,0]Ao[4727,13,4728,1523,1584,3299,0]Ao[4727,13,4728,1072,3142,3299,0]Ao[4727,13,4728,1072,4282,3299,0]Ao[4727,13,4728,1077,3348,3299,0]Ao[4727,13,4728,3285,3986,3299,0]Ao[4727,13,4728,2438,4678,3299,0]Ao[4727,13,4728,1904,4768,3299,0]Ao[4727,13,4728,1931,4122,3299,0]An[4727,13,4728,4728,483,3299,0]An[4727,13,4728,4728,118,3299,0]Ao[4727,13,4728,4728,2416,3299,0]Al[3940,13,3941,2437,2392,0,0]Al[4727,13,4728,2438,2393,0,0]Ai[3940,12,1841,0,3131,0,0]Ai[3940,12,1841,0,3124,0,0]Al[4727,12,1842,1842,3132,0,0]Al[4727,12,1842,1842,3125,0,0]Ao[1362,13,1537,1537,4003,1971,0]Ao[1362,13,1537,1537,1358,1971,0]Al[492,13,493,493,3973,1971,0]Al[3940,13,3941,0,4246,1971,0]Ao[3940,13,3941,2437,4255,1971,0]Al[3940,13,3941,0,1600,1971,0]Al[3940,13,3941,0,1583,1971,0]Al[3940,13,3941,0,2427,1971,0]Al[3940,13,3941,0,3308,1971,0]Al[3940,13,3941,0,3138,1971,0]Al[3940,13,3941,0,4491,1971,0]Al[3940,13,3941,0,2011,1971,0]Al[3940,13,3941,0,4112,1971,0]Al[3940,13,3941,0,4508,1971,0]Al[3940,13,3941,0,4793,1971,0]Al[3940,13,3941,0,4808,1971,0]Al[3940,13,3941,0,3985,1971,0]Al[3940,13,3941,0,4369,1971,0]Al[3940,13,3941,0,4985,1971,0]Al[3940,13,3941,0,4703,1971,0]Al[3940,13,3699,0,3109,1971,0]Al[3940,13,3699,0,3980,1971,0]Al[3940,13,3699,0,4274,1971,0]Al[3940,13,3699,0,4761,1971,0]Ao[4727,13,4728,4518,4247,1971,0]Ao[4727,13,4728,4295,5103,1971,0]Ao[4727,13,4728,1953,1601,1971,0]Ao[4727,13,4728,1523,1584,1971,0]Ao[4727,13,4728,1523,3323,1971,0]Ao[4727,13,4728,1523,3298,1971,0]Ao[4727,13,4728,1538,2428,1971,0]Ao[4727,13,4728,1538,3309,1971,0]Ao[4727,13,4728,1538,3139,1971,0]Ao[4727,13,4728,1538,4492,1971,0]Ao[4727,13,4728,1077,2012,1971,0]Ao[4727,13,4728,2145,4394,1971,0]Ao[4727,13,4728,2145,4116,1971,0]Ao[4727,13,4728,3285,4509,1971,0]Ao[4727,13,4728,3285,4794,1971,0]Ao[4727,13,4728,3285,4809,1971,0]Ao[4727,13,4728,3285,3986,1971,0]Ao[4727,13,4728,1839,4370,1971,0]Ao[4727,13,4728,1839,4986,1971,0]Ao[4727,13,4728,4295,5108,1971,0]Ao[4727,13,4728,4858,4704,1971,0]An[4727,13,3700,133,3110,1971,0]Ao[4727,13,3700,1508,3981,1971,0]Ao[4727,13,3700,2486,4275,1971,0]Ao[4727,13,3700,3700,4762,1971,0]Ao[4433,13,4434,4434,2413,1971,0]Ao[1047,13,1539,1539,1359,1971,0]Ao[1047,13,1539,1539,4496,1971,0]Al[4727,13,4728,1931,4254,0,0]Al[4727,13,4728,1904,4277,0,0]Al[4727,13,4728,1904,4500,0,0]Al[4727,14,4728,1904,5015,0,0]Ai[4727,14,4767,0,4829,0,0]Ah[4727,14,4499,0,471,0,0]Ai[4727,14,4499,0,1353,0,0]Ai[4727,14,4499,0,1355,0,0]Ai[4727,14,4499,0,4707,0,0]Ai[3940,12,1841,0,3131,0,0]Ai[3940,12,1841,0,3124,0,0]Al[4727,12,1842,1842,3132,0,0]Al[4727,12,1842,1842,3125,0,0]Ae[4727,2,4728,0,0,0,0]Al[3940,13,3941,4076,3327,0,0]Al[3940,13,3941,4076,4870,0,0]Ai[3940,13,3699,0,3304,0,0]Ak[3940,13,3699,0,111,3304,0]Al[3940,13,3699,0,1325,3304,0]Al[3940,13,3699,0,1985,3304,0]Al[4727,13,3700,3700,3305,0,0]Am[4727,13,3700,133,112,3305,0]Ao[4727,13,3700,1508,1326,3305,0]Ao[4727,13,3700,2486,1986,3305,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]9854Al[3940,13,3941,3704,3302,0,0]Al[3940,13,3941,1046,2407,0,0]Al[4727,14,4728,1904,5015,0,0]Ai[4727,14,4767,0,4829,0,0]Ah[4727,14,4499,0,471,0,0]Ai[4727,14,4499,0,1353,0,0]Ai[4727,14,4499,0,4707,0,0]Al[3940,13,3941,4081,3333,0,0]Al[4727,13,4728,2145,3334,0,0]Ak[1362,14,1363,1363,125,0,0]Al[1362,14,1537,1537,1358,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,12,1841,0,3131,0,0]Al[4727,12,1842,1842,3132,0,0]Am[1362,13,1363,1363,125,109,0]An[1362,13,1363,1363,3972,109,0]An[1362,13,1537,1537,4003,109,0]An[1362,13,1537,1537,1358,109,0]An[1362,13,1537,1537,4395,109,0]Ak[492,13,493,493,3973,109,0]An[4009,13,4010,4010,5016,109,0]An[3940,13,3941,3704,4513,109,0]An[3940,13,3941,3704,2406,109,0]Ak[3940,13,3941,0,4246,109,0]An[3940,13,3941,1046,2407,109,0]An[3940,13,3941,1046,3112,109,0]An[3940,13,3941,1046,4670,109,0]An[3940,13,3941,3704,3300,109,0]An[3940,13,3941,3704,3302,109,0]An[3940,13,3941,3704,1988,109,0]An[3940,13,3941,3704,1576,109,0]An[3940,13,3941,3704,3102,109,0]An[3940,13,3941,3704,2380,109,0]An[3940,13,3941,3704,4146,109,0]An[3940,13,3941,3704,4151,109,0]An[3940,13,3941,3704,4931,109,0]An[3940,13,3941,3704,4769,109,0]An[3940,13,3941,2437,2392,109,0]An[3940,13,3941,2437,4255,109,0]An[3940,13,3941,2437,1555,109,0]An[3940,13,3941,2464,2396,109,0]An[3940,13,3941,2464,3330,109,0]An[3940,13,3941,2464,4374,109,0]Am[3940,13,3941,1046,475,109,0]An[3940,13,3941,4076,3327,109,0]An[3940,13,3941,4076,4870,109,0]An[3940,13,3941,4076,4393,109,0]An[3940,13,3941,4076,3141,109,0]An[3940,13,3941,4076,3329,109,0]An[3940,13,3941,4076,4392,109,0]An[3940,13,3941,1549,4161,109,0]An[3940,13,3941,1089,3998,109,0]An[3940,13,3941,4081,3333,109,0]An[3940,13,3941,4081,3347,109,0]An[3940,13,3941,4081,4160,109,0]Ak[3940,13,3941,0,3324,109,0]Ak[3940,13,3941,0,4286,109,0]Ak[3940,13,3941,0,4505,109,0]Ak[3940,13,3941,0,4138,109,0]Ak[3940,13,3941,0,4383,109,0]Ak[3940,13,3941,0,1600,109,0]Ak[3940,13,3941,0,4928,109,0]Ak[3940,13,3941,0,1321,109,0]Ak[3940,13,3941,0,1583,109,0]Ak[3940,13,3941,0,2427,109,0]Ak[3940,13,3941,0,3308,109,0]Ak[3940,13,3941,0,3138,109,0]Ak[3940,13,3941,0,4491,109,0]Ak[3940,13,3941,0,2011,109,0]Ak[3940,13,3941,0,4112,109,0]Ak[3940,13,3941,0,4487,109,0]Ak[3940,13,3941,0,4501,109,0]Ak[3940,13,3941,0,4508,109,0]Ak[3940,13,3941,0,4793,109,0]Ak[3940,13,3941,0,4808,109,0]Ak[3940,13,3941,0,3985,109,0]Ak[3940,13,3941,0,4369,109,0]Ak[3940,13,3941,0,4154,109,0]Ak[3940,13,3941,0,4985,109,0]Ak[3940,13,3941,0,4106,109,0]Ak[3940,13,3941,0,3975,109,0]Ak[3940,13,3941,0,3121,109,0]Ak[3940,13,3941,0,4484,109,0]Ak[3940,13,3941,0,4672,109,0]Ak[3940,13,3941,0,4118,109,0]Ak[3940,13,3941,0,4121,109,0]Ak[3940,13,3941,0,4126,109,0]Ak[3940,13,3941,0,4827,109,0]Ak[3940,13,3941,0,3311,109,0]Ak[3940,13,3941,0,4703,109,0]Aj[3940,13,3941,0,482,109,0]Aj[3940,13,3941,0,117,109,0]Ak[3940,13,3941,0,2415,109,0]Ak[3940,13,3941,0,2424,109,0]Aj[3940,13,3699,0,111,109,0]Ak[3940,13,3699,0,3109,109,0]Ak[3940,13,3699,0,1325,109,0]Ak[3940,13,3699,0,3980,109,0]Ak[3940,13,3699,0,1985,109,0]Ak[3940,13,3699,0,4274,109,0]Ak[3940,13,3699,0,4761,109,0]Ak[3940,13,1841,0,1980,109,0]Aj[3940,13,1841,0,469,109,0]Ak[3940,13,1841,0,5074,109,0]Ak[3940,13,1841,0,3978,109,0]Ak[3940,13,1841,0,4831,109,0]Ak[3940,13,1920,0,3342,109,0]Ak[3940,13,1920,0,4257,109,0]Ak[3940,13,1920,0,4699,109,0]Ak[3940,13,1920,0,4701,109,0]Ak[3940,13,1920,0,4924,109,0]Ak[3940,13,1920,0,1338,109,0]An[4727,13,4728,4518,4247,109,0]An[4727,13,4728,3166,3116,109,0]An[4727,13,4728,1072,1563,109,0]An[4727,13,4728,2145,2005,109,0]An[4727,13,4728,2145,3328,109,0]An[4727,13,4728,2145,3334,109,0]An[4727,13,4728,3705,3303,109,0]An[4727,13,4728,1068,3325,109,0]An[4727,13,4728,1839,4287,109,0]An[4727,13,4728,2438,2393,109,0]An[4727,13,4728,2438,4253,109,0]An[4727,13,4728,1904,5133,109,0]An[4727,13,4728,4295,5103,109,0]An[4727,13,4728,1548,4506,109,0]An[4727,13,4728,1548,4139,109,0]An[4727,13,4728,1548,4384,109,0]An[4727,13,4728,1953,1601,109,0]An[4727,13,4728,1953,4929,109,0]An[4727,13,4728,3157,1322,109,0]An[4727,13,4728,3166,4770,109,0]An[4727,13,4728,1523,1584,109,0]An[4727,13,4728,1523,4150,109,0]An[4727,13,4728,1523,3323,109,0]An[4727,13,4728,1523,3137,109,0]An[4727,13,4728,1523,4824,109,0]An[4727,13,4728,1523,3298,109,0]An[4727,13,4728,1538,2428,109,0]An[4727,13,4728,1538,3309,109,0]An[4727,13,4728,1538,3139,109,0]An[4727,13,4728,1538,4492,109,0]An[4727,13,4728,1072,3142,109,0]An[4727,13,4728,1072,4282,109,0]An[4727,13,4728,1077,2012,109,0]An[4727,13,4728,1077,3348,109,0]An[4727,13,4728,1077,3984,109,0]An[4727,13,4728,2145,4002,109,0]An[4727,13,4728,2145,4394,109,0]An[4727,13,4728,2145,4116,109,0]An[4727,13,4728,3705,4488,109,0]An[4727,13,4728,3705,3134,109,0]An[4727,13,4728,3705,4360,109,0]An[4727,13,4728,3705,5012,109,0]An[4727,13,4728,3705,1343,109,0]An[4727,13,4728,1068,4502,109,0]An[4727,13,4728,3285,4509,109,0]An[4727,13,4728,3285,4794,109,0]An[4727,13,4728,3285,4809,109,0]An[4727,13,4728,3285,3986,109,0]An[4727,13,4728,1839,4370,109,0]An[4727,13,4728,1839,4156,109,0]An[4727,13,4728,1839,4986,109,0]An[4727,13,4728,1839,4108,109,0]An[4727,13,4728,1839,3976,109,0]An[4727,13,4728,1513,3122,109,0]An[4727,13,4728,1513,4375,109,0]An[4727,13,4728,1513,3315,109,0]An[4727,13,4728,1513,3337,109,0]An[4727,13,4728,1513,4123,109,0]An[4727,13,4728,1513,4486,109,0]An[4727,13,4728,1513,4674,109,0]An[4727,13,4728,1513,2386,109,0]An[4727,13,4728,2438,4678,109,0]An[4727,13,4728,2438,2397,109,0]An[4727,13,4728,2438,4119,109,0]An[4727,13,4728,2438,3970,109,0]An[4727,13,4728,2438,3331,109,0]An[4727,13,4728,1904,4277,109,0]An[4727,13,4728,1904,4500,109,0]An[4727,13,4728,1904,5015,109,0]An[4727,13,4728,1904,2399,109,0]An[4727,13,4728,1904,4768,109,0]An[4727,13,4728,1931,4490,109,0]An[4727,13,4728,1931,4254,109,0]An[4727,13,4728,1931,4122,109,0]An[4727,13,4728,1931,4127,109,0]Ak[4727,13,4728,0,4398,109,0]Ak[4727,13,4728,0,4828,109,0]An[4727,13,4728,4295,3312,109,0]An[4727,13,4728,4295,5108,109,0]An[4727,13,4728,4858,4704,109,0]Am[4727,13,4728,4728,483,109,0]Am[4727,13,4728,4728,118,109,0]An[4727,13,4728,4728,2416,109,0]An[4727,13,4728,4728,2425,109,0]An[4727,13,4728,4728,3120,109,0]An[4727,13,1837,1837,4288,109,0]An[4727,13,4970,4970,4869,109,0]An[4727,13,4970,4970,1340,109,0]An[4727,13,1842,1842,1981,109,0]Am[4727,13,1842,1842,470,109,0]An[4727,13,1842,1842,5075,109,0]An[4727,13,1842,1842,3979,109,0]An[4727,13,1842,1842,4834,109,0]An[4727,13,1921,1921,3343,109,0]An[4727,13,1921,1921,4258,109,0]An[4727,13,1921,1921,4700,109,0]An[4727,13,1921,1921,4702,109,0]An[4727,13,1921,1921,4925,109,0]Ak[4727,13,1921,0,1339,109,0]Al[4727,13,3700,133,112,109,0]Am[4727,13,3700,133,3110,109,0]An[4727,13,3700,1508,1326,109,0]An[4727,13,3700,1508,3981,109,0]An[4727,13,3700,2486,1986,109,0]An[4727,13,3700,2486,4275,109,0]An[4727,13,3700,3700,4762,109,0]An[4727,13,2116,2116,1341,109,0]An[4727,13,2116,2116,1344,109,0]An[4433,13,4434,4434,3346,109,0]Am[4433,13,4434,390,4399,109,0]An[4433,13,4434,4434,2413,109,0]An[4433,13,4434,4434,2426,109,0]An[4433,13,4434,4434,4128,109,0]An[1047,13,1048,1048,3113,109,0]An[1047,13,1048,1048,4862,109,0]Am[1047,13,1831,1831,126,109,0]An[1047,13,1539,1539,1359,109,0]An[1047,13,1539,1539,4004,109,0]An[1047,13,1539,1539,4496,109,0]An[1047,13,4213,4213,5060,109,0]An[1047,13,4213,4213,5061,109,0]Am[1047,13,1926,1926,127,109,0]Am[3960,13,3961,3961,116,109,0]An[3960,13,3961,3961,2383,109,0]Al[3940,13,3941,3704,4931,0,0]Ai[3940,13,3941,0,4106,0,0]Al[4727,13,4728,1839,4108,0,0]Al[4727,14,4728,1904,5015,0,0]Ai[4727,14,4767,0,4829,0,0]Ah[4727,14,4499,0,471,0,0]Ai[4727,14,4499,0,1353,0,0]Ah[3940,5,3941,3704,0,0,0]Al[1362,15,1363,1363,3972,0,0]Ae[3960,6,3961,0,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Ah[4727,5,4728,1513,0,0,0]Al[3940,15,3941,3704,4769,0,0]Al[4727,15,4728,3166,4770,0,0]Ah[3940,5,3941,2437,0,0,0]Ah[4727,5,4728,2438,0,0,0]Al[4727,15,4728,2145,4002,0,0]Ah[3940,5,3941,2464,0,0,0]2Ah[4727,6,4728,1904,0,0,0]Ai[3940,19,3941,0,1600,0,0]Al[4727,19,4728,1953,1601,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]=Ai[3940,12,1841,0,4155,0,0]>Al[3940,13,1920,0,4701,4155,0]>Al[4727,12,1842,1842,4157,0,0]>Ao[4727,13,1921,1921,4702,4157,0]Ao[1047,13,4213,4213,5061,4157,0]An[3960,13,3961,3961,116,3987,0]Ao[3960,13,3961,3961,2383,3987,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,14,4728,1538,3139,0,0]Ai[3940,13,3941,0,4154,0,0]Al[4727,13,4728,1839,4156,0,0]Al[3940,13,3941,2137,4357,0,0]0>=Al[1047,13,1539,1539,4496,0,0]Ae[1047,2,1048,0,0,0,0]Al[3940,13,3941,2464,2396,0,0]Al[4727,13,4728,2438,2397,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]43Ai[3940,14,3941,0,2424,0,0]Al[4727,14,4728,4728,2425,0,0]Al[4433,14,4434,4434,2426,0,0]Al[4727,13,4728,2145,2005,0,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,14,4728,1538,3139,0,0]Al[4727,13,4728,4295,5103,0,0]Al[4727,13,4728,4295,5108,0,0]Al[3940,13,3941,4081,3333,0,0]Al[4727,13,4728,2145,3334,0,0]>=Al[1362,14,1537,1537,1358,0,0]Ai[492,14,493,493,3973,0,0]Ai[3940,13,1920,0,4699,0,0]Ai[3940,13,1920,0,4701,0,0]Al[4727,13,1921,1921,4700,0,0]Al[4727,13,1921,1921,4702,0,0]76Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]Ah[3940,5,3941,4518,0,0,0]Ah[4727,5,4728,4518,0,0,0]Ai[3940,15,3941,0,4487,0,0]Ai[3940,15,3941,0,4118,0,0]Al[4727,15,4728,3705,4488,0,0]Al[4727,15,4728,2438,4119,0,0]Ah[4727,6,4728,2438,0,0,0]Ah[4727,5,4728,1931,0,0,0]Ah[3940,5,3941,2437,0,0,0]Al[3940,15,3941,4076,4392,0,0]Ah[3940,5,1920,1921,0,0,0]Ae[4727,5,1921,0,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Al[4727,15,4728,1904,4768,0,0]>Al[4727,15,4728,1523,3137,0,0]=5454Al[4727,15,4728,2438,4253,0,0]Ah[3940,5,3699,2486,0,0,0]Ah[4727,5,3700,2486,0,0,0]Ah[4727,5,4728,1904,0,0,0]98Ah[4727,6,4728,1072,0,0,0]:9Ah[3940,5,3941,1839,0,0,0]Ah[4727,5,4728,1839,0,0,0]Ae[4727,5,1837,0,0,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Al[1047,14,1048,1048,3113,0,0]Ae[4727,2,4728,0,0,0,0]Ai[3940,13,3941,0,4369,0,0]Al[4727,13,4728,1839,4370,0,0]Al[3940,12,3941,2137,4357,0,0]Ab[4298,3,0,0,0,0,0]Ak[1362,14,1363,1363,125,0,0]Al[1362,14,1537,1537,4003,0,0]98Al[3940,13,3941,2464,3330,0,0]Al[4727,13,4728,2438,3331,0,0]Ai[3940,13,3941,0,4793,0,0]Al[4727,13,4728,3285,4794,0,0];=Al[3940,13,3941,4076,4870,0,0]Al[4727,13,4728,2145,3328,0,0]Al[3940,13,3941,3704,3300,0,0]Al[3940,13,3941,3704,3302,0,0]Al[3940,13,3941,2464,2396,0,0]Ak[3940,13,3941,1046,475,0,0]Al[4727,13,4728,2438,2397,0,0]<;Ai[3940,13,3941,0,1600,0,0]Ai[3940,13,3941,0,1583,0,0]Al[4727,13,4728,1953,1601,0,0]Al[4727,13,4728,1523,1584,0,0]20Ae[1362,7,1537,0,0,0,0]?>Al[1362,14,1537,1537,1358,0,0]Ao[1047,13,1048,1048,4862,3146,0]Ai[3940,15,3941,0,3121,0,0]Ah[3940,5,3941,1513,0,0,0]Al[4727,15,4728,1513,3122,0,0]Ah[4727,5,4728,1513,0,0,0]Ah[3940,6,3941,3705,0,0,0]Ah[4727,6,4728,3705,0,0,0]Al[4727,15,4728,2438,4253,0,0]Ah[4727,5,4728,1931,0,0,0]Ah[3940,5,3941,1538,0,0,0]Ah[4727,5,4728,1538,0,0,0]97Ae[1047,5,1539,0,0,0,0]:8Ah[4727,6,4728,1904,0,0,0]Ah[3940,5,3941,1068,0,0,0]Ah[4727,5,4728,1068,0,0,0]Ah[3940,5,3941,1548,0,0,0]Ah[4727,5,4728,1548,0,0,0]Ah[3940,5,3941,3285,0,0,0]Ah[4727,5,4728,3285,0,0,0]Ai[3940,15,3941,0,4487,0,0]Al[4727,15,4728,3705,4488,0,0]Ah[3940,5,3941,3704,0,0,0]Al[3940,15,3941,4076,3141,0,0]Al[3940,15,3941,4076,3329,0,0]Al[3940,15,3941,4081,3347,0,0]Al[3940,15,3941,4081,4160,0,0]An[1362,13,1363,1363,125,4252,0]Ao[1362,13,1363,1363,3972,4252,0]Ao[3940,13,3941,3704,3302,4252,0]Ao[3940,13,3941,3704,4931,4252,0]Ao[3940,13,3941,3704,4769,4252,0]Ao[3940,13,3941,2437,2392,4252,0]Ao[3940,13,3941,2437,4255,4252,0]Ao[3940,13,3941,4076,4393,4252,0]Ao[3940,13,3941,4076,3141,4252,0]Ao[3940,13,3941,4076,3329,4252,0]Ao[3940,13,3941,4076,4392,4252,0]Ao[3940,13,3941,4081,3347,4252,0]Ao[3940,13,3941,4081,4160,4252,0]Al[3940,13,3941,0,1600,4252,0]Al[3940,13,3941,0,4928,4252,0]Al[3940,13,3941,0,1583,4252,0]Al[3940,13,3941,0,2427,4252,0]Al[3940,13,3941,0,3308,4252,0]Al[3940,13,3941,0,3138,4252,0]Al[3940,13,3941,0,4491,4252,0]Al[3940,13,3941,0,2011,4252,0]Al[3940,13,3941,0,4112,4252,0]Al[3940,13,3941,0,4487,4252,0]Al[3940,13,3941,0,4508,4252,0]Al[3940,13,3941,0,4793,4252,0]Al[3940,13,3941,0,4808,4252,0]Al[3940,13,3941,0,3985,4252,0]Al[3940,13,3941,0,4369,4252,0]Al[3940,13,3941,0,4154,4252,0]Al[3940,13,3941,0,4985,4252,0]Al[3940,13,3941,0,4106,4252,0]Al[3940,13,3941,0,3975,4252,0]Al[3940,13,3941,0,3121,4252,0]Al[3940,13,3941,0,4484,4252,0]Al[3940,13,3941,0,4672,4252,0]Al[3940,13,3941,0,4118,4252,0]Al[3940,13,3941,0,4121,4252,0]Al[3940,13,3941,0,4126,4252,0]Al[3940,13,3941,0,4827,4252,0]Al[3940,13,3941,0,3311,4252,0]Ak[3940,13,3941,0,482,4252,0]Ak[3940,13,3941,0,117,4252,0]Al[3940,13,3941,0,2415,4252,0]Ak[3940,13,3699,0,111,4252,0]Al[3940,13,3699,0,3109,4252,0]Al[3940,13,3699,0,1325,4252,0]Al[3940,13,3699,0,3980,4252,0]Al[3940,13,3699,0,1985,4252,0]Al[3940,13,3699,0,4274,4252,0]Al[3940,13,1920,0,4924,4252,0]Al[3940,13,1920,0,1338,4252,0]Ao[4727,13,4728,1953,1601,4252,0]Ao[4727,13,4728,1953,4929,4252,0]Ao[4727,13,4728,3166,4770,4252,0]Ao[4727,13,4728,1523,1584,4252,0]Ao[4727,13,4728,1523,4150,4252,0]Ao[4727,13,4728,1523,3323,4252,0]Ao[4727,13,4728,1523,3137,4252,0]Ao[4727,13,4728,1523,4824,4252,0]Ao[4727,13,4728,1523,3298,4252,0]Ao[4727,13,4728,1538,2428,4252,0]Ao[4727,13,4728,1538,3309,4252,0]Ao[4727,13,4728,1538,3139,4252,0]Ao[4727,13,4728,1538,4492,4252,0]Ao[4727,13,4728,1072,3142,4252,0]Ao[4727,13,4728,1072,4282,4252,0]Ao[4727,13,4728,1077,2012,4252,0]Ao[4727,13,4728,1077,3348,4252,0]Ao[4727,13,4728,1077,3984,4252,0]Ao[4727,13,4728,2145,4394,4252,0]Ao[4727,13,4728,2145,4116,4252,0]Ao[4727,13,4728,3705,4488,4252,0]Ao[4727,13,4728,3705,3134,4252,0]Ao[4727,13,4728,3705,4360,4252,0]Ao[4727,13,4728,3705,5012,4252,0]Ao[4727,13,4728,3285,4509,4252,0]Ao[4727,13,4728,3285,4794,4252,0]Ao[4727,13,4728,3285,4809,4252,0]Ao[4727,13,4728,3285,3986,4252,0]Ao[4727,13,4728,1839,4370,4252,0]Ao[4727,13,4728,1839,4156,4252,0]Ao[4727,13,4728,1839,4986,4252,0]Ao[4727,13,4728,1839,4108,4252,0]Ao[4727,13,4728,1839,3976,4252,0]Ao[4727,13,4728,1513,3122,4252,0]Ao[4727,13,4728,1513,3315,4252,0]Ao[4727,13,4728,1513,3337,4252,0]Ao[4727,13,4728,1513,4123,4252,0]Ao[4727,13,4728,1513,4486,4252,0]Ao[4727,13,4728,1513,4674,4252,0]Ao[4727,13,4728,1513,2386,4252,0]Ao[4727,13,4728,2438,4678,4252,0]Ao[4727,13,4728,2438,4119,4252,0]Ao[4727,13,4728,1904,4500,4252,0]Ao[4727,13,4728,1904,5015,4252,0]Ao[4727,13,4728,1904,2399,4252,0]Ao[4727,13,4728,1904,4768,4252,0]Ao[4727,13,4728,1931,4122,4252,0]Ao[4727,13,4728,1931,4127,4252,0]Al[4727,13,4728,0,4398,4252,0]Al[4727,13,4728,0,4828,4252,0]Ao[4727,13,4728,4295,3312,4252,0]Ao[4727,13,4728,4295,5108,4252,0]An[4727,13,4728,4728,483,4252,0]An[4727,13,4728,4728,118,4252,0]Ao[4727,13,4728,4728,2416,4252,0]Ao[4727,13,4728,4728,3120,4252,0]Ao[4727,13,1837,1837,4288,4252,0]Ao[4727,13,4970,4970,4869,4252,0]Ao[4727,13,4970,4970,1340,4252,0]Ao[4727,13,1921,1921,4925,4252,0]Al[4727,13,1921,0,1339,4252,0]Am[4727,13,3700,133,112,4252,0]An[4727,13,3700,133,3110,4252,0]Ao[4727,13,3700,1508,1326,4252,0]Ao[4727,13,3700,1508,3981,4252,0]Ao[4727,13,3700,2486,1986,4252,0]Ao[4727,13,3700,2486,4275,4252,0]Ao[4727,13,2116,2116,1344,4252,0]Ao[4433,13,4434,4434,2413,4252,0]Ao[4433,13,4434,4434,2426,4252,0]Ao[4433,13,4434,4434,4128,4252,0]Ao[1047,13,1048,1048,4862,4252,0]Ao[1047,13,1539,1539,4496,4252,0]Ah[3940,7,3941,2137,0,0,0]Ai[3940,13,3941,0,4369,0,0]Al[4727,13,4728,1839,4370,0,0]Ai[3940,12,1841,0,3131,0,0]Al[4727,12,1842,1842,3132,0,0]Al[4433,14,4434,4434,2413,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]10Ae[1047,7,1048,0,0,0,0]Al[3940,13,3941,2437,2392,0,0]Ae[4298,7,4299,0,0,0,0]Ae[1362,7,1363,0,0,0,0]Al[4727,13,4728,3705,3303,0,0]Al[3940,13,3941,3704,3302,0,0]1Al[4727,13,4728,2145,4394,0,0]Al[1362,13,1537,1537,1358,0,0]Al[1362,14,1537,1537,4003,0,0]Al[3940,12,3941,2137,1997,0,0]Ao[3940,13,3941,1549,4161,1997,0]Ao[3940,13,3941,1089,3998,1997,0]Ai[3940,12,1841,0,1998,0,0]Al[3940,13,1920,0,3342,1998,0]Al[4727,12,1842,1842,1999,0,0]Ao[4727,13,1921,1921,3343,1999,0]Ah[3940,5,3941,1046,0,0,0]Ai[3940,15,3941,0,3121,0,0]Ah[3940,5,3941,1513,0,0,0]Al[4727,15,4728,1513,3122,0,0]Ah[4727,5,4728,1513,0,0,0]Ai[3940,15,3941,0,4487,0,0]Al[4727,15,4728,3705,4488,0,0]Ah[4727,5,4728,2438,0,0,0]Al[4727,15,4728,3705,1343,0,0]Ai[3940,15,3941,0,4118,0,0]Al[4727,15,4728,2438,4119,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]32Al[4727,15,4728,3705,3134,0,0]><><><21Ah[3940,5,1920,1921,0,0,0]Ae[4727,5,1921,0,0,0,0]10Ah[3940,5,3941,4858,0,0,0]Ah[4727,5,4728,4858,0,0,0]Al[4727,15,2116,2116,1341,0,0]5Al[4727,15,4728,1904,4500,0,0]Ai[3940,12,3941,0,2420,0,0]Al[3940,13,3941,0,4246,2420,0]Al[3940,13,3941,0,4138,2420,0]Al[4727,12,4728,1548,2421,0,0]Ao[4727,13,4728,4518,4247,2421,0]Ao[4727,13,4728,1548,4139,2421,0]Ae[4009,7,4010,0,0,0,0]Al[3940,13,3941,3704,3302,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]32An[1362,13,1363,1363,125,4371,0]Ao[1047,13,1048,1048,3113,4371,0]An[1047,13,1831,1831,126,4371,0]An[1047,13,1926,1926,127,4371,0]An[3960,13,3961,3961,116,4371,0]Ao[3960,13,3961,3961,2383,4371,0]Ab[4727,3,0,0,0,0,0]Ak[4433,13,4434,390,4399,0,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0];:Al[3940,13,3941,2437,2392,0,0]Al[4727,13,4728,2438,2393,0,0]?>32Ae[4298,7,4299,0,0,0,0]5Al[1047,14,1048,1048,3113,0,0]60Ae[1362,7,1363,0,0,0,0]Ai[3940,13,3941,0,4154,0,0]Ai[3940,13,3941,0,4106,0,0]Al[4727,13,4728,1839,4156,0,0]Al[4727,13,4728,1839,4108,0,0]Ai[3940,14,3941,0,4154,0,0]Al[4727,14,4728,1839,4156,0,0]53Al[3940,13,3941,4081,3333,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Ai[3940,15,3941,0,4487,0,0]Al[4727,15,4728,3705,4488,0,0]Ai[3940,10,3699,3700,0,0,0]Af[4727,10,3700,0,0,0,0]Ah[3940,5,3699,3700,0,0,0]Ae[4727,5,3700,0,0,0,0]Ai[3940,10,1841,1842,0,0,0]Af[4727,10,1842,0,0,0,0]Ai[3940,15,3941,0,4118,0,0]Al[4727,15,4728,2438,4119,0,0]Ah[4727,6,4728,1904,0,0,0]Ah[3940,6,3941,3704,0,0,0]Ah[4727,6,4728,3166,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]10Al[3940,15,3941,3704,4769,0,0]Al[4727,15,4728,3166,4770,0,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,14,4728,1538,3139,0,0]Ai[3940,14,3699,0,4761,0,0]Al[4727,14,3700,3700,4762,0,0]Al[3940,13,3941,3704,3302,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]10Al[4727,13,4728,3166,3116,0,0]Al[3940,13,3941,2437,2392,0,0]Al[4727,13,4728,2438,2393,0,0]Ab[4788,3,0,0,0,0,0]Ao[1047,13,1048,1048,4862,3146,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Ah[3940,5,3941,3285,0,0,0]Ah[4727,5,4728,3285,0,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]54Ai[4727,10,4728,1904,0,0,0]Al[4727,15,4728,3705,1343,0,0]7600Al[4727,15,4728,1072,4282,0,0]4365Al[4727,15,2116,2116,1341,0,0]Ae[1047,9,1048,0,0,0,0]:9Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]<;<;Ah[4727,6,4728,1523,0,0,0]21Ae[3940,5,3941,0,0,0,0]Ae[4727,5,4728,0,0,0,0]Ai[3940,15,3941,0,4118,0,0]Ah[3940,5,1841,1842,0,0,0]Al[4727,15,4728,2438,4119,0,0]Al[4727,15,4728,1904,4768,0,0]Ae[4727,5,1842,0,0,0,0]An[1362,13,1363,1363,125,4361,0]Ao[1047,13,1048,1048,3113,4361,0]An[1047,13,1831,1831,126,4361,0]An[1047,13,1926,1926,127,4361,0]An[3960,13,3961,3961,116,4361,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]Ai[3940,13,3941,0,1600,0,0]Al[4727,13,4728,1953,1601,0,0]Al[3940,14,3941,2437,4255,0,0]Ai[3940,13,3941,0,4369,0,0]Al[4727,13,4728,1839,4370,0,0]Ai[3940,13,3941,0,4246,0,0]Al[4727,13,4728,4518,4247,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]6Al[4727,13,4728,3705,3303,0,0]Al[3940,13,3941,3704,3302,0,0]0176Ao[4009,13,4010,4010,5016,4822,0]Ai[3940,13,3941,0,4501,0,0]Al[4727,13,4728,1068,4502,0,0]Ae[1047,6,1048,0,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]10Al[4727,15,4728,3705,3134,0,0]Ae[4727,5,4970,0,0,0,0]Ah[3940,5,3941,4076,0,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]Al[4727,15,2116,2116,1341,0,0]Al[3940,14,3941,2437,4255,0,0]Ak[3940,13,3941,0,482,3103,0]Ak[3940,13,3941,0,117,3103,0]Al[3940,13,3941,0,2415,3103,0]An[4727,13,4728,4728,483,3103,0]An[4727,13,4728,4728,118,3103,0]Ao[4727,13,4728,4728,2416,3103,0]Ao[3960,13,3961,3961,2383,3338,0]Ai[3940,13,3941,0,4808,0,0]Al[4727,13,4728,3285,4809,0,0]Ai[3940,12,1841,0,1998,0,0]Al[3940,13,1920,0,3342,1998,0]Al[4727,12,1842,1842,1999,0,0]Ao[4727,13,1921,1921,3343,1999,0]Al[3940,13,3941,3704,3302,0,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]Ai[3940,14,3941,0,3138,0,0]Al[4727,14,4728,1538,3139,0,0]Ai[3940,12,3699,0,3304,0,0]Ak[3940,13,3699,0,111,3304,0]Al[3940,13,3699,0,1325,3304,0]Al[3940,13,3699,0,1985,3304,0]Al[4727,12,3700,3700,3305,0,0]Am[4727,13,3700,133,112,3305,0]Ao[4727,13,3700,1508,1326,3305,0]Ao[4727,13,3700,2486,1986,3305,0]Al[3940,14,3941,2437,4255,0,0]Al[4727,13,4728,2145,2005,0,0]Ah[4727,2,4293,4080,0,0,0]Ae[1362,7,1363,0,0,0,0]==Ak[3940,13,3941,1046,475,0,0]Ak[4433,13,4434,390,4399,0,0]032Ai[3940,12,1841,0,1998,0,0]Al[3940,13,1920,0,3342,1998,0]Ae[4727,2,4728,0,0,0,0]Al[4727,12,1842,1842,1999,0,0]Ao[4727,13,1921,1921,3343,1999,0]Ai[3940,10,3941,4295,0,0,0]Ai[4727,10,4728,4295,0,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]Ai[3940,15,3941,0,4118,0,0]Al[4727,15,4728,2438,4119,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Ah[3940,5,3941,1839,0,0,0]Ah[4727,5,4728,1839,0,0,0]Ai[3940,12,1841,0,3131,0,0]Al[4727,12,1842,1842,3132,0,0]Al[4727,13,4728,2438,2393,0,0]Al[4727,13,4728,4728,3120,0,0]Al[4727,13,4728,3705,3303,0,0]Ai[3940,13,3699,0,4274,0,0]Al[4727,13,3700,2486,4275,0,0]Al[3940,13,3941,2137,3144,0,0]0Al[3940,13,3941,3704,3302,0,0]4Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]Al[1047,14,1048,1048,3113,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Al[4727,15,2116,2116,1341,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]10Al[4727,15,4728,1523,4824,0,0]Al[4727,15,4728,3705,3134,0,0]Ah[4727,5,4728,3705,0,0,0]Ai[3940,15,3941,0,3121,0,0]Al[4727,15,4728,1513,3122,0,0]Ah[4727,5,4728,1904,0,0,0]Ae[4009,5,4010,0,0,0,0]Al[1047,13,1048,1048,3113,0,0]Al[3940,13,3941,3704,3302,0,0]Ai[3940,12,1841,0,1998,0,0]Al[3940,13,1920,0,3342,1998,0]Al[4727,12,1842,1842,1999,0,0]Ao[4727,13,1921,1921,3343,1999,0]Ah[4727,7,4904,4903,0,0,0]0Al[4727,13,4728,3705,3303,0,0]6Ak[3940,13,3941,1046,475,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]Ao[4009,13,4010,4010,5016,4822,0]0Ai[3940,13,3941,0,4106,0,0]Al[4727,13,4728,1839,4108,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]10Ai[3940,13,3941,0,4793,0,0]Al[4727,13,4728,3285,4794,0,0]Am[1362,13,1363,1363,125,457,0]An[1047,13,1048,1048,3113,457,0]Am[1047,13,1831,1831,126,457,0]Am[1047,13,1926,1926,127,457,0]Am[3960,13,3961,3961,116,457,0]??>An[1362,13,1363,1363,125,4371,0]Ao[1047,13,1048,1048,3113,4371,0]An[1047,13,1831,1831,126,4371,0]An[1047,13,1926,1926,127,4371,0]An[3960,13,3961,3961,116,4371,0]Ao[3960,13,3961,3961,2383,4371,0]Ai[3940,13,3941,0,4154,0,0]Al[4727,13,4728,1839,4156,0,0]Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]Ae[1047,5,4213,0,0,0,0]0Ai[3940,13,3941,0,4369,0,0]Al[4727,13,4728,1839,4370,0,0]Ai[3940,13,3941,0,4106,0,0]Al[4727,13,4728,1839,4108,0,0]Al[3940,13,3941,2437,2392,0,0]Al[3940,13,3941,2464,2396,0,0]Al[4727,13,4728,2438,2397,0,0]Al[4727,13,4728,3705,3303,0,0]Ao[4009,13,4010,4010,5016,4822,0];:Ah[3940,5,1841,1842,0,0,0]Ae[4727,5,1842,0,0,0,0]Ai[3940,15,1920,0,1338,0,0]Ai[4727,15,1921,0,1339,0,0]?>?>Al[3940,13,3941,3704,3302,0,0]6Ai[3940,13,3941,0,4508,0,0]Al[4727,13,4728,3285,4509,0,0]Ae[4009,7,4010,0,0,0,0]Ae[4298,7,4299,0,0,0,0]Ai[3940,14,3941,0,3308,0,0]Al[4727,14,4728,1538,3309,0,0]?=Ai[3940,19,3941,0,4827,0,0]Ai[4727,19,4728,0,4828,0,0]2=<==<>=>=>=<;<;Al[4727,14,4970,4970,4869,0,0]?>Ai[4727,18,4728,3285,0,0,0]") \ No newline at end of file diff --git a/pr/323/docs/search.index/function/0a18a7c168e3.js b/pr/323/docs/search.index/function/0a18a7c168e3.js deleted file mode 100644 index f97d324b1..000000000 --- a/pr/323/docs/search.index/function/0a18a7c168e3.js +++ /dev/null @@ -1 +0,0 @@ -rd_("Bf[\"{{{AI@j{AO@d}}{AI@j{AO@d}}}CMf}\",[]]Bf[\"{{{AI@j{BECn}}{AI@j{BECn}}}CMf}\",[]]Bf[\"{{{AI@j{BBDn}}{AI@j{BBDn}}}CMf}\",[]]Bf[\"{{{AI@j{AHHh}}{AI@j{AHHh}}}CMf}\",[]]Bf[\"{{{AI@j{AIOn}}{AI@j{AIOn}}}CMf}\",[]]Bf[\"{{{AI@j{BBDl}}{AI@j{BBDl}}}CMf}\",[]]Bf[\"{{{AI@j{AJBb}}{AI@j{AJBb}}}CMf}\",[]]Bf[\"{{{AI@j{B@Gl}}{AI@j{B@Gl}}}CMf}\",[]]Bd[\"{{{AI@j{LGn}}{AI@j{LGn}}}CMf}\",[]]Bf[\"{{{AI@j{BFGl}}{AI@j{BFGl}}}CMf}\",[]]Bd[\"{{{AI@j{JE`}}{AI@j{JE`}}}CMf}\",[]]Bd[\"{{{AI@j{LEl}}{AI@j{LEl}}}CMf}\",[]]Bf[\"{{{AI@j{AHHb}}{AI@j{AHHb}}}CMf}\",[]]Bd[\"{{{AI@j{OKd}}{AI@j{OKd}}}CMf}\",[]]Bf[\"{{{AI@j{B@Al}}{AI@j{B@Al}}}CMf}\",[]]Bf[\"{{{AI@j{BC@j}}{AI@j{BC@j}}}CMf}\",[]]Bf[\"{{{AI@j{AOAn}}{AI@j{AOAn}}}CMf}\",[]]Bf[\"{{{AI@j{AO@j}}{AI@j{AO@j}}}CMf}\",[]]Bf[\"{{{AI@j{AHF`}}{AI@j{AHF`}}}CMf}\",[]]Bf[\"{{{AI@j{BC@d}}{AI@j{BC@d}}}CMf}\",[]]Bf[\"{{{AI@j{BDGl}}{AI@j{BDGl}}}CMf}\",[]]Bf[\"{{{AI@j{B@Bh}}{AI@j{B@Bh}}}CMf}\",[]]Bf[\"{{{AI@j{B@Bn}}{AI@j{B@Bn}}}CMf}\",[]]Bf[\"{{{AI@j{B@Ch}}{AI@j{B@Ch}}}CMf}\",[]]Bf[\"{{{AI@j{BEKb}}{AI@j{BEKb}}}CMf}\",[]]Bd[\"{{{AI@j{CLb}}{AI@j{CLb}}}CMf}\",[]]Bb[\"{{{AI@j{Nj}}{AI@j{Nj}}}CMf}\",[]]Bf[\"{{{AI@j{ABMl}}{AI@j{ABMl}}}CMf}\",[]]Bf[\"{{{AI@j{BGJ`}}{AI@j{BGJ`}}}CMf}\",[]]Bd[\"{{{AI@j{JGb}}{AI@j{JGb}}}CMf}\",[]]Bd[\"{{{AI@j{LH`}}{AI@j{LH`}}}CMf}\",[]]Bf[\"{{{AI@j{BFGn}}{AI@j{BFGn}}}CMf}\",[]]Bd[\"{{{AI@j{JEb}}{AI@j{JEb}}}CMf}\",[]]Bf[\"{{{AI@j{BED`}}{AI@j{BED`}}}CMf}\",[]]Bd[\"{{{AI@j{LEn}}{AI@j{LEn}}}CMf}\",[]]Bf[\"{{{AI@j{AIOb}}{AI@j{AIOb}}}CMf}\",[]]Bf[\"{{{AI@j{AHH`}}{AI@j{AHH`}}}CMf}\",[]]Bf[\"{{{AI@j{BEJl}}{AI@j{BEJl}}}CMf}\",[]]Bf[\"{{{AI@j{AIL`}}{AI@j{AIL`}}}CMf}\",[]]Bf[\"{{{AI@j{AHHd}}{AI@j{AHHd}}}CMf}\",[]]Bf[\"{{{AI@j{AHHj}}{AI@j{AHHj}}}CMf}\",[]]Bf[\"{{{AI@j{BAG`}}{AI@j{BAG`}}}CMf}\",[]]Bd[\"{{{AI@j{OKf}}{AI@j{OKf}}}CMf}\",[]]Bf[\"{{{AI@j{AJBd}}{AI@j{AJBd}}}CMf}\",[]]Bf[\"{{{AI@j{AOAl}}{AI@j{AOAl}}}CMf}\",[]]Bf[\"{{{AI@j{AOD`}}{AI@j{AOD`}}}CMf}\",[]]Bf[\"{{{AI@j{BBE`}}{AI@j{BBE`}}}CMf}\",[]]Bf[\"{{{AI@j{B@Bd}}{AI@j{B@Bd}}}CMf}\",[]]Bf[\"{{{AI@j{BC@l}}{AI@j{BC@l}}}CMf}\",[]]Bf[\"{{{AI@j{AHGj}}{AI@j{AHGj}}}CMf}\",[]]Bf[\"{{{AI@j{BB@l}}{AI@j{BB@l}}}CMf}\",[]]Bf[\"{{{AI@j{AOB`}}{AI@j{AOB`}}}CMf}\",[]]Bf[\"{{{AI@j{AO@l}}{AI@j{AO@l}}}CMf}\",[]]Bf[\"{{{AI@j{AHFb}}{AI@j{AHFb}}}CMf}\",[]]Bf[\"{{{AI@j{BBBj}}{AI@j{BBBj}}}CMf}\",[]]Bf[\"{{{AI@j{AINb}}{AI@j{AINb}}}CMf}\",[]]Bf[\"{{{AI@j{AJ@n}}{AI@j{AJ@n}}}CMf}\",[]]Bf[\"{{{AI@j{B@Cb}}{AI@j{B@Cb}}}CMf}\",[]]Bf[\"{{{AI@j{BC@h}}{AI@j{BC@h}}}CMf}\",[]]Bf[\"{{{AI@j{BDH`}}{AI@j{BDH`}}}CMf}\",[]]Bf[\"{{{AI@j{BDHh}}{AI@j{BDHh}}}CMf}\",[]]Bf[\"{{{AI@j{B@Bj}}{AI@j{B@Bj}}}CMf}\",[]]Bf[\"{{{AI@j{ABKl}}{AI@j{ABKl}}}CMf}\",[]]Bf[\"{{{AI@j{BECl}}{AI@j{BECl}}}CMf}\",[]]Bf[\"{{{AI@j{B@C`}}{AI@j{B@C`}}}CMf}\",[]]Bf[\"{{{AI@j{B@Cj}}{AI@j{B@Cj}}}CMf}\",[]]Bf[\"{{{AI@j{BBEh}}{AI@j{BBEh}}}CMf}\",[]]Bf[\"{{{AI@j{BEKd}}{AI@j{BEKd}}}CMf}\",[]]Bd[\"{{{AI@j{CLd}}{AI@j{CLd}}}CMf}\",[]]Bb[\"{{{AI@j{Nl}}{AI@j{Nl}}}CMf}\",[]]Bf[\"{{{AI@j{ABMn}}{AI@j{ABMn}}}CMf}\",[]]Bf[\"{{{AI@j{AHEn}}{AI@j{AHEn}}}CMf}\",[]]Bf[\"{{{AI@j{BAGl}}{AI@j{BAGl}}}CMf}\",[]]Bf[\"{{{AI@j{BF@f}}{AI@j{BF@f}}}CMf}\",[]]Bd[\"{{{AI@j{JGf}}{AI@j{JGf}}}CMf}\",[]]Bf[\"{{{AI@j{BGJb}}{AI@j{BGJb}}}CMf}\",[]]Bd[\"{{{AI@j{JGd}}{AI@j{JGd}}}CMf}\",[]]Bf[\"{{{AI@j{B@Cl}}{AI@j{B@Cl}}}CMf}\",[]]Ai[\"{{{AI@j{CKd}}}LEl}\",[]]Ak[\"{{{AI@j{AIOj}}}ABMl}\",[]]Ak[\"{{{AI@j{BF@h}}}ABMl}\",[]]Ak[\"{{{AI@j{AJ@f}}}ABMl}\",[]]o[\"{LE`LEn}\",[]]o[\"{LClLEn}\",[]]A`[\"{AHCfLEn}\",[]]A`[\"{BADnLEn}\",[]]A`[\"{B@FbLEn}\",[]]A`[\"{B@EdLEn}\",[]]Aa[\"{ABM`ABMn}\",[]]Aa[\"{ABOjABMn}\",[]]Aa[\"{ABKbABMn}\",[]]A`[\"{OInABMn}\",[]]n[\"{{}OIf}\",[]]A`[\"{CJlABKl}\",[]]Be[\"{{{AI@j{LGn}}{AI@j{LGn}}}ABLn}\",[]]Be[\"{{{AI@j{LEl}}{AI@j{LEl}}}ABLn}\",[]]Bg[\"{{{AI@j{AOAn}}{AI@j{AOAn}}}ABLn}\",[]]Bg[\"{{{AI@j{B@Bn}}{AI@j{B@Bn}}}ABLn}\",[]]Be[\"{{{AI@j{CLb}}{AI@j{CLb}}}ABLn}\",[]]Bc[\"{{{AI@j{Nj}}{AI@j{Nj}}}ABLn}\",[]]Bg[\"{{{AI@j{ABMl}}{AI@j{ABMl}}}ABLn}\",[]]Be[\"{{{AI@j{LH`}}{AI@j{LH`}}}ABLn}\",[]]Bg[\"{{{AI@j{BED`}}{AI@j{BED`}}}ABLn}\",[]]Be[\"{{{AI@j{LEn}}{AI@j{LEn}}}ABLn}\",[]]Bg[\"{{{AI@j{AHHj}}{AI@j{AHHj}}}ABLn}\",[]]Bg[\"{{{AI@j{BAG`}}{AI@j{BAG`}}}ABLn}\",[]]Bg[\"{{{AI@j{AJBd}}{AI@j{AJBd}}}ABLn}\",[]]Bg[\"{{{AI@j{AOB`}}{AI@j{AOB`}}}ABLn}\",[]]Bg[\"{{{AI@j{BDHh}}{AI@j{BDHh}}}ABLn}\",[]]Bg[\"{{{AI@j{BECl}}{AI@j{BECl}}}ABLn}\",[]]Bg[\"{{{AI@j{B@C`}}{AI@j{B@C`}}}ABLn}\",[]]Be[\"{{{AI@j{CLd}}{AI@j{CLd}}}ABLn}\",[]]Bc[\"{{{AI@j{Nl}}{AI@j{Nl}}}ABLn}\",[]]Bg[\"{{{AI@j{ABMn}}{AI@j{ABMn}}}ABLn}\",[]]o[\"{ABMlNj}\",[]]o[\"{ABMnNl}\",[]]n[\"{LEbNl}\",[]]o[\"{AHDbNl}\",[]]A`[\"{ABNnLEh}\",[]]A`[\"{ABO`LEh}\",[]]A`[\"{ABMhLEh}\",[]]A`[\"{ABObLEh}\",[]]Bg[\"{{{AI@j{Oj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AO@d}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBEb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BGBl}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AILd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AILh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BFHb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BECn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{ABJn}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BACj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABKf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AJ@`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BBBh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{CKd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AIOj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BF@h}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBDn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AHHh}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AIOn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BBDl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{B@Gn}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AOCh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AJ@f}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AJBb}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{B@Gl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Cd[\"{{{AI@j{LGn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{BFGl}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Cd[\"{{{AI@j{JE`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cd[\"{{{AI@j{LEl}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{ABOd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AIMd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AHHb}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BCAb}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cd[\"{{{AI@j{OKd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{B@Al}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{BC@j}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{BCBf}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BCCd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BEFn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BEHl}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AOAn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BBAn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{B@G`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BFNn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{B@A`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AO@j}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{AHF`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{BC@d}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{BDGl}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{B@Bh}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{B@Bn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{B@Ch}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BEKb}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{AIMj}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BDKj}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cd[\"{{{AI@j{CLb}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cc[\"{{{AI@j{Nj}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{ABMl}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Ce[\"{{{AI@j{ABNn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cc[\"{{{AI@j{Mn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AHDh}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cd[\"{{{AI@j{JEh}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AOAd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Cd[\"{{{AI@j{OH`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BAF`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BEBn}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BGJ`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{AOA`}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BEKj}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]Ce[\"{{{AI@j{BFGd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Cd[\"{{{AI@j{JGb}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Bi[\"{{{AI@j{AILj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABK`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{LH`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BFGn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bh[\"{{{AI@j{JEb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BED`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bh[\"{{{AI@j{LEn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{B@Fh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AIOb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AHH`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BEJl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AIL`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{ABOf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AIMf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AHHd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BCAd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AHHj}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BAG`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bh[\"{{{AI@j{OKf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AJBd}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AOAl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AOD`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBE`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@Bd}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BC@l}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AHGj}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BB@l}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BGBd}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bh[\"{{{AI@j{JGl}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BCBh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BCCf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BEG`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BEHn}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AOB`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBB`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@Gd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BFO`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@Ad}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AO@l}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AHFb}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BBBj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AINb}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AJ@n}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{B@Cb}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BC@h}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BDH`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{ABJb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BDHh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABKh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@Bj}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AO@`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AJ@b}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BAFf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BCBd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BGBj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABKl}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BECl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BCA`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BACh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@C`}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{B@Cj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBEh}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BEKd}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{AIMl}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BGNd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BDKl}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{CLd}}{AI@j{CAnAHEl}}}LF`}\",[]]Bg[\"{{{AI@j{Nl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{ABMn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{ABO`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AHEn}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BAGl}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BF@f}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{JGf}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bi[\"{{{AI@j{BGJb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AOAb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BEL`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BFGf}}{AI@j{CAnAHEl}}}LF`}\",[]]0Cd[\"{{{AI@j{JGd}}{AI@j{CAnAHEl}}}{{LFb{HIhJFb}}}}\",[]]0Bg[\"{{{AI@j{N`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AHDj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{JEj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{AOAf}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{OHb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BAFb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BEC`}}{AI@j{CAnAHEl}}}LF`}\",[]]Bh[\"{{{AI@j{JGh}}{AI@j{CAnAHEl}}}LF`}\",[]]0Bh[\"{{{AI@j{JGn}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{BBEj}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABMh}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{ABOb}}{AI@j{CAnAHEl}}}LF`}\",[]]Bi[\"{{{AI@j{B@Cl}}{AI@j{CAnAHEl}}}LF`}\",[]]Ak[\"{{{AI@j{B@Fh}}}ABJj}\",[]]A`[\"{ABOdCHl}\",[]]A`[\"{ABOfCHl}\",[]]Aj[\"{{{AI@j{BAFf}}}LAl}\",[]]A`[\"{BGBjCHl}\",[]]o[\"{CJlCHl}\",[]]A`[\"{ABMhLAl}\",[]]Ce[\"{{{AI@j{CAnBFNn}}{Ml{ABNf}}}{{AI@j{CAnBFNn}}}}\",[]]Ce[\"{{{AI@j{CAnBFO`}}{Ml{ABNh}}}{{AI@j{CAnBFO`}}}}\",[]]8Af[\"{{ABJjCHl}BBEb}\",[]]Ag[\"{{}{{Ml{BGBl}}}}\",[]]Ae[\"{{LAlCHj}BABh}\",[]]Cd[\"{{BBAn{LEh{B@G`}}AHI`{Ml{OIh}}}{{LFd{ABJn}}}}\",[]]Ah[\"{{{Nd{JJ`}}}AIOd}\",[]]A`[\"{OI`BCBf}\",[]]Ah[\"{{{Ml{OFn}}}BBAn}\",[]]Bc[\"{{{Ml{B@Gb}}{Ml{BECb}}}B@G`}\",[]]Ai[\"{{{Ml{B@Ab}}}B@A`}\",[]]B`[\"{{{AI@j{{KOh{Md}}}}}B@Bn}\",[]]Ae[\"{{CHjLAl}BDKj}\",[]]Ag[\"{{CLbNjCHl}ABMl}\",[]]Aj[\"{{{Ml{AHDh}}CHh}Mn}\",[]]An[\"{{{Ml{AOAd}}OGdCHh}JEh}\",[]]An[\"{{{Ml{BAF`}}OGdCHh}OH`}\",[]]Ae[\"{{LAlCHj}BABj}\",[]]Ah[\"{{{Nd{JJ`}}}AIOf}\",[]]Bm[\"{{{Ml{BBB`}}{LEh{{Ml{B@Gd}}}}CMf}ABK`}\",[]]A`[\"{LAlBHAf}\",[]]o[\"{{}BGMj}\",[]]A`[\"{OI`BCBh}\",[]]Ah[\"{{{Ml{OG`}}}BBB`}\",[]]Bc[\"{{{Ml{B@Gf}}{Ml{BECd}}}B@Gd}\",[]]Ai[\"{{{Ml{B@Af}}}B@Ad}\",[]]Ek[\"{{ABIh{AI@j{c}}{AI@j{{KOh{CHj}}}}CMf}{{LFb{{LAj{BAFf{LEh{ABIh}}}}BECl}}}{BEGjJIh}}\",[\"\"]]B`[\"{{{AI@j{{KOh{Md}}}}}B@C`}\",[]]Af[\"{{BEKdLGj}BBEh}\",[]]A`[\"{LAlBGNd}\",[]]Ae[\"{{CHjLAl}BDKl}\",[]]Ag[\"{{CLdNlCHl}ABMn}\",[]]Aj[\"{{{Ml{AHDj}}CHh}N`}\",[]]An[\"{{{Ml{AOAf}}OGdCHh}JEj}\",[]]An[\"{{{Ml{BAFb}}OGdCHh}OHb}\",[]]Aj[\"{AJB`{{LFd{BBEj}}}}\",[]]Ai[\"{{{Ml{BDKd}}}BGHd}\",[]]Ai[\"{{{Ml{BDKh}}}BGHf}\",[]]o[\"{CLbCLb}\",[]]o[\"{CLdCLd}\",[]]Ak[\"{{{AI@j{AJAd}}}AJAb}\",[]]Ai[\"{{{AI@j{OIh}}}OGd}\",[]]0Aj[\"{{{AI@j{B@Gn}}}OGd}\",[]]Ak[\"{{{AI@j{BAGh}}}AJAb}\",[]]Ak[\"{{{AI@j{AJAf}}}AJAb}\",[]]Ak[\"{{{AI@j{BAGj}}}AJAb}\",[]]Bg[\"{{{AI@j{AILh}}LEl}{{LEh{ABJj}}}}\",[]]Aa[\"{AHHbABJj}\",[]]Bg[\"{{{AI@j{AILj}}LEn}{{LEh{ABJj}}}}\",[]]Aa[\"{AHHdABJj}\",[]]Ag[\"{Ol{{LFf{HIh}}}}\",[]]Ag[\"{On{{LFf{HIh}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Aa[\"{B@EdAJ@l}\",[]]Aa[\"{ABMhAJ@l}\",[]]Aa[\"{BBEhBEKd}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]A`[\"{ABJbJDn}\",[]]Am[\"{{{AI@j{CAnABLj}}}HIh}\",[]]Am[\"{{{AI@j{CAnABKf}}}HIh}\",[]]Am[\"{{{AI@j{CAnAIOj}}}HIh}\",[]]Am[\"{{{AI@j{CAnAJ@f}}}HIh}\",[]]Al[\"{{{AI@j{CAnLCd}}}HIh}\",[]]A`[\"{cc{}}\",[\"T\"]]0000000000000000000000Aa[\"{BC@jBECn}\",[]]111111111Aa[\"{AHHhBBDn}\",[]]2Aa[\"{BC@jAHHh}\",[]]Aa[\"{AIOnAHHh}\",[]]4Aa[\"{BC@jAIOn}\",[]]5Aa[\"{AHHhBBDl}\",[]]66666Aa[\"{BC@jAJBb}\",[]]Aa[\"{B@GlAJBb}\",[]]Aa[\"{B@AlAJBb}\",[]]Aa[\"{BC@jB@Gl}\",[]]::::::A`[\"{BEKbLGn}\",[]];o[\"{CHjLGn}\",[]]A`[\"{ABMlLGn}\",[]]n[\"{MdLGn}\",[]]o[\"{CHhLGn}\",[]]???Al[\"{cLEl{{CJf{CHj}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]00000Aa[\"{AO@hBC@j}\",[]]A`[\"{JFnBC@j}\",[]]A`[\"{JEfBC@j}\",[]]33333A`[\"{LGnAOAn}\",[]]44444Aa[\"{BFGlAO@j}\",[]]5Aa[\"{BCChAO@j}\",[]]66A`[\"{JFnBC@d}\",[]]77Aa[\"{BEKjB@Bh}\",[]]888A`[\"{LGnBEKb}\",[]]9999A`[\"{LGnABMl}\",[]]::::::::::::Aa[\"{AODfAOA`}\",[]];;;;;;;;A`[\"{BGK`JGb}\",[]]A`[\"{BDIdJGb}\",[]]A`[\"{BBBlJGb}\",[]]A`[\"{BBCnJGb}\",[]]A`[\"{BDJlJGb}\",[]]A`[\"{BDHbJGb}\",[]]A`[\"{BGE`JGb}\",[]]A`[\"{BGInJGb}\",[]]A`[\"{BCBlJGb}\",[]]A`[\"{BCAfJGb}\",[]]A`[\"{AINhJGb}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000o[\"{CHjLH`}\",[]]A`[\"{ABMnLH`}\",[]]A`[\"{BEKdLH`}\",[]]n[\"{MdLH`}\",[]]o[\"{CHhLH`}\",[]]5555Al[\"{cLEn{{CJf{CHj}}}}\",[\"T\"]]6666666666666666666Aa[\"{BBEhBC@l}\",[]]7777A`[\"{AHH`JGl}\",[]]8888A`[\"{LH`AOB`}\",[]]99999Aa[\"{BCChAO@l}\",[]]Aa[\"{BFGnAO@l}\",[]];;;;;;;Aa[\"{BBEhBC@h}\",[]]<<<>>>>A`[\"{JGfBBEh}\",[]]?A`[\"{LH`BEKd}\",[]]A`[\"{cc{}}\",[\"T\"]]00000A`[\"{LH`ABMn}\",[]]1111A`[\"{BAGlJGf}\",[]]2222Aa[\"{AODfAOAb}\",[]]3333333A`[\"{BDHbJGd}\",[]]A`[\"{BCAfJGd}\",[]]A`[\"{BCBlJGd}\",[]]6A`[\"{BGK`JGd}\",[]]A`[\"{BGInJGd}\",[]]A`[\"{AINhJGd}\",[]]A`[\"{BGE`JGd}\",[]]A`[\"{BDIdJGd}\",[]]A`[\"{BBBlJGd}\",[]]A`[\"{BBCnJGd}\",[]]A`[\"{BDJlJGd}\",[]]>>>>>>>>>An[\"{{{AI@j{c}}}AJB`CIb}\",[\"S\"]]????????????????Bm[\"{{{AI@j{LGn}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bk[\"{{{AI@j{LEl}}{AI@j{CAnc}}}HIhLDj}\",[\"H\"]]Bn[\"{{{AI@j{AOAn}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{B@Bn}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bm[\"{{{AI@j{CLb}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bl[\"{{{AI@j{Nj}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{ABMl}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bm[\"{{{AI@j{LH`}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{BED`}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bk[\"{{{AI@j{LEn}}{AI@j{CAnc}}}HIhLDj}\",[\"H\"]]Bn[\"{{{AI@j{AHHj}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{BAG`}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{AJBd}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{AOB`}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{BDHh}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{BECl}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{B@C`}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bm[\"{{{AI@j{CLd}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bl[\"{{{AI@j{Nl}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{ABMn}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Bn[\"{{{AI@j{AHEn}}{AI@j{CAnc}}}HIhLDj}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HIh}\",[]]0000Cc[\"{{{AI@j{CAnLCd}}LAl}{{LFb{{LEh{JEb}}AHHj}}}}\",[]]El[\"{{{AI@j{OEf}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{{AI@j{CAn{KOh{Md}}}}AOA`}}}}\",[]]Bh[\"{{{AI@j{CAnOJh}}Nl}{{LEh{ABMn}}}}\",[]]El[\"{{{AI@j{OEh}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{{AI@j{CAn{KOh{Md}}}}AOAb}}}}\",[]]Bf[\"{{{AI@j{AILh}}LEl}{{LEh{CKd}}}}\",[]]Bf[\"{{{AI@j{CKd}}}{{LFb{HIhAIKj}}}}\",[]]A`[\"{AIMdCHl}\",[]]Am[\"{{{AI@j{CAnAILj}}}HIh}\",[]]A`[\"{AIMfCHl}\",[]]Ck[\"{{{A@b{{AI@j{CAnAIKb}}}}{AI@j{CAnOFb}}}{{CKl{HIh}}}}\",[]]Dk[\"{{{A@b{{AI@j{CAnBCCn}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnABLj}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnABLl}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnAHDn}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnAILd}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@j{CAnOHf}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@j{CAnLDn}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnAHCj}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnABIf}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnB@F`}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnB@Fj}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@j{CAnLBd}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@j{CAnBBBh}}}}{AI@j{CAnOFb}}}{{CKl{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AI@j{CAnAILj}}}{{LEh{JGl}}}}\",[]]De[\"{{{AI@j{CAnAIOj}}{AI@j{CAn{KOh{Md}}}}}{{LFb{{LEh{LAl}}AHHh}}}}\",[]]Bm[\"{{{AI@j{CAnAIOl}}CMf}{{LFb{LCdBAG`}}}}\",[]]Bo[\"{{CLd{AI@j{CAnc}}}{{LFb{BF@fJGf}}}Nf}\",[\"R\"]]Bf[\"{{{AI@j{CAnAHEf}}}{{LEh{JDn}}}}\",[]]Ec[\"{{{AI@j{BBEj}}AJB`{AI@j{CAn{KOh{AIMn}}}}{AI@j{CAn{KOh{ABMh}}}}}{{LFd{LAl}}}}\",[]]Dk[\"{{{AI@j{OEf}}{AI@j{CAn{A@h{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhAOA`}}}}\",[]]Dk[\"{{{AI@j{OEh}}{AI@j{CAn{A@h{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhAOAb}}}}\",[]]C`[\"{{{AI@j{CAnAHEf}}JDnCMf}{{LFb{HIhBED`}}}}\",[]]Cc[\"{{{AI@j{BBEj}}AJB`{AI@j{ABOb}}}{{LFd{HIh}}}}\",[]]Bn[\"{{{AI@j{CAnBFNn}}CHj}{{AI@j{CAnBFNn}}}}\",[]]Bn[\"{{{AI@j{CAnBFO`}}CHj}{{AI@j{CAnBFO`}}}}\",[]]Aj[\"{{{AI@j{AILh}}}CLb}\",[]]Aj[\"{{{AI@j{AILj}}}CLd}\",[]]Cn[\"{{{AI@j{OFn}}{AI@j{{KOh{Md}}}}{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]Cn[\"{{{AI@j{OG`}}{AI@j{{KOh{Md}}}}{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]A`[\"{BBEbCHl}\",[]]A`[\"{ABNnLAl}\",[]]A`[\"{ABO`LAl}\",[]]o[\"{JIdCMf}\",[]]Bm[\"{{{AI@j{CAnAIOj}}LGn}{{LFb{HIhB@Al}}}}\",[]]Bm[\"{{{AI@j{CAnBF@h}}LGn}{{LFb{HIhB@Al}}}}\",[]]Bm[\"{{{AI@j{CAnAIOl}}LH`}{{LFb{HIhB@Bd}}}}\",[]]Bn[\"{{{AI@j{AJAl}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Bn[\"{{{AI@j{BBCj}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Bn[\"{{{AI@j{BDKj}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Bn[\"{{{AI@j{AJAn}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Bn[\"{{{AI@j{BBCl}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Bn[\"{{{AI@j{BDKl}}{AI@j{CHf}}}{{LEh{JDn}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{Ml{BEBj}}OGdCHh}{{Nd{AILl}}}}\",[]]Bg[\"{{{Ml{AHDh}}OGdCHh}{{Nd{AILl}}}}\",[]]Bg[\"{{{Ml{AOAd}}OGdCHh}{{Nd{AILl}}}}\",[]]Bg[\"{{{Ml{BAF`}}OGdCHh}{{Nd{AILl}}}}\",[]]Bg[\"{{{Ml{BEBl}}OGdCHh}{{Nd{AILn}}}}\",[]]Bg[\"{{{Ml{AHDj}}OGdCHh}{{Nd{AILn}}}}\",[]]Bg[\"{{{Ml{AOAf}}OGdCHh}{{Nd{AILn}}}}\",[]]Bg[\"{{{Ml{BAFb}}OGdCHh}{{Nd{AILn}}}}\",[]]Ai[\"{BEOh{{Ml{BEBl}}}}\",[]]o[\"{JE`JDn}\",[]]A`[\"{ABOdCHl}\",[]]o[\"{OKdLAl}\",[]]o[\"{JEbJDn}\",[]]A`[\"{ABOfCHl}\",[]]o[\"{OKfLAl}\",[]]Aa[\"{AO@`BC@l}\",[]]Ag[\"{{{AI@j{Oj}}}Oj}\",[]]Ak[\"{{{AI@j{AO@d}}}AO@d}\",[]]Ak[\"{{{AI@j{AILh}}}AILh}\",[]]Ak[\"{{{AI@j{BFHb}}}BFHb}\",[]]Ak[\"{{{AI@j{BECn}}}BECn}\",[]]Ak[\"{{{AI@j{ABJn}}}ABJn}\",[]]Ak[\"{{{AI@j{BACj}}}BACj}\",[]]Ak[\"{{{AI@j{BBDn}}}BBDn}\",[]]Ak[\"{{{AI@j{AHHh}}}AHHh}\",[]]Ak[\"{{{AI@j{AIOn}}}AIOn}\",[]]Ak[\"{{{AI@j{BBDl}}}BBDl}\",[]]Ak[\"{{{AI@j{AJBb}}}AJBb}\",[]]Ak[\"{{{AI@j{B@Gl}}}B@Gl}\",[]]Ai[\"{{{AI@j{LGn}}}LGn}\",[]]Ak[\"{{{AI@j{BFGl}}}BFGl}\",[]]Ai[\"{{{AI@j{LEl}}}LEl}\",[]]Ak[\"{{{AI@j{ABOd}}}ABOd}\",[]]Ak[\"{{{AI@j{AIMd}}}AIMd}\",[]]Ak[\"{{{AI@j{AHHb}}}AHHb}\",[]]Ak[\"{{{AI@j{BCAb}}}BCAb}\",[]]Ai[\"{{{AI@j{OKd}}}OKd}\",[]]Ak[\"{{{AI@j{B@Al}}}B@Al}\",[]]Ak[\"{{{AI@j{BC@j}}}BC@j}\",[]]Ak[\"{{{AI@j{BCCd}}}BCCd}\",[]]Ak[\"{{{AI@j{BEFn}}}BEFn}\",[]]Ak[\"{{{AI@j{BEHl}}}BEHl}\",[]]Ak[\"{{{AI@j{AOAn}}}AOAn}\",[]]Ak[\"{{{AI@j{BBAn}}}BBAn}\",[]]Ak[\"{{{AI@j{B@G`}}}B@G`}\",[]]Ak[\"{{{AI@j{BFNn}}}BFNn}\",[]]Ak[\"{{{AI@j{B@A`}}}B@A`}\",[]]Ak[\"{{{AI@j{AO@j}}}AO@j}\",[]]Ak[\"{{{AI@j{AHF`}}}AHF`}\",[]]Ak[\"{{{AI@j{BC@d}}}BC@d}\",[]]Ak[\"{{{AI@j{BDGl}}}BDGl}\",[]]Ak[\"{{{AI@j{B@Bh}}}B@Bh}\",[]]Ak[\"{{{AI@j{B@Bn}}}B@Bn}\",[]]Ak[\"{{{AI@j{B@Ch}}}B@Ch}\",[]]Ak[\"{{{AI@j{BEKb}}}BEKb}\",[]]Ak[\"{{{AI@j{AIMj}}}AIMj}\",[]]Ai[\"{{{AI@j{CLb}}}CLb}\",[]]Ag[\"{{{AI@j{Nj}}}Nj}\",[]]Ak[\"{{{AI@j{ABMl}}}ABMl}\",[]]Ag[\"{{{AI@j{Mn}}}Mn}\",[]]Ak[\"{{{AI@j{AHDh}}}AHDh}\",[]]Ai[\"{{{AI@j{JEh}}}JEh}\",[]]Ak[\"{{{AI@j{AOAd}}}AOAd}\",[]]Ai[\"{{{AI@j{OH`}}}OH`}\",[]]Ak[\"{{{AI@j{BAF`}}}BAF`}\",[]]Ak[\"{{{AI@j{BFGd}}}BFGd}\",[]]Ai[\"{{{AI@j{JGb}}}JGb}\",[]]Ai[\"{{{AI@j{LH`}}}LH`}\",[]]Ak[\"{{{AI@j{BFGn}}}BFGn}\",[]]Ak[\"{{{AI@j{BED`}}}BED`}\",[]]Ai[\"{{{AI@j{LEn}}}LEn}\",[]]Ak[\"{{{AI@j{B@Fh}}}B@Fh}\",[]]Ak[\"{{{AI@j{AIOb}}}AIOb}\",[]]Ak[\"{{{AI@j{AHH`}}}AHH`}\",[]]Ak[\"{{{AI@j{BEJl}}}BEJl}\",[]]Ak[\"{{{AI@j{AIL`}}}AIL`}\",[]]Ak[\"{{{AI@j{ABOf}}}ABOf}\",[]]Ak[\"{{{AI@j{AIMf}}}AIMf}\",[]]Ak[\"{{{AI@j{AHHd}}}AHHd}\",[]]Ak[\"{{{AI@j{BCAd}}}BCAd}\",[]]Ak[\"{{{AI@j{AHHj}}}AHHj}\",[]]Ak[\"{{{AI@j{BAG`}}}BAG`}\",[]]Ai[\"{{{AI@j{OKf}}}OKf}\",[]]Ak[\"{{{AI@j{AJBd}}}AJBd}\",[]]Ak[\"{{{AI@j{AOAl}}}AOAl}\",[]]Ak[\"{{{AI@j{BBE`}}}BBE`}\",[]]Ak[\"{{{AI@j{B@Bd}}}B@Bd}\",[]]Ak[\"{{{AI@j{BC@l}}}BC@l}\",[]]Ak[\"{{{AI@j{AHGj}}}AHGj}\",[]]Ak[\"{{{AI@j{BB@l}}}BB@l}\",[]]Ak[\"{{{AI@j{BGBd}}}BGBd}\",[]]Ak[\"{{{AI@j{BCCf}}}BCCf}\",[]]Ak[\"{{{AI@j{BEG`}}}BEG`}\",[]]Ak[\"{{{AI@j{BEHn}}}BEHn}\",[]]Ak[\"{{{AI@j{AOB`}}}AOB`}\",[]]Ak[\"{{{AI@j{BBB`}}}BBB`}\",[]]Ak[\"{{{AI@j{B@Gd}}}B@Gd}\",[]]Ak[\"{{{AI@j{BFO`}}}BFO`}\",[]]Ak[\"{{{AI@j{B@Ad}}}B@Ad}\",[]]Ak[\"{{{AI@j{AO@l}}}AO@l}\",[]]Ak[\"{{{AI@j{AHFb}}}AHFb}\",[]]Ak[\"{{{AI@j{AINb}}}AINb}\",[]]Ak[\"{{{AI@j{AJ@n}}}AJ@n}\",[]]Ak[\"{{{AI@j{B@Cb}}}B@Cb}\",[]]Ak[\"{{{AI@j{BC@h}}}BC@h}\",[]]Ak[\"{{{AI@j{BDH`}}}BDH`}\",[]]Ak[\"{{{AI@j{ABJb}}}ABJb}\",[]]Ak[\"{{{AI@j{BDHh}}}BDHh}\",[]]Ak[\"{{{AI@j{B@Bj}}}B@Bj}\",[]]Ak[\"{{{AI@j{BCBd}}}BCBd}\",[]]Ak[\"{{{AI@j{BGBj}}}BGBj}\",[]]Ak[\"{{{AI@j{ABKl}}}ABKl}\",[]]Ak[\"{{{AI@j{BECl}}}BECl}\",[]]Ak[\"{{{AI@j{B@C`}}}B@C`}\",[]]Ak[\"{{{AI@j{B@Cj}}}B@Cj}\",[]]Ak[\"{{{AI@j{BBEh}}}BBEh}\",[]]Ak[\"{{{AI@j{BEKd}}}BEKd}\",[]]Ak[\"{{{AI@j{AIMl}}}AIMl}\",[]]Ak[\"{{{AI@j{BGNd}}}BGNd}\",[]]Ai[\"{{{AI@j{CLd}}}CLd}\",[]]Ag[\"{{{AI@j{Nl}}}Nl}\",[]]Ak[\"{{{AI@j{ABMn}}}ABMn}\",[]]Ak[\"{{{AI@j{AHEn}}}AHEn}\",[]]Ak[\"{{{AI@j{BAGl}}}BAGl}\",[]]Ak[\"{{{AI@j{BF@f}}}BF@f}\",[]]Ai[\"{{{AI@j{JGf}}}JGf}\",[]]Ak[\"{{{AI@j{BFGf}}}BFGf}\",[]]Ai[\"{{{AI@j{JGd}}}JGd}\",[]]Ag[\"{{{AI@j{N`}}}N`}\",[]]Ak[\"{{{AI@j{AHDj}}}AHDj}\",[]]Ai[\"{{{AI@j{JEj}}}JEj}\",[]]Ak[\"{{{AI@j{AOAf}}}AOAf}\",[]]Ai[\"{{{AI@j{OHb}}}OHb}\",[]]Ak[\"{{{AI@j{BAFb}}}BAFb}\",[]]Ai[\"{{{AI@j{JGn}}}JGn}\",[]]Ak[\"{{{AI@j{ABMh}}}ABMh}\",[]]Ak[\"{{{AI@j{ABOb}}}ABOb}\",[]]Ak[\"{{{AI@j{B@Cl}}}B@Cl}\",[]]Ak[\"{{{AI@j{BEOh}}}BEOh}\",[]]Ak[\"{{{AI@j{BCAl}}}BCAl}\",[]]Bn[\"{{{AI@j{AILh}}LGn{AI@j{{KOh{Md}}}}}HIh}\",[]]Bn[\"{{{AI@j{ABJn}}LGn{AI@j{{KOh{Md}}}}}HIh}\",[]]Bg[\"{{{AI@j{CKd}}}{{LFb{AHDnBB@j}}}}\",[]]Bf[\"{{{AI@j{CAnAILj}}OGdLH`JDn}HIh}\",[]]Bj[\"{{{AI@j{B@Bn}}}{{AI@j{{KOh{Md}}}}}}\",[]]Bj[\"{{{AI@j{B@C`}}}{{AI@j{{KOh{Md}}}}}}\",[]]Aa[\"{BADnAHGj}\",[]]Aa[\"{BBEhAINb}\",[]]A`[\"{ABMlCHl}\",[]]A`[\"{ABMnCHl}\",[]]A`[\"{B@ChCMf}\",[]]A`[\"{B@CjCMf}\",[]]l[\"{OGf}\",[]]l[\"{OGh}\",[]]Ce[\"{{{AI@j{BEGj}}{AI@j{CAnNf}}}{{LFb{B@C`BECl}}}}\",[]]Ce[\"{{{AI@j{BHAf}}{AI@j{CAnNf}}}{{LFb{B@C`BECl}}}}\",[]]Bc[\"{{{AI@j{AILj}}}{{A@h{LEn}}}}\",[]]Bd[\"{{{AI@j{JIj}}{AI@j{CHf}}}HIh}\",[]]Am[\"{{{AI@j{AO@f}}LAl}HIh}\",[]]Ai[\"{{{AI@j{JIl}}}HIh}\",[]]Bg[\"{{{A@b{{AI@j{CAnAIKb}}}}OGd}HIh}\",[]]Bm[\"{{{AI@j{CAnAJ@f}}LGn}{{LFb{HIhB@Al}}}}\",[]]Bm[\"{{{AI@j{CAnAJ@h}}LH`}{{LFb{HIhB@Bd}}}}\",[]]Am[\"{ABKf{{LFb{HIhAJ@`}}}}\",[]]Dc[\"{{{AI@j{CAnABK`}}ABKh{AI@j{CAn{A@h{Md}}}}}{{LFb{ABO`AJ@b}}}}\",[]]Bi[\"{{{AI@j{OIh}}{A@b{{Nd{LDh}}}}}HIh}\",[]]Bj[\"{{{AI@j{B@Gn}}{A@b{{Nd{LDh}}}}}HIh}\",[]]Bj[\"{{{AI@j{AOCh}}{A@b{{Nd{LDh}}}}}HIh}\",[]]n[\"{OjCMf}\",[]]Ak[\"{{{AI@j{AILh}}}BCAb}\",[]]Ak[\"{{{AI@j{ABJn}}}BACj}\",[]]An[\"{{{AI@j{CAnAILj}}}BCAd}\",[]]Ce[\"{{{AI@j{CAnAIOd}}{LEh{LGj}}}{{AI@j{CAnAIOd}}}}\",[]]Ce[\"{{{AI@j{CAnAIOf}}{LEh{LGj}}}{{AI@j{CAnAIOf}}}}\",[]]Ck[\"{{{AI@j{CAnAJ@f}}{AI@j{{KOh{Md}}}}}{{LFb{LAlAJBb}}}}\",[]]Ck[\"{{{AI@j{CAnAJ@h}}{AI@j{{KOh{Md}}}}}{{LFb{LAlAJBd}}}}\",[]]Bl[\"{{{AI@j{BF@f}}{AI@j{CAnc}}}HIhLCb}\",[\"W\"]]Aj[\"{{{AI@j{ABJn}}}LBd}\",[]]An[\"{ABKf{{LFb{AILdBC@j}}}}\",[]]Bh[\"{{{AI@j{CAnOJh}}Nl}{{LEh{ABMn}}}}\",[]]Fh[\"{{{AI@j{CAnABK`}}ABKhOGd{AI@j{CAn{A@h{Md}}}}{LEh{{Ml{B@Gd}}}}}{{LFb{{LAj{BDHhAILj}}{Nd{AO@`}}}}}}\",[]]Am[\"{AI@j{{AI@j{c}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000o[\"{OKdLAl}\",[]]o[\"{OKfLAl}\",[]]o[\"{OjAO@d}\",[]]Bb[\"{{AJ@lBB@hOj}{{LFf{AO@f}}}}\",[]]Aj[\"{AJ@l{{LFd{ABJn}}}}\",[]]B`[\"{{}{{LFb{HIh{Nd{JFd}}}}}}\",[]]Ak[\"{{{AI@j{AILh}}}BC@j}\",[]]Be[\"{{{AI@j{ABK`}}}{{AI@j{BBB`}}}}\",[]]o[\"{MdBEKb}\",[]]A`[\"{AIMdCHl}\",[]]o[\"{B@G`Ml}\",[]]o[\"{MdBEKd}\",[]]A`[\"{AIMfCHl}\",[]]o[\"{B@GdMl}\",[]]A`[\"{BBEhLEh}\",[]]Bl[\"{{{AI@j{CAnc}}}{{LFb{LGnBAGl}}}Nf}\",[\"B\"]]Bl[\"{{{AI@j{CAnc}}}{{LFb{LElBAGl}}}Nf}\",[\"B\"]]Bm[\"{{{AI@j{CAnc}}}{{LFb{AHF`BAGl}}}Nf}\",[\"B\"]]Bm[\"{{{AI@j{CAnc}}}{{LFb{BEKbBAGl}}}Nf}\",[\"B\"]]Bm[\"{{{AI@j{CAnc}}}{{LFb{ABMlBAGl}}}Nf}\",[\"B\"]]Bh[\"{{{AI@j{CAnc}}}{{LFj{LH`}}}Nf}\",[\"B\"]]Bh[\"{{{AI@j{CAnc}}}{{LFj{LEn}}}Nf}\",[\"B\"]]Bi[\"{{{AI@j{CAnc}}}{{LFj{AHFb}}}Nf}\",[\"B\"]]Bi[\"{{{AI@j{CAnc}}}{{LFj{AINb}}}Nf}\",[\"B\"]]Ek[\"{{{AI@j{CAn{LDf{ABIh}}}}{AI@j{c}}{AI@j{{KOh{CHj}}}}CMf}{{LFb{BCBdBECl}}}{BEGjJIh}}\",[\"\"]]Bi[\"{{{AI@j{CAnc}}}{{LFj{BEKd}}}Nf}\",[\"B\"]]Bi[\"{{{AI@j{CAnc}}}{{LFj{ABMn}}}Nf}\",[\"B\"]]Bi[\"{{{AI@j{CAnc}}}{{LFj{AHEh}}}Nf}\",[\"B\"]]A`[\"{ABMhLEh}\",[]]Bk[\"{{{AI@j{LGn}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bk[\"{{{AI@j{LEl}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{AHF`}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{BEKb}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{ABMl}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bk[\"{{{AI@j{LH`}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bk[\"{{{AI@j{LEn}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{AHFb}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{AINb}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{ABJb}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{BEKd}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{ABMn}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bl[\"{{{AI@j{AHEj}}{AI@j{CAnc}}}HIhLCb}\",[\"B\"]]Bj[\"{{{AI@j{CAnAJ@f}}}{{LFb{HIhB@Al}}}}\",[]]Bj[\"{{{AI@j{CAnAJ@h}}}{{LFb{HIhAOAl}}}}\",[]]An[\"{{{AI@j{AODd}}ABJj}HIh}\",[]]Ea[\"{{{AI@j{CAnABK`}}OGdAHEn{LEh{B@Cj}}ABIh{AI@j{CAn{A@h{Md}}}}}{{LEh{BACf}}}}\",[]]o[\"{CJhOGf}\",[]]o[\"{CJjOGh}\",[]]A`[\"{ABKfHIh}\",[]]Ba[\"{{{AI@j{CAnABK`}}ABKh}HIh}\",[]]Bh[\"{{{AI@j{AJAl}}{AI@j{CHf}}JDn}HIh}\",[]]Bh[\"{{{AI@j{BBCj}}{AI@j{CHf}}JDn}HIh}\",[]]Bh[\"{{{AI@j{BDKj}}{AI@j{CHf}}JDn}HIh}\",[]]Bh[\"{{{AI@j{AJAn}}{AI@j{CHf}}JDn}HIh}\",[]]Bh[\"{{{AI@j{BBCl}}{AI@j{CHf}}JDn}HIh}\",[]]Bh[\"{{{AI@j{BDKl}}{AI@j{CHf}}JDn}HIh}\",[]]A`[\"{AHE`CMf}\",[]]o[\"{JE`CHl}\",[]]o[\"{JEbCHl}\",[]]Ce[\"{{{AI@j{CAnAILl}}OGdOGdCHlCMf{AI@j{B@Ff}}}HIh}\",[]]Cc[\"{{{AI@j{CAnMn}}OGdOGdCHlCMf{AI@j{B@Ff}}}HIh}\",[]]Cd[\"{{{AI@j{CAnJEh}}OGdOGdCHlCMf{AI@j{B@Ff}}}HIh}\",[]]Cd[\"{{{AI@j{CAnOH`}}OGdOGdCHlCMf{AI@j{B@Ff}}}HIh}\",[]]Ce[\"{{{AI@j{CAnAILn}}OGdOGdCHlCMf{AI@j{B@Fh}}}HIh}\",[]]Cc[\"{{{AI@j{CAnN`}}OGdOGdCHlCMf{AI@j{B@Fh}}}HIh}\",[]]Cd[\"{{{AI@j{CAnJEj}}OGdOGdCHlCMf{AI@j{B@Fh}}}HIh}\",[]]Cd[\"{{{AI@j{CAnOHb}}OGdOGdCHlCMf{AI@j{B@Fh}}}HIh}\",[]]o[\"{CJhOGf}\",[]]o[\"{CJjOGh}\",[]]o[\"{BDLbMd}\",[]]A`[\"{BC@dJDn}\",[]]A`[\"{BDGlJDn}\",[]]A`[\"{BC@hJDn}\",[]]A`[\"{BDH`JDn}\",[]]A`[\"{BBEhLGj}\",[]]Aa[\"{BBA`BC@l}\",[]]Bg[\"{{{AI@j{ABJn}}AHI`}{{LFd{HIh}}}}\",[]]A`[\"{ABKfHIh}\",[]]Cf[\"{{{AI@j{CAnABK`}}ABKh{AI@j{CAn{A@h{Md}}}}}ABO`}\",[]]l[\"{OGf}\",[]]Aa[\"{AHEnAJ@l}\",[]]l[\"{OGh}\",[]]Am[\"{{ABJlOj}{{LFf{HIh}}}}\",[]]B`[\"{{B@G`AJ@l}{{LFd{ABJn}}}}\",[]]Bb[\"{B@Fn{{LFb{HIh{Nd{JFd}}}}}}\",[]]Bk[\"{{{AI@j{BECn}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{BBDn}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{AHHh}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{AIOn}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{BBDl}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{AJBb}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{B@Gl}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{BC@j}}}{{LEh{{AI@j{JFd}}}}}}\",[]]Bk[\"{{{AI@j{BC@l}}}{{LEh{{AI@j{JFd}}}}}}\",[]]A`[\"{ABNnLEh}\",[]]A`[\"{ABO`LEh}\",[]]A`[\"{ABObLEh}\",[]]Bg[\"{{{AI@j{CKd}}}{{LFb{AIO`AIKj}}}}\",[]]Aa[\"{B@FbAIOb}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]A`[\"{ABMhLAl}\",[]]Aa[\"{BCAbABOd}\",[]]Aa[\"{BCAdABOf}\",[]]10Dh[\"{{{AI@j{OFn}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhAOA`}}}}\",[]]Dh[\"{{{AI@j{OG`}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhAOAb}}}}\",[]]Aj[\"{{{AI@j{AILl}}}CHl}\",[]]Ah[\"{{{AI@j{Mn}}}CHl}\",[]]Ai[\"{{{AI@j{JEh}}}CHl}\",[]]Ai[\"{{{AI@j{OH`}}}CHl}\",[]]Aj[\"{{{AI@j{AILn}}}CHl}\",[]]Ah[\"{{{AI@j{N`}}}CHl}\",[]]Ai[\"{{{AI@j{JEj}}}CHl}\",[]]Ai[\"{{{AI@j{OHb}}}CHl}\",[]]Aj[\"{{{AI@j{BFMl}}}LAl}\",[]]Aj[\"{{{AI@j{BFMn}}}LAl}\",[]]Aj[\"{{{AI@j{BGMj}}}LAl}\",[]]Aj[\"{{{AI@j{BGNd}}}LAl}\",[]]n[\"{OjLAl}\",[]]n[\"{{}OF`}\",[]]0000o[\"{NhABIl}\",[]]Ba[\"{{AI@j{AI@j{c}}}ABLn{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AI@j{ABJn}}AJ@l{AI@j{CHf}}}{{LFb{AILdB@Bh}}}}\",[]]Dl[\"{{{AI@j{CAnABK`}}OGdB@AdAJ@l{AI@j{CHf}}}{{LFb{{LAj{BDHhAILj}}B@Bj}}}}\",[]]Dm[\"{{{AI@j{AHGd}}LElCHl{AI@j{{KOh{Md}}}}{AI@j{CAnABIh}}}{{LFb{HIhAOA`}}}}\",[]]Ca[\"{{{AI@j{AHFf}}LAl{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]Dm[\"{{{AI@j{AHGf}}LEnCHl{AI@j{{KOh{Md}}}}{AI@j{CAnABIh}}}{{LFb{HIhAOAb}}}}\",[]]Ca[\"{{{AI@j{AHFh}}LAl{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]o[\"{{}AODb}\",[]]n[\"{{}JIj}\",[]]o[\"{{}AO@f}\",[]]o[\"{{}BABh}\",[]]o[\"{{}BACj}\",[]]n[\"{{}LGn}\",[]]n[\"{{}LEl}\",[]]o[\"{{}ABOd}\",[]]o[\"{{}AIMd}\",[]]o[\"{{}AHHb}\",[]]o[\"{{}BCAb}\",[]]n[\"{{}OKd}\",[]]o[\"{{}B@Al}\",[]]o[\"{{}BCCd}\",[]]o[\"{{}BEFn}\",[]]o[\"{{}BEHl}\",[]]o[\"{{}AOAn}\",[]]o[\"{{}BBAn}\",[]]o[\"{{}BFNn}\",[]]o[\"{{}BDKj}\",[]]o[\"{{}AHDh}\",[]]o[\"{{}AOAd}\",[]]o[\"{{}BAF`}\",[]]o[\"{{}BEBn}\",[]]o[\"{{}BABj}\",[]]o[\"{{}BGMj}\",[]]n[\"{{}LH`}\",[]]n[\"{{}LEn}\",[]]o[\"{{}AIOb}\",[]]o[\"{{}AIL`}\",[]]o[\"{{}ABOf}\",[]]o[\"{{}AIMf}\",[]]o[\"{{}AHHd}\",[]]o[\"{{}BCAd}\",[]]n[\"{{}OKf}\",[]]o[\"{{}BBE`}\",[]]o[\"{{}B@Bd}\",[]]o[\"{{}BCCf}\",[]]o[\"{{}BEG`}\",[]]o[\"{{}BEHn}\",[]]o[\"{{}AOB`}\",[]]o[\"{{}BBB`}\",[]]o[\"{{}BFO`}\",[]]o[\"{{}BGNd}\",[]]o[\"{{}BDKl}\",[]]o[\"{{}AHDj}\",[]]o[\"{{}AOAf}\",[]]o[\"{{}BAFb}\",[]]o[\"{{}BEC`}\",[]]o[\"{{}ABMh}\",[]]n[\"{{}JIl}\",[]]o[\"{{}BCAl}\",[]]o[\"{{}BACh}\",[]]Ak[\"{{{AI@j{BAFf}}}B@C`}\",[]]Ak[\"{{{AI@j{BCBd}}}B@C`}\",[]]Aa[\"{BGBjB@C`}\",[]]Aa[\"{BEKfB@C`}\",[]]A`[\"{CJlB@C`}\",[]]A`[\"{JI`B@C`}\",[]]A`[\"{JIdB@C`}\",[]]Aa[\"{BDLbB@C`}\",[]]Cg[\"{{{AI@j{AHGd}}LElCHl{AI@j{CAn{KOh{Md}}}}LAl}HIh}\",[]]Ca[\"{{{AI@j{AHFf}}LAl{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]Cg[\"{{{AI@j{AHGf}}LEnCHl{AI@j{CAn{KOh{Md}}}}LAl}HIh}\",[]]Ca[\"{{{AI@j{AHFh}}LAl{AI@j{CAn{KOh{Md}}}}}HIh}\",[]]Aj[\"{{{AI@j{AIOj}}}CMf}\",[]]Aj[\"{{{AI@j{BF@h}}}CMf}\",[]]Ak[\"{{{AI@j{AILl}}}BEBn}\",[]]Ai[\"{{{AI@j{Mn}}}BEBn}\",[]]Aj[\"{{{AI@j{JEh}}}BEBn}\",[]]Aj[\"{{{AI@j{OH`}}}BEBn}\",[]]Ak[\"{{{AI@j{AILn}}}BEC`}\",[]]Ai[\"{{{AI@j{N`}}}BEC`}\",[]]Aj[\"{{{AI@j{JEj}}}BEC`}\",[]]Aj[\"{{{AI@j{OHb}}}BEC`}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CHh}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CHh}{{AI@j{CAnBCCf}}}}\",[]]Bf[\"{{{AI@j{CAnAILl}}OGdCHlCHl}HIh}\",[]]Bd[\"{{{AI@j{CAnMn}}OGdCHlCHl}HIh}\",[]]Bf[\"{{{AI@j{CAnAILn}}OGdCHlCHl}HIh}\",[]]Bd[\"{{{AI@j{CAnN`}}OGdCHlCHl}HIh}\",[]]Aj[\"{{{AI@j{AILh}}}LDn}\",[]]Bc[\"{{{AI@j{ABLl}}}{{LEh{LEl}}}}\",[]]Aa[\"{BGBjB@C`}\",[]]Aa[\"{BEKfB@C`}\",[]]A`[\"{CJlB@C`}\",[]]A`[\"{JI`B@C`}\",[]]Aa[\"{BDLbB@C`}\",[]]Di[\"{{{AI@j{AJ@f}}}{{`{{LDh{}{{LEj{{LFb{{LEh{LGn}}B@Gl}}}}}}CL`CLf}}}}\",[]]Bn[\"{{{AI@j{AJ@h}}}{{LFb{{LEh{LH`}}B@Bd}}}}\",[]]n[\"{OjLAl}\",[]]o[\"{JIjLAl}\",[]]Am[\"{{{AI@j{CAnAILj}}}OJh}\",[]]Aj[\"{{{AI@j{AHGd}}}LAl}\",[]]Aj[\"{{{AI@j{AHGf}}}LAl}\",[]]A`[\"{AI@jLGl}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AI@j{BFHb}}}{{LEh{AILh}}}}\",[]]Bn[\"{{{AI@j{CAnB@A`}}CHj}{{AI@j{CAnB@A`}}}}\",[]]Bn[\"{{{AI@j{CAnB@Ad}}CHj}{{AI@j{CAnB@Ad}}}}\",[]]A`[\"{BGBjCHj}\",[]]A`[\"{BEKfCHj}\",[]]o[\"{CJlCHj}\",[]]o[\"{JI`CHj}\",[]]A`[\"{AHE`LEh}\",[]]Aa[\"{ABObAI@j}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Aa[\"{BBEbABJj}\",[]]A`[\"{LCdBBE`}\",[]]Aa[\"{BCAbAIMd}\",[]]Aa[\"{BCAdAIMf}\",[]]10Bg[\"{{{AI@j{CAnc}}LAl}B@Bn{NfJIh}}\",[\"\"]]Bg[\"{{{AI@j{CAnc}}LAl}B@C`{NfJIh}}\",[\"\"]]o[\"{{}BCBf}\",[]]o[\"{{}BCBh}\",[]]A`[\"{CHlBGMj}\",[]]Cd[\"{{{AI@j{CHf}}}{{LFf{AO@dc}}}{}}\",[\"FromStr::Err\"]]o[\"{CHjLGn}\",[]]o[\"{CHjLH`}\",[]]Al[\"{CHl{{LFb{LGnBFGl}}}}\",[]]Al[\"{CHl{{LFb{LH`BFGn}}}}\",[]]Aa[\"{BFFfAO@n}\",[]]Aa[\"{BHAhBADf}\",[]]Aa[\"{BFFhAO@n}\",[]]Aa[\"{BHAjBADf}\",[]]Af[\"{{}{{LEh{Ab}}}}\",[]]0000Aj[\"{{{AI@j{AILj}}}CMf}\",[]]Bo[\"{{{AI@j{CAnBEHl}}ABJj}{{AI@j{CAnBEHl}}}}\",[]]Bo[\"{{{AI@j{CAnBEHn}}ABJj}{{AI@j{CAnBEHn}}}}\",[]]Ao[\"{{{Nd{AILl}}}{{Nd{Mj}}}}\",[]]Am[\"{{{Nd{Mn}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{JEh}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{OH`}}}{{Nd{Mj}}}}\",[]]Ao[\"{{{Nd{AILn}}}{{Nd{Mj}}}}\",[]]Am[\"{{{Nd{N`}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{JEj}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{OHb}}}{{Nd{Mj}}}}\",[]]Aj[\"{{{AI@j{BFHb}}}CMf}\",[]]Bo[\"{{{AI@j{CAnBFNn}}ABJj}{{AI@j{CAnBFNn}}}}\",[]]Bo[\"{{{AI@j{CAnBFO`}}ABJj}{{AI@j{CAnBFO`}}}}\",[]]Bc[\"{{{AI@j{AILd}}}{{LEh{LDl}}}}\",[]]Bc[\"{{{AI@j{AILh}}}{{LEh{LDl}}}}\",[]]Bc[\"{{{AI@j{ABKf}}}{{LEh{LDl}}}}\",[]]Bc[\"{{{AI@j{ABKh}}}{{LEh{LDl}}}}\",[]]A`[\"{AHEnLEh}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Bc[\"{{{AI@j{AHEf}}}{{LEh{LAl}}}}\",[]]Am[\"{{{AI@j{AODd}}LAl}HIh}\",[]]Aj[\"{{{AI@j{AILh}}}OHf}\",[]]Bg[\"{{{AI@j{AJ@f}}}{{LFb{C@hB@Al}}}}\",[]]Bg[\"{{{AI@j{AJ@h}}}{{LFb{C@hB@Bd}}}}\",[]]A`[\"{BACnLEh}\",[]]A`[\"{BAD`LEh}\",[]]A`[\"{AHE`LEh}\",[]]A`[\"{AO@`LEh}\",[]]Ck[\"{{{AI@j{CAnBBAn}}{LEh{{JJb{Md}}}}}{{AI@j{CAnBBAn}}}}\",[]]Ck[\"{{{AI@j{CAnBBB`}}{LEh{{JJb{Md}}}}}{{AI@j{CAnBBB`}}}}\",[]]A`[\"{BEBnLEh}\",[]]A`[\"{BEC`LEh}\",[]]Ac[\"{AI@jc{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{LFb{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000000Al[\"{CHl{{LFb{LGnBFGl}}}}\",[]]Al[\"{LAl{{LFb{LGnBFGl}}}}\",[]]2Al[\"{HIf{{LFb{LGnBFGl}}}}\",[]]333333333333333Aj[\"{ABJj{{LFb{AOAn}}}}\",[]]444444Ai[\"{CHl{{LFb{AHF`}}}}\",[]]55555555555555555555555555Bb[\"{{{Ml{B@@l}}}{{LFb{BDKb}}}}\",[]]Aj[\"{B@@l{{LFb{BDKb}}}}\",[]]77Bb[\"{{{Ml{B@Fl}}}{{LFb{BDKf}}}}\",[]]Aj[\"{B@Fl{{LFb{BDKf}}}}\",[]]999999999999999999Al[\"{CHl{{LFb{LH`BFGn}}}}\",[]]Al[\"{HIf{{LFb{LH`BFGn}}}}\",[]]Al[\"{LAl{{LFb{LH`BFGn}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Bm[\"{B@@l{{LFb{BDKdc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Ml{B@@l}}}{{LFb{BDKdc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LFb{e}}}{}{}}\",[\"U\",\"T\"]]Ce[\"{{{Ml{B@Fl}}}{{LFb{BDKhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@Fl{{LFb{BDKhc}}}{}}\",[\"TryFrom::Error\"]]2222222222222222222222222222Aj[\"{{}{{LFb{c}}}{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@b{{AI@j{CAnc}}}}{AI@j{CAnOFb}}}CKl{}}\",[\"F\"]]000000000Cc[\"{{{AI@j{BBEj}}AJB`{AI@j{ABOb}}}{{LFd{HIh}}}}\",[]]Bk[\"{{{AI@j{BFMl}}B@Bn}{{LFb{HIhAIMj}}}}\",[]]Bk[\"{{{AI@j{BFMn}}B@C`}{{LFb{HIhAIMl}}}}\",[]]0Bk[\"{{{AI@j{BGMj}}B@C`}{{LFb{HIhAIMl}}}}\",[]]Ak[\"{{{AI@j{AILh}}}ABIf}\",[]]Bi[\"{{{AI@j{CAnB@H`}}}{{LFb{LGnJG`}}}}\",[]]Bi[\"{{{AI@j{CAnB@H`}}}{{LFb{CLbJG`}}}}\",[]]Bh[\"{{{AI@j{CAnB@H`}}}{{LFb{NjJG`}}}}\",[]]Bj[\"{{{AI@j{CAnB@H`}}}{{LFb{ABMlJG`}}}}\",[]]Bf[\"{{{AI@j{CAnB@H`}}}{{LFh{LH`}}}}\",[]]Bf[\"{{{AI@j{CAnB@H`}}}{{LFh{CLd}}}}\",[]]Be[\"{{{AI@j{CAnB@H`}}}{{LFh{Nl}}}}\",[]]Bg[\"{{{AI@j{CAnB@H`}}}{{LFh{ABMn}}}}\",[]]Aa[\"{AO@dBFGj}\",[]]Bc[\"{{{AI@j{AILl}}}{{Nd{AILl}}}}\",[]]Ba[\"{{{AI@j{Mn}}}{{Nd{AILl}}}}\",[]]Bb[\"{{{AI@j{JEh}}}{{Nd{AILl}}}}\",[]]Bb[\"{{{AI@j{OH`}}}{{Nd{AILl}}}}\",[]]Bc[\"{{{AI@j{AILn}}}{{Nd{AILn}}}}\",[]]Ba[\"{{{AI@j{N`}}}{{Nd{AILn}}}}\",[]]Bb[\"{{{AI@j{JEj}}}{{Nd{AILn}}}}\",[]]Bb[\"{{{AI@j{OHb}}}{{Nd{AILn}}}}\",[]]A`[\"{ABOdCHl}\",[]]An[\"{{{AI@j{CAnAILj}}}AHEf}\",[]]A`[\"{ABOfCHl}\",[]]C`[\"{{{AI@j{CAnB@Bn}}}{{AI@j{CAn{KOh{Md}}}}}}\",[]]C`[\"{{{AI@j{CAnB@C`}}}{{AI@j{CAn{KOh{Md}}}}}}\",[]]Ah[\"{Md{{LEh{B@Ch}}}}\",[]]Ah[\"{Md{{LEh{B@Cj}}}}\",[]]Ah[\"{Md{{LEh{B@Cl}}}}\",[]]A`[\"{ABMlCLb}\",[]]A`[\"{ABMnCLd}\",[]]Bi[\"{AILd{{LFb{{LAj{AILhBCCn}}AILd}}}}\",[]]o[\"{CLbCMf}\",[]]o[\"{CLdCMf}\",[]]Aj[\"{{{AI@j{AILj}}}CMf}\",[]]21Aj[\"{{{AI@j{ABKf}}}CMf}\",[]]Aj[\"{{{AI@j{ABKh}}}CMf}\",[]]Bn[\"{{{AI@j{CAnB@G`}}CMf}{{AI@j{CAnB@G`}}}}\",[]]Bn[\"{{{AI@j{CAnB@Gd}}CMf}{{AI@j{CAnB@Gd}}}}\",[]]Bl[\"{{{AI@j{OIh}}OGd}{{A@b{{Nd{AIKb}}}}}}\",[]]Bm[\"{{{AI@j{B@Gn}}OGd}{{A@b{{Nd{AIKb}}}}}}\",[]]Bm[\"{{{AI@j{AOCh}}OGd}{{A@b{{Nd{AIKb}}}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Ak[\"{{{AI@j{AILh}}}ABLj}\",[]]Bc[\"{{{AI@j{AILh}}AJ@lAIO`}ABLl}\",[]]Ce[\"{{{AI@j{CAnAILj}}AHEnAIObOGd}{{LFb{LEnAHGj}}}}\",[]]43Bm[\"{{{AI@j{CAnAILj}}LEn}{{LFb{HIhAIL`}}}}\",[]]E`[\"{{{A@b{{AI@j{CAnBDGh}}}}{AI@j{CAnOFb}}}{{CKl{{LEh{c}}}}}{}}\",[\"Stream::Item\"]]Ef[\"{{{A@b{{AI@j{CAnAIOj}}}}{AI@j{CAnOFb}}{AI@j{CAn{KOh{Md}}}}}{{CKl{{LFd{LAl}}}}}}\",[]]E`[\"{{{A@b{{AI@j{CAnAIOj}}}}{AI@j{CAnOFb}}{AI@j{CAnOIb}}}{{CKl{{LFd{HIh}}}}}}\",[]]Ec[\"{{{AI@j{CAnAIOj}}{AI@j{CAnOFb}}{AI@j{CAn{KOh{Md}}}}}{{CKl{{LFb{LAlAHHh}}}}}}\",[]]Fg[\"{{{AI@j{CAnBB@f}}{AI@j{CAnOFb}}{AI@j{CAn{KOh{AIMn}}}}{AI@j{CAn{KOh{ABMh}}}}}{{CKl{{LFd{LAl}}}}}}\",[]]Dn[\"{{{A@b{{AI@j{CAnAHHn}}}}{AI@j{ABOb}}{AI@j{CAnOFb}}}{{CKl{{LFd{HIh}}}}}}\",[]];:Cd[\"{{{AI@j{CAnBBAn}}{Ml{OFn}}}{{AI@j{CAnBBAn}}}}\",[]]Cd[\"{{{AI@j{CAnBBB`}}{Ml{OG`}}}{{AI@j{CAnBBB`}}}}\",[]]Cj[\"{{{AI@j{B@Gb}}CHjB@Bn{AI@j{{KOh{Md}}}}}{{JJb{Md}}}}\",[]]Cj[\"{{{AI@j{BDKf}}CHjB@Bn{AI@j{{KOh{Md}}}}}{{JJb{Md}}}}\",[]]Cj[\"{{{AI@j{B@Gf}}CHjB@C`{AI@j{{KOh{Md}}}}}{{JJb{Md}}}}\",[]]Cj[\"{{{AI@j{BDKh}}CHjB@C`{AI@j{{KOh{Md}}}}}{{JJb{Md}}}}\",[]]Cj[\"{{{AI@j{BGHf}}CHjB@Bl{AI@j{{KOh{Md}}}}}{{JJb{Md}}}}\",[]]Bb[\"{LAl{{LAj{LAl{LEh{LAl}}}}}}\",[]]00000Aj[\"{{{AI@j{AILh}}}LAl}\",[]]A`[\"{AI@jLGj}\",[]]00000000000000000000000000000000000000000000000000000000000Ce[\"{{{AI@j{CAnB@G`}}{Ml{BECb}}}{{AI@j{CAnB@G`}}}}\",[]]Ce[\"{{{AI@j{CAnB@Gd}}{Ml{BECd}}}{{AI@j{CAnB@Gd}}}}\",[]]A`[\"{BGBjJHf}\",[]]o[\"{B@G`Ml}\",[]]o[\"{B@GdMl}\",[]]Aj[\"{{{AI@j{ABJn}}}HIh}\",[]]Ck[\"{{{AI@j{CAnAJ@f}}{AI@j{{KOh{Md}}}}}{{LFb{HIhAJBb}}}}\",[]]n[\"{{}LAl}\",[]]Ak[\"{{{AI@j{AILh}}}AHCj}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CMf}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CMf}{{AI@j{CAnBCCf}}}}\",[]]Bi[\"{{{AI@j{CAn}}}{{AI@j{CAnc}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AI@j{AI@j{CAnc}}}HIh{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AI@j{CAnAILj}}OGdLEnLH`}{{LFb{HIhBB@l}}}}\",[]]A`[\"{AHE`CMf}\",[]]B`[\"{{AI@j{AI@j{c}}}CMf{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BC@dBEKb}\",[]]A`[\"{BDGlLGn}\",[]]Aa[\"{BC@hBEKd}\",[]]A`[\"{BDH`LH`}\",[]]o[\"{LClLH`}\",[]]o[\"{OInLH`}\",[]]Aa[\"{BC@dAIN`}\",[]]Aa[\"{BC@hAINb}\",[]]o[\"{LGnCHl}\",[]]o[\"{LH`CHl}\",[]]Aj[\"{{{AI@j{AILj}}}CMf}\",[]]Aj[\"{{{AI@j{BACh}}}CMf}\",[]]Bd[\"{{{AI@j{BB@f}}}{{LFd{AJ@l}}}}\",[]]Bd[\"{{{AI@j{ABJn}}}{{LFd{AJ@l}}}}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]Cb[\"{{{AI@j{BCAl}}{AI@j{AJ@d}}CHl}{{Ml{AODd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CMf}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CMf}{{AI@j{CAnBCCf}}}}\",[]]Bg[\"{{{AI@j{AILh}}LEl}{{LEh{AHHb}}}}\",[]]Bj[\"{{{AI@j{CAnAILj}}LEn}{{LEh{AHHd}}}}\",[]]Aa[\"{AHCfAHHd}\",[]]Db[\"{{{A@b{{AI@j{CAnAJ@f}}}}{AI@j{CAnOFb}}}{{CKl{{LFd{HIh}}}}}}\",[]]00Eg[\"{{{A@b{{AI@j{CAnAJ@f}}}}{AI@j{CAnOFb}}{AI@j{{KOh{Md}}}}}{{CKl{{LFb{LAlAJBb}}}}}}\",[]]Ec[\"{{{A@b{{AI@j{CAnAJ@f}}}}{AI@j{CAnOFb}}{AI@j{{KOh{Md}}}}}{{CKl{{LFd{LAl}}}}}}\",[]]0Bm[\"{{{AI@j{JIl}}{AI@j{CKb}}}{{LFd{HIh}}}}\",[]]Cd[\"{{{AI@j{CAnAIOj}}LAl}{{LFb{{LEh{JE`}}AHHh}}}}\",[]]Cd[\"{{{AI@j{CAnBF@h}}LAl}{{LFb{{LEh{JE`}}AHHh}}}}\",[]]Cn[\"{{{AI@j{CAnAIOj}}{AI@j{CAn{KOh{Md}}}}}{{LFb{HIhBBDl}}}}\",[]]Bj[\"{{{AI@j{CKd}}AIO`}{{LFb{HIhBEJj}}}}\",[]]Bn[\"{{{AI@j{CAnAIOd}}OGd}{{AI@j{CAnAIOd}}}}\",[]]Bn[\"{{{AI@j{CAnAIOf}}OGd}{{AI@j{CAnAIOf}}}}\",[]]A`[\"{BBEbADf}\",[]]o[\"{JIjCHl}\",[]]o[\"{{}AJ@l}\",[]]0o[\"{{}AI@j}\",[]]Bj[\"{{ABKf{Ml{B@G`}}}{{LFb{AILdBC@j}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Bh[\"{{{AI@j{AHE`}}AJ@l}{{LFf{AHI`}}}}\",[]]A`[\"{AHHbCHh}\",[]]Aj[\"{{{AI@j{AILj}}}CHh}\",[]]A`[\"{AHHdCHh}\",[]]Ce[\"{{{AI@j{CAnAIOd}}{LEh{LGj}}}{{AI@j{CAnAIOd}}}}\",[]]Ce[\"{{{AI@j{CAnAIOf}}{LEh{LGj}}}{{AI@j{CAnAIOf}}}}\",[]]Aa[\"{ABNnAJ@l}\",[]]Aa[\"{ABO`AJ@l}\",[]]Aa[\"{ABObAJ@l}\",[]]n[\"{OjCHh}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CHh}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CHh}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHE`CHh}\",[]]Bo[\"{{{AI@j{CAnBCCd}}ABJj}{{AI@j{CAnBCCd}}}}\",[]]Bo[\"{{{AI@j{CAnBCCf}}ABJj}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHE`LEh}\",[]]k[\"{{}}\",[]]00000000000Bh[\"{ABKfc{}}\",[\"IntoFuture::IntoFuture\"]]1A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]n[\"{OjLAl}\",[]]Ao[\"{{AI@jc}{{OGj{c}}}{}}\",[\"S\"]]0C`[\"{{{AI@j{CAnAILl}}OGdCHlCMf{LEh{CHl}}}HIh}\",[]]Bn[\"{{{AI@j{CAnMn}}OGdCHlCMf{LEh{CHl}}}HIh}\",[]]C`[\"{{{AI@j{CAnAILn}}OGdCHlCMf{LEh{CHl}}}HIh}\",[]]Bn[\"{{{AI@j{CAnN`}}OGdCHlCMf{LEh{CHl}}}HIh}\",[]]Bk[\"{{{AI@j{CAnJIl}}OGd{AI@j{BCAl}}}HIh}\",[]]A`[\"{BEBnLEh}\",[]]A`[\"{BEC`LEh}\",[]]Bn[\"{{{AI@j{LGn}}{AI@j{LGn}}}{{LEh{ABLn}}}}\",[]]Bn[\"{{{AI@j{LEl}}{AI@j{LEl}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{AOAn}}{AI@j{AOAn}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{B@Bn}}{AI@j{B@Bn}}}{{LEh{ABLn}}}}\",[]]Bn[\"{{{AI@j{CLb}}{AI@j{CLb}}}{{LEh{ABLn}}}}\",[]]Bl[\"{{{AI@j{Nj}}{AI@j{Nj}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{ABMl}}{AI@j{ABMl}}}{{LEh{ABLn}}}}\",[]]Bn[\"{{{AI@j{LH`}}{AI@j{LH`}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{BED`}}{AI@j{BED`}}}{{LEh{ABLn}}}}\",[]]Bn[\"{{{AI@j{LEn}}{AI@j{LEn}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{AHHj}}{AI@j{AHHj}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{BAG`}}{AI@j{BAG`}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{AJBd}}{AI@j{AJBd}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{AOB`}}{AI@j{AOB`}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{BDHh}}{AI@j{BDHh}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{BECl}}{AI@j{BECl}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{B@C`}}{AI@j{B@C`}}}{{LEh{ABLn}}}}\",[]]Bn[\"{{{AI@j{CLd}}{AI@j{CLd}}}{{LEh{ABLn}}}}\",[]]Bl[\"{{{AI@j{Nl}}{AI@j{Nl}}}{{LEh{ABLn}}}}\",[]]C`[\"{{{AI@j{ABMn}}{AI@j{ABMn}}}{{LEh{ABLn}}}}\",[]]Be[\"{{{AI@j{AILh}}}{{ABMd{AHGn}}}}\",[]]Bk[\"{{{AI@j{AILj}}LEn}{{LFb{AIObAIL`}}}}\",[]]Df[\"{{{AI@j{CAnAIOj}}{AI@j{CAn{KOh{JDn}}}}}{{LFb{{LEh{LAl}}AHHh}}}}\",[]]Cc[\"{{{AI@j{CAnAIOj}}LAl}{{LFb{{A@h{Md}}BBDn}}}}\",[]]Bb[\"{{{AI@j{CAnAILj}}ABMn}AIOl}\",[]]Aj[\"{{{AI@j{AHFf}}}LAl}\",[]]Aj[\"{{{AI@j{AHFh}}}LAl}\",[]]Bb[\"{{{AI@j{CAnAILj}}ABMn}AJ@h}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CHl}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CHl}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHE`LEh}\",[]]A`[\"{BACnLEh}\",[]]A`[\"{BAD`LEh}\",[]]Ce[\"{{{AI@j{CAnB@G`}}{Ml{AJAd}}}{{AI@j{CAnB@G`}}}}\",[]]Ce[\"{{{AI@j{CAnB@Gd}}{Ml{AJAf}}}{{AI@j{CAnB@Gd}}}}\",[]]Ce[\"{{{AI@j{CAnB@A`}}{Ml{AJAl}}}{{AI@j{CAnB@A`}}}}\",[]]Ce[\"{{{AI@j{CAnB@Ad}}{Ml{AJAn}}}{{AI@j{CAnB@Ad}}}}\",[]]n[\"{OjCHl}\",[]]Bn[\"{{{AI@j{CAnBEHl}}CHh}{{AI@j{CAnBEHl}}}}\",[]]Bn[\"{{{AI@j{CAnBEHn}}CHh}{{AI@j{CAnBEHn}}}}\",[]]Ak[\"{{{AI@j{AILh}}}BFHb}\",[]]Ai[\"{{{Ml{B@Gb}}}B@G`}\",[]]Ai[\"{{{Ml{B@Gf}}}B@Gd}\",[]]Aj[\"{{BCBfc}BCBfABNd}\",[\"\"]]Aj[\"{{BCBhc}BCBhABNd}\",[\"\"]]Bm[\"{{{AI@j{CAnAJ@f}}JDn}{{LFb{HIhAJBb}}}}\",[]]n[\"{{}LDl}\",[]]o[\"{OF`OF`}\",[]]0000Bd[\"{{{AI@j{BFMl}}}{{LEh{ABJj}}}}\",[]]Bd[\"{{{AI@j{BFMn}}}{{LEh{ABJj}}}}\",[]]Bd[\"{{{AI@j{BGMj}}}{{LEh{ABJj}}}}\",[]]Bd[\"{{{AI@j{BGNd}}}{{LEh{ABJj}}}}\",[]]Bd[\"{{{AI@j{AILh}}}{{LEh{BC@j}}}}\",[]]Ck[\"{{{AI@j{ABJn}}B@A`AJ@l{AI@j{CHf}}}{{LFb{AILdB@Bh}}}}\",[]]Ak[\"{{{AI@j{B@Fh}}}ABJj}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]An[\"{{AIOhCMf}{{LFf{LAl}}}}\",[]]Cj[\"{{{AI@j{OIj}}}{{LEh{{LAj{{Nd{AHFf}}{Nd{AHGd}}}}}}}}\",[]]Ck[\"{{{AI@j{AJAh}}}{{LEh{{LAj{{Nd{AHFf}}{Nd{AHGd}}}}}}}}\",[]]Cj[\"{{{AI@j{OIl}}}{{LEh{{LAj{{Nd{AHFh}}{Nd{AHGf}}}}}}}}\",[]]Ck[\"{{{AI@j{AJAj}}}{{LEh{{LAj{{Nd{AHFh}}{Nd{AHGf}}}}}}}}\",[]]A`[\"{BFFfA@h}\",[]]A`[\"{BHAhA@h}\",[]]A`[\"{BFFhA@h}\",[]]A`[\"{BHAjA@h}\",[]]An[\"{{{AI@j{CAnBFMl}}}B@Bn}\",[]]An[\"{{{AI@j{CAnBFMn}}}B@C`}\",[]]An[\"{{{AI@j{CAnBGMj}}}B@C`}\",[]]An[\"{{{AI@j{CAnBGNd}}}B@C`}\",[]]Ak[\"{{{AI@j{BBEj}}}B@Dn}\",[]]Ba[\"{{{AI@j{CAnAILj}}BCA`}HIh}\",[]]Bo[\"{{{AI@j{CAnABK`}}BDHhBACh}{{LEh{BCA`}}}}\",[]]B`[\"{{{AI@j{OIj}}B@BnCLb}CJh}\",[]]Bn[\"{{{AI@j{B@Gb}}CHjB@Bn}{{LFb{CJhBEKj}}}}\",[]]Ba[\"{{{AI@j{AJAh}}B@BnCLb}CJh}\",[]]Bn[\"{{{AI@j{BDKf}}CHjB@Bn}{{LFb{CJhBEKj}}}}\",[]]B`[\"{{{AI@j{OIl}}B@C`CLd}CJj}\",[]]Bn[\"{{{AI@j{B@Gf}}CHjB@C`}{{LFb{CJjBEL`}}}}\",[]]Ba[\"{{{AI@j{AJAj}}B@C`CLd}CJj}\",[]]Bn[\"{{{AI@j{BDKh}}CHjB@C`}{{LFb{CJjBEL`}}}}\",[]]Bn[\"{{{AI@j{BGHf}}CHjB@Bl}{{LFb{CJjBEL`}}}}\",[]]Aj[\"{{}{{OId{c}}}{}}\",[\"T\"]]0A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]Bn[\"{{{AI@j{CAnB@G`}}LAl}{{AI@j{CAnB@G`}}}}\",[]]Bn[\"{{{AI@j{CAnB@Gd}}LAl}{{AI@j{CAnB@Gd}}}}\",[]]Aj[\"{{{AI@j{BB@f}}}CMf}\",[]]0Aj[\"{{{AI@j{BBEj}}}CMf}\",[]]Bk[\"{{{AI@j{AILj}}LEn}{{LFb{AHEnAIL`}}}}\",[]]Cb[\"{{{AI@j{BCAl}}{AI@j{AIOh}}CHl}{{Ml{AODd}}}}\",[]]Ak[\"{{{AI@j{ABKf}}}B@Bn}\",[]]Ak[\"{{{AI@j{ABKh}}}B@C`}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Bf[\"{{{AI@j{CAnAILj}}}{{LEh{OGd}}}}\",[]]Ce[\"{{{AI@j{CAnBCCd}}{Ml{AOBl}}}{{AI@j{CAnBCCd}}}}\",[]]Ce[\"{{{AI@j{CAnBCCf}}{Ml{AOBn}}}{{AI@j{CAnBCCf}}}}\",[]]43A`[\"{ABNnLEh}\",[]]A`[\"{ABO`LEh}\",[]]A`[\"{ABObLEh}\",[]]Ai[\"{{{AI@j{OJh}}}LAl}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]Bo[\"{{{AI@j{CAnBGMj}}ABJj}{{AI@j{CAnBGMj}}}}\",[]]Bo[\"{{{AI@j{CAnBGNd}}ABJj}{{AI@j{CAnBGNd}}}}\",[]]Bj[\"{{{AI@j{AJ@f}}C@h}{{LFb{HIhB@Al}}}}\",[]]Bm[\"{{{AI@j{CAnAJ@h}}C@h}{{LFb{HIhB@Bd}}}}\",[]]>=A`[\"{JIjAODb}\",[]]A`[\"{AO@fJIj}\",[]]Bi[\"{{{Ml{B@@l}}JIn}{{LFb{BDKbBFGd}}}}\",[]]Bi[\"{{{Ml{B@Fl}}JIn}{{LFb{BDKfBFGd}}}}\",[]]Bi[\"{{{Ml{B@@l}}JIn}{{LFb{BDKdBFGf}}}}\",[]]Bi[\"{{{Ml{B@Fl}}JIn}{{LFb{BDKhBFGf}}}}\",[]]Co[\"{{{AI@j{CAnAJ@f}}{AI@j{CAn{KOh{JDn}}}}}{{LFb{OKdAJBb}}}}\",[]]Co[\"{{{AI@j{CAnAJ@h}}{AI@j{CAn{KOh{JDn}}}}}{{LFb{OKfAJBd}}}}\",[]]Aj[\"{{{AI@j{AILj}}}CMf}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]A`[\"{AHE`CMf}\",[]]E`[\"{{{AI@j{CAnBBAn}}c}{{AI@j{CAnBBAn}}}{{A`{}{{LEj{{Nd{BFMl}}}}}}CL`CLf}}\",[\"F\"]]E`[\"{{{AI@j{CAnBBB`}}c}{{AI@j{CAnBBB`}}}{{A`{}{{LEj{{Nd{BFMn}}}}}}CL`CLf}}\",[\"F\"]]Bj[\"{{{AI@j{BB@f}}}{{A@b{{Nd{AHHn}}}}}}\",[]]n[\"{OjCHl}\",[]]Aa[\"{AODbAHFj}\",[]]76Aa[\"{AJ@`ABKf}\",[]]Aa[\"{AJ@bABKh}\",[]]Cf[\"{{{AI@j{CAnBEFn}}{LEh{ABJj}}}{{AI@j{CAnBEFn}}}}\",[]]Cf[\"{{{AI@j{CAnBEG`}}{LEh{ABJj}}}{{AI@j{CAnBEG`}}}}\",[]]9;:An[\"{{{AI@j{AODd}}ABJj}HIh}\",[]]B`[\"{{{AI@j{CAnAILl}}CHh}HIh}\",[]]An[\"{{{AI@j{CAnMn}}CHh}HIh}\",[]]Ao[\"{{{AI@j{CAnJEh}}CHh}HIh}\",[]]Ao[\"{{{AI@j{CAnOH`}}CHh}HIh}\",[]]B`[\"{{{AI@j{CAnAILn}}CHh}HIh}\",[]]An[\"{{{AI@j{CAnN`}}CHh}HIh}\",[]]Ao[\"{{{AI@j{CAnJEj}}CHh}HIh}\",[]]Ao[\"{{{AI@j{CAnOHb}}CHh}HIh}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]10Bh[\"{{{AI@j{AILh}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@j{OIj}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@j{AJAh}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@j{OIl}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@j{AJAj}}}{{LEh{{Nd{Mj}}}}}}\",[]]Dm[\"{{{AI@j{CAnAIOj}}{AI@j{CAnOFb}}{AI@j{CAnOIb}}}{{CKl{{LFb{HIhAHHh}}}}}}\",[]]Db[\"{{{A@b{{AI@j{CAnAJ@f}}}}{AI@j{CAnOFb}}}{{CKl{{LFd{HIh}}}}}}\",[]]Db[\"{{{AI@j{CAnAILj}}OGdB@Dn{AI@j{CAn{A@h{Md}}}}}{{LEh{ABO`}}}}\",[]]Cf[\"{{{AI@j{CAnBCCd}}{AI@j{CHf}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{AI@j{CHf}}}{{AI@j{CAnBCCf}}}}\",[]]Ak[\"{{{AI@j{AILh}}}B@F`}\",[]]Bj[\"{{{AI@j{AILh}}JDn}{{LFb{HIhBECn}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CMf}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CMf}{{AI@j{CAnBCCf}}}}\",[]]Ai[\"{{{AI@j{OFn}}}LAl}\",[]]Ai[\"{{{AI@j{OG`}}}LAl}\",[]]De[\"{{{Ml{B@Ab}}CHj{AI@j{CHf}}{AI@j{BF@f}}}{{LFb{{Nd{OIj}}B@Bh}}}}\",[]]Bo[\"{{{Ml{B@Gb}}CHj{AI@j{BF@f}}}{{Nd{OIj}}}}\",[]]De[\"{{{Ml{BDKb}}CHj{AI@j{CHf}}{AI@j{BF@f}}}{{LFb{{Nd{OIj}}B@Bh}}}}\",[]]Bo[\"{{{Ml{BDKf}}CHj{AI@j{BF@f}}}{{Nd{OIj}}}}\",[]]De[\"{{{Ml{B@Af}}CHj{AI@j{CHf}}{AI@j{BF@f}}}{{LFb{{Nd{OIl}}B@Bj}}}}\",[]]Bo[\"{{{Ml{B@Gf}}CHj{AI@j{BF@f}}}{{Nd{OIl}}}}\",[]]De[\"{{{Ml{BDKd}}CHj{AI@j{CHf}}{AI@j{BF@f}}}{{LFb{{Nd{OIl}}B@Bj}}}}\",[]]Bo[\"{{{Ml{BDKh}}CHj{AI@j{BF@f}}}{{Nd{OIl}}}}\",[]]De[\"{{{Ml{BGHd}}CHj{AI@j{CHf}}{AI@j{BF@f}}}{{LFb{{Nd{OIl}}B@Bf}}}}\",[]]Bo[\"{{{Ml{BGHf}}CHj{AI@j{BF@f}}}{{Nd{OIl}}}}\",[]]Bm[\"{LAl{{LFb{{LAj{LAl{LEh{LAl}}}}BEOj}}}}\",[]]0Cc[\"{{{AI@j{BECb}}{AI@j{{KOh{Md}}}}}{{Nd{OEf}}}}\",[]]Cc[\"{{{AI@j{BECd}}{AI@j{{KOh{Md}}}}}{{Nd{OEh}}}}\",[]]Bm[\"{{AJ@lBB@hOj}{{LFf{{LAj{ABJlAILf}}}}}}\",[]]Bd[\"{{{AI@j{AILj}}}{{AI@j{OIl}}}}\",[]]A`[\"{AO@fJIj}\",[]]Cb[\"{{{AI@j{AOBl}}CLbAJ@lB@BnOGd}{{LEh{AIOd}}}}\",[]]Cb[\"{{{AI@j{BCBf}}CLbAJ@lB@BnOGd}{{LEh{AIOd}}}}\",[]]Cb[\"{{{AI@j{AOBn}}CLdAJ@lB@C`OGd}{{LEh{AIOf}}}}\",[]]Cb[\"{{{AI@j{BCBh}}CLdAJ@lB@C`OGd}{{LEh{AIOf}}}}\",[]]B`[\"{{{AI@j{CAnAILj}}OGd}HIh}\",[]]Bo[\"{{{AI@j{CAnAILd}}}{{LFb{{Nd{Mj}}BC@j}}}}\",[]]Bh[\"{{{AI@j{AILh}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@j{OIj}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@j{AJAh}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@j{OIl}}}{{LEh{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@j{AJAj}}}{{LEh{{Nd{Mj}}}}}}\",[]]o[\"{AIMdMd}\",[]]o[\"{AIMfMd}\",[]]Ai[\"{{{AI@j{CHf}}}CMf}\",[]]Aj[\"{{{AI@j{AILl}}}CHl}\",[]]Ah[\"{{{AI@j{Mn}}}CHl}\",[]]Bn[\"{{{AI@j{CAnAHDh}}CHl}{{AI@j{CAnAHDh}}}}\",[]]Ai[\"{{{AI@j{JEh}}}CHl}\",[]]Bn[\"{{{AI@j{CAnAOAd}}CHl}{{AI@j{CAnAOAd}}}}\",[]]Ai[\"{{{AI@j{OH`}}}CHl}\",[]]Bn[\"{{{AI@j{CAnBAF`}}CHl}{{AI@j{CAnBAF`}}}}\",[]]Aj[\"{{{AI@j{AILn}}}CHl}\",[]]Ah[\"{{{AI@j{N`}}}CHl}\",[]]Bn[\"{{{AI@j{CAnAHDj}}CHl}{{AI@j{CAnAHDj}}}}\",[]]Ai[\"{{{AI@j{JEj}}}CHl}\",[]]Bn[\"{{{AI@j{CAnAOAf}}CHl}{{AI@j{CAnAOAf}}}}\",[]]Ai[\"{{{AI@j{OHb}}}CHl}\",[]]Bn[\"{{{AI@j{CAnBAFb}}CHl}{{AI@j{CAnBAFb}}}}\",[]]Aa[\"{AIOjBF@h}\",[]]Ai[\"{{{AI@j{OIj}}}CMf}\",[]]Aj[\"{{{AI@j{AJAh}}}CMf}\",[]]Aj[\"{{{AI@j{AILj}}}CMf}\",[]]Ai[\"{{{AI@j{OIl}}}CMf}\",[]]Aj[\"{{{AI@j{AJAj}}}CMf}\",[]]Co[\"{{{AI@j{OIj}}B@Bn{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}CMf}\",[]]D`[\"{{{AI@j{AJAh}}B@Bn{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}CMf}\",[]]Co[\"{{{AI@j{OIl}}B@C`{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}CMf}\",[]]D`[\"{{{AI@j{AJAj}}B@C`{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}CMf}\",[]]Bn[\"{{{AI@j{CAnBEHl}}CHh}{{AI@j{CAnBEHl}}}}\",[]]Bn[\"{{{AI@j{CAnBEHn}}CHh}{{AI@j{CAnBEHn}}}}\",[]]Cc[\"{{{AI@j{CAnOIj}}}{{LEh{{OGf{{Nd{AHGd}}}}}}}}\",[]]Cd[\"{{{AI@j{CAnAJAh}}}{{LEh{{OGf{{Nd{AHGd}}}}}}}}\",[]]Cc[\"{{{AI@j{CAnOIl}}}{{LEh{{OGh{{Nd{AHGf}}}}}}}}\",[]]Cd[\"{{{AI@j{CAnAJAj}}}{{LEh{{OGh{{Nd{AHGf}}}}}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]De[\"{{{AI@j{CAnBCCd}}c{AI@j{CHf}}}{{AI@j{CAnBCCd}}}{{JDl{CKb}}}}\",[\"\"]]De[\"{{{AI@j{CAnBCCf}}c{AI@j{CHf}}}{{AI@j{CAnBCCf}}}{{JDl{CKb}}}}\",[\"\"]]Ci[\"{{{AI@j{CAnOIj}}{AI@j{{KOh{Md}}}}}{{LFb{CMfJFn}}}}\",[]]Cj[\"{{{AI@j{CAnAJAh}}{AI@j{{KOh{Md}}}}}{{LFb{CMfJFn}}}}\",[]]Cj[\"{{{AI@j{CAnOIl}}{AI@j{{KOh{Md}}}}}{{LFb{CMfBBEh}}}}\",[]]Ck[\"{{{AI@j{CAnAJAj}}{AI@j{{KOh{Md}}}}}{{LFb{CMfBBEh}}}}\",[]]n[\"{OjCMf}\",[]]Bn[\"{{{AI@j{CAnBCCd}}LGn}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}LH`}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHE`LEh}\",[]]Ca[\"{{{AI@j{CAnAIOj}}}{{LFb{{LEh{LGn}}AIOn}}}}\",[]]Ca[\"{{{AI@j{CAnBF@h}}}{{LFb{{LEh{LGn}}AIOn}}}}\",[]]Ca[\"{{{AI@j{CAnAIOl}}}{{LFb{{LEh{LH`}}B@Bd}}}}\",[]]Ak[\"{{{AI@j{AILd}}}AJ@l}\",[]]Ak[\"{{{AI@j{AILh}}}AJ@l}\",[]]Ak[\"{{{AI@j{ABKf}}}AJ@l}\",[]]Bg[\"{{{AI@j{CKd}}}{{LFb{AJ@lAIKj}}}}\",[]]Ak[\"{{{AI@j{ABKh}}}AJ@l}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]B`[\"{{LGnc}LGn{{CJf{LGn}}}}\",[\"\"]]B`[\"{{LElc}LEl{{CJf{LEl}}}}\",[\"\"]]B`[\"{{LH`c}LH`{{CJf{LH`}}}}\",[\"\"]]B`[\"{{LEnc}LEn{{CJf{LEn}}}}\",[\"\"]]20Ae[\"{{ABJjCHl}ADf}\",[]]Bn[\"{{{AI@j{CAnBCCd}}ADd}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}ADd}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{JIjABJj}\",[]]B`[\"{{}{{AI@j{{KOh{BEOh}}}}}}\",[]]Ao[\"{{AI@j{A@Jn{CAnMd}}}HIh}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LEh{{Ml{OIh}}}}}}\",[]]Bn[\"{{{AI@j{CAnBBAn}}CMf}{{AI@j{CAnBBAn}}}}\",[]]Bn[\"{{{AI@j{CAnBBB`}}CMf}{{AI@j{CAnBBB`}}}}\",[]]Aj[\"{{{AI@j{AHGd}}}CHl}\",[]]Aj[\"{{{AI@j{AHGf}}}CHl}\",[]]A`[\"{ABMhLEh}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]10Bf[\"{{{AI@j{CHf}}}{{LFf{CHlBAFd}}}}\",[]]Bm[\"{{{AI@j{ABJn}}{Nd{BB@f}}}{{LFd{HIh}}}}\",[]]Ai[\"{AILf{{LFf{HIh}}}}\",[]]Ci[\"{{{AI@j{OIf}}BB@hBAFn{AI@j{Oj}}}{{LAj{AJ@lABJl}}}}\",[]]Cb[\"{{{AI@j{CAnAILj}}LEnAIOb}{{LFb{AIObBEJl}}}}\",[]]Am[\"{{{AI@j{AILh}}CHl}HIh}\",[]]B`[\"{{{AI@j{CAnAILj}}CHl}HIh}\",[]]A`[\"{BBE`CMf}\",[]]B`[\"{{{AI@j{CAnJIj}}BBEb}HIh}\",[]]Aa[\"{AODbAHFj}\",[]]Bm[\"{{{AI@j{OIh}}AHI`}{{LFd{{Nd{BB@f}}}}}}\",[]]Bn[\"{{{AI@j{B@Gn}}AHI`}{{LFd{{Nd{BB@f}}}}}}\",[]]Bn[\"{{{AI@j{AOCh}}AHI`}{{LFd{{Nd{BB@f}}}}}}\",[]]Ci[\"{{{AI@j{CAnOIj}}{AI@j{CAn{A@h{Md}}}}}{{LEh{CJh}}}}\",[]]Cj[\"{{{AI@j{CAnAJAh}}{AI@j{CAn{A@h{Md}}}}}{{LEh{CJh}}}}\",[]]Ci[\"{{{AI@j{CAnOIl}}{AI@j{CAn{A@h{Md}}}}}{{LEh{CJj}}}}\",[]]Cj[\"{{{AI@j{CAnAJAj}}{AI@j{CAn{A@h{Md}}}}}{{LEh{CJj}}}}\",[]]Cb[\"{{{AI@j{ABNf}}HIfAJAbABJj}{{LFb{HIhBCBn}}}}\",[]]Cb[\"{{{AI@j{BABh}}HIfAJAbABJj}{{LFb{HIhBCBn}}}}\",[]]Cb[\"{{{AI@j{B@E`}}HIfAJAbABJj}{{LFb{HIhBCBn}}}}\",[]]Cb[\"{{{AI@j{ABNh}}HIfAJAbABJj}{{LFb{HIhBCC`}}}}\",[]]Cb[\"{{{AI@j{BABj}}HIfAJAbABJj}{{LFb{HIhBCC`}}}}\",[]]Cb[\"{{{AI@j{B@Eb}}HIfAJAbABJj}{{LFb{HIhBCC`}}}}\",[]]Al[\"{{}{{LFb{B@@nBFGf}}}}\",[]]Bm[\"{{{AI@j{AILh}}LEl}{{LEh{{Nd{AILl}}}}}}\",[]]Bo[\"{{{AI@j{AILj}}LEn}{{LEh{{AI@j{AILn}}}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Aj[\"{{{AI@j{AILh}}}HIh}\",[]]Am[\"{{{AI@j{CAnAILj}}}HIh}\",[]]Be[\"{{{AI@j{AIK`}}}{{LFb{OjJFh}}}}\",[]]Bg[\"{{{AI@j{AIK`}}}{{LFb{AHE`JFh}}}}\",[]]Be[\"{{{AI@j{AIK`}}}{{LFb{OlJFh}}}}\",[]]Be[\"{{{AI@j{AIK`}}}{{LFb{OnJFh}}}}\",[]]Be[\"{{{AI@j{AIK`}}}{{LFb{NhJFh}}}}\",[]]Bg[\"{{{AI@j{AIK`}}}{{LFb{ABIlJFh}}}}\",[]]Ak[\"{{{AI@j{BBEj}}}B@Dn}\",[]]Cf[\"{{{AI@j{CAnBCCd}}{LEh{AOAn}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{LEh{AOB`}}}{{AI@j{CAnBCCf}}}}\",[]]<;Aj[\"{{{AI@j{ABJn}}}LAl}\",[]]Aj[\"{{{AI@j{ABK`}}}LAl}\",[]]Bc[\"{{{AI@j{AILh}}AJ@lAIO`}ABLl}\",[]]Co[\"{{{AI@j{CAnAILj}}AHEnAIObOGd}{{LFb{{LAj{LEnCMf}}AHGj}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CHj}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CHj}{{AI@j{CAnBCCf}}}}\",[]]Ai[\"{AILf{{LFf{HIh}}}}\",[]]Bg[\"{{{AI@j{BBEj}}AJB`}{{LFd{LAl}}}}\",[]]A`[\"{AHE`CHl}\",[]]10Ai[\"{{{AI@j{Oj}}}BCCb}\",[]]Ce[\"{{{AI@j{CAnB@G`}}{Ml{BCCd}}}{{AI@j{CAnB@G`}}}}\",[]]Ce[\"{{{AI@j{CAnB@A`}}{Ml{BCCd}}}{{AI@j{CAnB@A`}}}}\",[]]Ce[\"{{{AI@j{CAnB@Gd}}{Ml{BCCf}}}{{AI@j{CAnB@Gd}}}}\",[]]Ce[\"{{{AI@j{CAnB@Ad}}{Ml{BCCf}}}{{AI@j{CAnB@Ad}}}}\",[]]Aa[\"{B@G`BFNn}\",[]]Aa[\"{B@GdBFO`}\",[]]Bj[\"{{{A@h{BB@h}}BAFn}{{LFb{B@G`JGb}}}}\",[]]Bj[\"{{{A@h{BB@h}}BAFn}{{LFb{B@GdJGd}}}}\",[]]Co[\"{{{AI@j{CAnAJ@f}}{AI@j{CAn{KOh{JDn}}}}}{{LFb{HIhAJBb}}}}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]A`[\"{BEBnCHl}\",[]]A`[\"{BEC`CHl}\",[]]Bc[\"{{{AI@j{AILh}}}{{LEh{LAl}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]10Aj[\"{{{AI@j{AHEf}}}LAl}\",[]]Be[\"{{{AI@j{ABJn}}{LEh{B@G`}}}HIh}\",[]]Bn[\"{{{AI@j{CAnABK`}}{LEh{{Ml{B@Gd}}}}}HIh}\",[]]Bd[\"{{{AI@j{BEOh}}}{{LEh{BAFl}}}}\",[]]o[\"{{}AI@j}\",[]]n[\"{{}OF`}\",[]]0000Bn[\"{{{AI@j{CAnBCCd}}LAl}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}LAl}{{AI@j{CAnBCCf}}}}\",[]]o[\"{CHlLGn}\",[]]o[\"{CHlLH`}\",[]]A`[\"{BACjCHl}\",[]]Bo[\"{{{AI@j{CAnBBAn}}ABJj}{{AI@j{CAnBBAn}}}}\",[]]Bo[\"{{{AI@j{CAnBBB`}}ABJj}{{AI@j{CAnBBB`}}}}\",[]]Ae[\"{{LAlCHl}BABh}\",[]]Ae[\"{{LAlCHl}BABj}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]6Bg[\"{{{AI@j{AILj}}LEn}{{LEh{AIOb}}}}\",[]]An[\"{{{AI@j{AILh}}JDn}B@Fj}\",[]]Am[\"{{{AI@j{AILh}}LGn}HIh}\",[]]B`[\"{{{AI@j{CAnAILj}}LH`}HIh}\",[]]Ce[\"{{{AI@j{CAnBBAn}}{A@h{CHj}}}{{AI@j{CAnBBAn}}}}\",[]]Ce[\"{{{AI@j{CAnBBB`}}{A@h{CHj}}}{{AI@j{CAnBBB`}}}}\",[]]Fg[\"{{{AI@j{BGBl}}{AI@j{BB@h}}{AI@j{{KOh{BB@h}}}}{AI@j{AJ@j}}{AI@j{{KOh{Md}}}}ABOh}{{LFb{BEJfJFf}}}}\",[]]Af[\"{{BCBfOGd}BCBf}\",[]]Af[\"{{BCBhOGd}BCBh}\",[]]?Al[\"{B@H`{{LFb{CLbJG`}}}}\",[]]Ak[\"{B@H`{{LFb{NjJG`}}}}\",[]]Am[\"{B@H`{{LFb{ABMlJG`}}}}\",[]]Ai[\"{B@H`{{LFh{CLd}}}}\",[]]Ah[\"{B@H`{{LFh{Nl}}}}\",[]]Aj[\"{B@H`{{LFh{ABMn}}}}\",[]]o[\"{OF`OF`}\",[]]Bo[\"{{{AI@j{CAnBEHl}}ABJj}{{AI@j{CAnBEHl}}}}\",[]]Bo[\"{{{AI@j{CAnBEHn}}ABJj}{{AI@j{CAnBEHn}}}}\",[]]Bb[\"{{{AI@j{OIj}}}{{LEh{CMf}}}}\",[]]Bc[\"{{{AI@j{AJAh}}}{{LEh{CMf}}}}\",[]]Bb[\"{{{AI@j{OIl}}}{{LEh{CMf}}}}\",[]]Bc[\"{{{AI@j{AJAj}}}{{LEh{CMf}}}}\",[]]Bg[\"{{{AI@j{AILh}}}{{LFb{HIhBC@j}}}}\",[]]Cf[\"{{{AI@j{CAnBCCd}}{LEh{ABJj}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{LEh{ABJj}}}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]Bl[\"{{{AI@j{CAnAILl}}OGdOGdCMfCMfCHl}HIh}\",[]]Bj[\"{{{AI@j{CAnMn}}OGdOGdCMfCMfCHl}HIh}\",[]]Bk[\"{{{AI@j{CAnJEh}}OGdOGdCMfCMfCHl}HIh}\",[]]Bk[\"{{{AI@j{CAnOH`}}OGdOGdCMfCMfCHl}HIh}\",[]]Bl[\"{{{AI@j{CAnAILn}}OGdOGdCMfCMfCHl}HIh}\",[]]Bj[\"{{{AI@j{CAnN`}}OGdOGdCMfCMfCHl}HIh}\",[]]Bk[\"{{{AI@j{CAnJEj}}OGdOGdCMfCMfCHl}HIh}\",[]]Bk[\"{{{AI@j{CAnOHb}}OGdOGdCMfCMfCHl}HIh}\",[]]A`[\"{BACjCHl}\",[]]Ak[\"{{{AI@j{OJh}}Nl}CHl}\",[]]Bi[\"{{{AI@j{CAnAJ@d}}CHl}{{LFf{HIh}}}}\",[]]<;A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]10Cf[\"{{{AI@j{CAnBCCd}}{LEh{BEFn}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{LEh{BEG`}}}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHHbCHl}\",[]]A`[\"{AHHdCHl}\",[]]Bh[\"{{{AI@j{CAnAIK`}}}{{LFb{OjJFh}}}}\",[]]Bj[\"{{{AI@j{CAnAIK`}}}{{LFb{AHE`JFh}}}}\",[]]Bh[\"{{{AI@j{CAnAIK`}}}{{LFb{OlJFh}}}}\",[]]Bh[\"{{{AI@j{CAnAIK`}}}{{LFb{OnJFh}}}}\",[]]Bh[\"{{{AI@j{CAnAIK`}}}{{LFb{NhJFh}}}}\",[]]Bj[\"{{{AI@j{CAnAIK`}}}{{LFb{ABIlJFh}}}}\",[]]Cc[\"{{{Ml{AILf}}CHlCMf}{{LFf{{LAj{BBEbBBEb}}}}}}\",[]]Bn[\"{{{AI@j{CAnB@G`}}CHl}{{AI@j{CAnB@G`}}}}\",[]]Bn[\"{{{AI@j{CAnB@Gd}}CHl}{{AI@j{CAnB@Gd}}}}\",[]]Aj[\"{{{AI@j{AILh}}}CMf}\",[]]Ak[\"{{{AI@j{BB@f}}}B@Dn}\",[]]0Ci[\"{{{AI@j{CAnBBAn}}CHh}{{LFb{{AI@j{CAnBBAn}}AO@j}}}}\",[]]Ci[\"{{{AI@j{CAnBBB`}}CHh}{{LFb{{AI@j{CAnBBB`}}AO@l}}}}\",[]]A`[\"{AHE`CHh}\",[]]Cf[\"{{{AI@j{CAnBCCd}}{LEh{BEHl}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{LEh{BEHn}}}{{AI@j{CAnBCCf}}}}\",[]]Bg[\"{{{AI@j{CAnAILj}}}{{LEh{BACh}}}}\",[]]Cf[\"{{{AI@j{CAnB@G`}}{LEh{B@Gh}}}{{AI@j{CAnB@G`}}}}\",[]]Cf[\"{{{AI@j{CAnB@Gd}}{LEh{B@Gh}}}{{AI@j{CAnB@Gd}}}}\",[]]Cf[\"{{{AI@j{CAnB@G`}}{LEh{B@Gj}}}{{AI@j{CAnB@G`}}}}\",[]]Cf[\"{{{AI@j{CAnB@Gd}}{LEh{B@Gj}}}{{AI@j{CAnB@Gd}}}}\",[]]Cc[\"{{}{{LFb{{LAj{{A@h{BB@h}}BAFn}}{Nd{JFd}}}}}}\",[]]Bn[\"{{{AI@j{CAnBEFn}}LGn}{{AI@j{CAnBEFn}}}}\",[]]Bn[\"{{{AI@j{CAnBEG`}}LH`}{{AI@j{CAnBEG`}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Bo[\"{{{AI@j{CAnB@G`}}ABJj}{{AI@j{CAnB@G`}}}}\",[]]Bo[\"{{{AI@j{CAnB@Gd}}ABJj}{{AI@j{CAnB@Gd}}}}\",[]]Ci[\"{{{AI@j{CKd}}{LEh{ABJj}}}{{LFb{{LEh{ABJj}}AIKj}}}}\",[]]Bj[\"{{{AI@j{BBEj}}AJB`LAl}{{LFd{HIh}}}}\",[]]054Bm[\"{{{AI@j{OIj}}}{{LFb{{LEh{BF@f}}JFn}}}}\",[]]Bn[\"{{{AI@j{AJAh}}}{{LFb{{LEh{BF@f}}JFn}}}}\",[]]Bn[\"{{{AI@j{OIl}}}{{LFb{{LEh{BF@f}}BBEh}}}}\",[]]Bo[\"{{{AI@j{AJAj}}}{{LFb{{LEh{BF@f}}BBEh}}}}\",[]]Aj[\"{{{AI@j{AHGd}}}CHl}\",[]]Aj[\"{{{AI@j{AHGf}}}CHl}\",[]]Aj[\"{{{AI@j{ABK`}}}CHl}\",[]]Bf[\"{{{AI@j{AHEn}}{AI@j{AHEn}}}CMf}\",[]]Am[\"{{{AI@j{CAnAILj}}}HIh}\",[]]Bn[\"{{{AI@j{CAnBAF`}}ADd}{{AI@j{CAnBAF`}}}}\",[]]Bn[\"{{{AI@j{CAnBAFb}}ADd}{{AI@j{CAnBAFb}}}}\",[]]Ak[\"{{{AI@j{AHHn}}}B@Dn}\",[]]0Bd[\"{{{AI@j{AILh}}}{{ABMd{JGn}}}}\",[]]Cb[\"{{{AI@j{AILj}}LEn}{{LFb{{LEh{AJ@l}}AIL`}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]Bn[\"{{{AI@j{CAnBCCd}}LGn}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}LH`}{{AI@j{CAnBCCf}}}}\",[]]A`[\"{AHE`LEh}\",[]]Bo[\"{{{AI@j{AHE`}}{AI@j{CHf}}}{{LFd{BCCb}}}}\",[]]Em[\"{{{AI@j{AILh}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhBGJ`}}}}\",[]]El[\"{{{AI@j{OIj}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhBGJ`}}}}\",[]]Em[\"{{{AI@j{AJAh}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhBGJ`}}}}\",[]]El[\"{{{AI@j{OIl}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhBGJb}}}}\",[]]Em[\"{{{AI@j{AJAj}}{AI@j{CAn{KOh{Md}}}}{AI@j{{KOh{Md}}}}{AI@j{{KOh{Md}}}}}{{LFb{HIhBGJb}}}}\",[]]Am[\"{{{AI@j{CAnAHEd}}}HIh}\",[]]0Al[\"{{{AI@j{AILj}}Nl}CHl}\",[]]Bl[\"{{{AI@j{AILh}}}{{ABMf{{LEh{AJ@l}}}}}}\",[]]Bg[\"{{{AI@j{CKd}}}{{LFb{BDGhAIKj}}}}\",[]]>=E`[\"{{{AI@j{BGBl}}{AI@j{{KOh{Md}}}}{AI@j{BB@h}}{AI@j{BFN`}}}{{LFb{BGEbJFf}}}}\",[]]0Bf[\"{{{Ml{BAGf}}}{{LFb{B@A`BFH`}}}}\",[]]Bf[\"{{{Ml{BAGf}}}{{LFb{B@AdBFH`}}}}\",[]]Bn[\"{{{AI@j{CAnBEFn}}LGn}{{AI@j{CAnBEFn}}}}\",[]]Bn[\"{{{AI@j{CAnBEG`}}LH`}{{AI@j{CAnBEG`}}}}\",[]]o[\"{OF`OF`}\",[]]0000Aj[\"{{{AI@j{AILj}}}CMf}\",[]]0Ci[\"{{{AI@j{CKd}}{LEh{ABJj}}}{{LFb{{LEh{ABJj}}AIKj}}}}\",[]]Cc[\"{{{AI@j{CAnOj}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Ce[\"{{{AI@j{CAnAHE`}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Cc[\"{{{AI@j{CAnOl}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Cc[\"{{{AI@j{CAnOn}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Cc[\"{{{AI@j{CAnNh}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Ce[\"{{{AI@j{CAnABIl}}{AI@j{AIK`}}}{{LFb{HIhJFh}}}}\",[]]Bo[\"{{{AI@j{CAnB@G`}}BFNn}{{AI@j{CAnB@G`}}}}\",[]]Bo[\"{{{AI@j{CAnB@Gd}}BFO`}{{AI@j{CAnB@Gd}}}}\",[]]Aj[\"{{{AI@j{BBAn}}}CHl}\",[]]Aj[\"{{{AI@j{BBB`}}}CHl}\",[]]Cc[\"{{{AI@j{CAnB@A`}}{Ml{A`}}}{{AI@j{CAnB@A`}}}}\",[]]Cc[\"{{{AI@j{CAnB@Ad}}{Ml{A`}}}{{AI@j{CAnB@Ad}}}}\",[]]Cj[\"{{BBAn{LEh{B@G`}}{Nd{BB@f}}{Ml{OIh}}}{{LFd{ABJn}}}}\",[]]Aj[\"{{{AI@j{ABKf}}}CMf}\",[]]Aj[\"{{{AI@j{ABKh}}}CMf}\",[]]Ca[\"{{{AI@j{CAnAILj}}OGdAINf}{{LFb{HIhBGBd}}}}\",[]]Bd[\"{{{AI@j{BGBl}}}{{A@h{BCBj}}}}\",[]]Bj[\"{{{AI@j{AILh}}AJ@l}{{LFb{HIhJGh}}}}\",[]]Bm[\"{{{AI@j{CAnAILj}}AJ@l}{{LFb{HIhJGh}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}LAl}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}LAl}{{AI@j{CAnBCCf}}}}\",[]]Bl[\"{{}{{LFb{{LAj{BB@hBEIh}}{Nd{JFd}}}}}}\",[]]Ai[\"{AILf{{LFf{HIh}}}}\",[]]A`[\"{AIMdCHl}\",[]]A`[\"{AIMfCHl}\",[]]An[\"{{{AI@j{ABJn}}B@A`}HIh}\",[]]D`[\"{{{AI@j{CAnAILj}}LEn{LEh{ABJj}}}{{LFb{{LEh{ABJj}}AIL`}}}}\",[]]o[\"{{}AI@j}\",[]]Aj[\"{{{AI@j{AILh}}}LAl}\",[]]Bn[\"{{{AI@j{CAnB@G`}}CHl}{{AI@j{CAnB@G`}}}}\",[]]Bn[\"{{{AI@j{CAnB@Gd}}CHl}{{AI@j{CAnB@Gd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}LGn}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}LH`}{{AI@j{CAnBCCf}}}}\",[]]:Bb[\"{{{AI@j{CAnAILj}}NlLH`}HIh}\",[]]Ak[\"{{}{{LFb{B@A`JGb}}}}\",[]]Ak[\"{{}{{LFb{B@AdJGd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CMf}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CMf}{{AI@j{CAnBCCf}}}}\",[]]65Cf[\"{{{AI@j{CAnOj}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Ch[\"{{{AI@j{CAnAHE`}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Cf[\"{{{AI@j{CAnOl}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Cf[\"{{{AI@j{CAnOn}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Cf[\"{{{AI@j{CAnNh}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Ch[\"{{{AI@j{CAnABIl}}{AI@j{CAnAIK`}}}{{LFb{HIhJFh}}}}\",[]]Bi[\"{{{AI@j{CAnAHEn}}{AI@j{AHEn}}}CMf}\",[]]n[\"{{}HIh}\",[]]Ce[\"{{{AI@j{CAnBCCd}}{LEh{LAl}}}{{AI@j{CAnBCCd}}}}\",[]]Ce[\"{{{AI@j{CAnBCCf}}{LEh{LAl}}}{{AI@j{CAnBCCf}}}}\",[]]Bn[\"{{{AI@j{AILh}}}{{LFb{{A@h{AJ@l}}JGh}}}}\",[]]Cd[\"{{{AI@j{CAnAILj}}OGd}{{LFb{{A@h{AJ@l}}JGh}}}}\",[]]Am[\"{{{AI@j{CAnAILl}}}HIh}\",[]]Al[\"{{{AI@j{CAnJEh}}}HIh}\",[]]Am[\"{{{AI@j{CAnAILn}}}HIh}\",[]]Al[\"{{{AI@j{CAnJEj}}}HIh}\",[]]Ai[\"{AILf{{LFf{HIh}}}}\",[]]Bj[\"{{{AI@j{AILh}}AJ@l}{{LFb{HIhJGh}}}}\",[]]Bm[\"{{{AI@j{CAnAILj}}AJ@l}{{LFb{HIhJGh}}}}\",[]]D`[\"{{{AI@j{CAnAILj}}LEn{LEh{ABJj}}}{{LFb{{LEh{ABJj}}AIL`}}}}\",[]]Ce[\"{{{AI@j{CAnBCCd}}{Ml{BEBj}}}{{AI@j{CAnBCCd}}}}\",[]]Ce[\"{{{AI@j{CAnBCCf}}{Ml{BEBl}}}{{AI@j{CAnBCCf}}}}\",[]]Cf[\"{{{AI@j{CAnBCCd}}{LEh{ABJj}}}{{AI@j{CAnBCCd}}}}\",[]]Cf[\"{{{AI@j{CAnBCCf}}{LEh{ABJj}}}{{AI@j{CAnBCCf}}}}\",[]]Aa[\"{BACnAINd}\",[]]Aa[\"{BAD`AINd}\",[]]9Bn[\"{{{AI@j{CAnBCCd}}CMf}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CMf}{{AI@j{CAnBCCf}}}}\",[]]Am[\"{{{AI@j{AILh}}LGn}HIh}\",[]]o[\"{OF`OF`}\",[]]Bn[\"{{{AI@j{CAnBCCd}}CHj}{{AI@j{CAnBCCd}}}}\",[]]Bn[\"{{{AI@j{CAnBCCf}}CHj}{{AI@j{CAnBCCf}}}}\",[]]3109854Bn[\"{{{AI@j{AILh}}}{{LFb{{A@h{AJ@l}}JGh}}}}\",[]]Bn[\"{{{AI@j{AILj}}}{{LFb{{A@h{AJ@l}}JGh}}}}\",[]]10A`[\"{BF@fLEh}\",[]]Bm[\"{{{AI@j{CAnBCCd}}Md}{{AI@j{CAnBCCd}}}}\",[]]Bm[\"{{{AI@j{CAnBCCf}}Md}{{AI@j{CAnBCCf}}}}\",[]]o[\"{{}AINf}\",[]]") \ No newline at end of file diff --git a/pr/323/docs/search.index/function/3b5fc09aa528.js b/pr/323/docs/search.index/function/3b5fc09aa528.js new file mode 100644 index 000000000..56c8757af --- /dev/null +++ b/pr/323/docs/search.index/function/3b5fc09aa528.js @@ -0,0 +1 @@ +rd_("Bf[\"{{{AI@l{AO@h}}{AI@l{AO@h}}}CMh}\",[]]Bf[\"{{{AI@l{BEDb}}{AI@l{BEDb}}}CMh}\",[]]Bf[\"{{{AI@l{BBEb}}{AI@l{BBEb}}}CMh}\",[]]Bf[\"{{{AI@l{AHHj}}{AI@l{AHHj}}}CMh}\",[]]Bf[\"{{{AI@l{AJ@b}}{AI@l{AJ@b}}}CMh}\",[]]Bf[\"{{{AI@l{BBE`}}{AI@l{BBE`}}}CMh}\",[]]Bf[\"{{{AI@l{AJBf}}{AI@l{AJBf}}}CMh}\",[]]Bf[\"{{{AI@l{B@H`}}{AI@l{B@H`}}}CMh}\",[]]Bd[\"{{{AI@l{LH`}}{AI@l{LH`}}}CMh}\",[]]Bf[\"{{{AI@l{BFH`}}{AI@l{BFH`}}}CMh}\",[]]Bd[\"{{{AI@l{JEb}}{AI@l{JEb}}}CMh}\",[]]Bd[\"{{{AI@l{LEn}}{AI@l{LEn}}}CMh}\",[]]Bf[\"{{{AI@l{AHHd}}{AI@l{AHHd}}}CMh}\",[]]Bd[\"{{{AI@l{OKf}}{AI@l{OKf}}}CMh}\",[]]Bf[\"{{{AI@l{B@B`}}{AI@l{B@B`}}}CMh}\",[]]Bf[\"{{{AI@l{BC@n}}{AI@l{BC@n}}}CMh}\",[]]Bf[\"{{{AI@l{AOBb}}{AI@l{AOBb}}}CMh}\",[]]Bf[\"{{{AI@l{AO@n}}{AI@l{AO@n}}}CMh}\",[]]Bf[\"{{{AI@l{AHFb}}{AI@l{AHFb}}}CMh}\",[]]Bf[\"{{{AI@l{BC@h}}{AI@l{BC@h}}}CMh}\",[]]Bf[\"{{{AI@l{BDH`}}{AI@l{BDH`}}}CMh}\",[]]Bf[\"{{{AI@l{B@Bl}}{AI@l{B@Bl}}}CMh}\",[]]Bf[\"{{{AI@l{B@Cb}}{AI@l{B@Cb}}}CMh}\",[]]Bf[\"{{{AI@l{B@Cl}}{AI@l{B@Cl}}}CMh}\",[]]Bf[\"{{{AI@l{BEKf}}{AI@l{BEKf}}}CMh}\",[]]Bd[\"{{{AI@l{CLd}}{AI@l{CLd}}}CMh}\",[]]Bb[\"{{{AI@l{Nj}}{AI@l{Nj}}}CMh}\",[]]Bf[\"{{{AI@l{ABMn}}{AI@l{ABMn}}}CMh}\",[]]Bf[\"{{{AI@l{BGJd}}{AI@l{BGJd}}}CMh}\",[]]Bd[\"{{{AI@l{JGd}}{AI@l{JGd}}}CMh}\",[]]Bd[\"{{{AI@l{LHb}}{AI@l{LHb}}}CMh}\",[]]Bf[\"{{{AI@l{BFHb}}{AI@l{BFHb}}}CMh}\",[]]Bd[\"{{{AI@l{JEd}}{AI@l{JEd}}}CMh}\",[]]Bf[\"{{{AI@l{BEDd}}{AI@l{BEDd}}}CMh}\",[]]Bd[\"{{{AI@l{LF`}}{AI@l{LF`}}}CMh}\",[]]Bf[\"{{{AI@l{AIOf}}{AI@l{AIOf}}}CMh}\",[]]Bf[\"{{{AI@l{AHHb}}{AI@l{AHHb}}}CMh}\",[]]Bf[\"{{{AI@l{BEK`}}{AI@l{BEK`}}}CMh}\",[]]Bf[\"{{{AI@l{AILd}}{AI@l{AILd}}}CMh}\",[]]Bf[\"{{{AI@l{AHHf}}{AI@l{AHHf}}}CMh}\",[]]Bf[\"{{{AI@l{AHHl}}{AI@l{AHHl}}}CMh}\",[]]Bf[\"{{{AI@l{BAGd}}{AI@l{BAGd}}}CMh}\",[]]Bd[\"{{{AI@l{OKh}}{AI@l{OKh}}}CMh}\",[]]Bf[\"{{{AI@l{AJBh}}{AI@l{AJBh}}}CMh}\",[]]Bf[\"{{{AI@l{AOB`}}{AI@l{AOB`}}}CMh}\",[]]Bf[\"{{{AI@l{AODd}}{AI@l{AODd}}}CMh}\",[]]Bf[\"{{{AI@l{BBEd}}{AI@l{BBEd}}}CMh}\",[]]Bf[\"{{{AI@l{B@Bh}}{AI@l{B@Bh}}}CMh}\",[]]Bf[\"{{{AI@l{BCA`}}{AI@l{BCA`}}}CMh}\",[]]Bf[\"{{{AI@l{AHGl}}{AI@l{AHGl}}}CMh}\",[]]Bf[\"{{{AI@l{BBA`}}{AI@l{BBA`}}}CMh}\",[]]Bf[\"{{{AI@l{AOBd}}{AI@l{AOBd}}}CMh}\",[]]Bf[\"{{{AI@l{AOA`}}{AI@l{AOA`}}}CMh}\",[]]Bf[\"{{{AI@l{AHFd}}{AI@l{AHFd}}}CMh}\",[]]Bf[\"{{{AI@l{BBBn}}{AI@l{BBBn}}}CMh}\",[]]Bf[\"{{{AI@l{AINf}}{AI@l{AINf}}}CMh}\",[]]Bf[\"{{{AI@l{AJAb}}{AI@l{AJAb}}}CMh}\",[]]Bf[\"{{{AI@l{B@Cf}}{AI@l{B@Cf}}}CMh}\",[]]Bf[\"{{{AI@l{BC@l}}{AI@l{BC@l}}}CMh}\",[]]Bf[\"{{{AI@l{BDHd}}{AI@l{BDHd}}}CMh}\",[]]Bf[\"{{{AI@l{BDHl}}{AI@l{BDHl}}}CMh}\",[]]Bf[\"{{{AI@l{B@Bn}}{AI@l{B@Bn}}}CMh}\",[]]Bf[\"{{{AI@l{ABKn}}{AI@l{ABKn}}}CMh}\",[]]Bf[\"{{{AI@l{BED`}}{AI@l{BED`}}}CMh}\",[]]Bf[\"{{{AI@l{B@Cd}}{AI@l{B@Cd}}}CMh}\",[]]Bf[\"{{{AI@l{B@Cn}}{AI@l{B@Cn}}}CMh}\",[]]Bf[\"{{{AI@l{BBEl}}{AI@l{BBEl}}}CMh}\",[]]Bf[\"{{{AI@l{BEKh}}{AI@l{BEKh}}}CMh}\",[]]Bd[\"{{{AI@l{CLf}}{AI@l{CLf}}}CMh}\",[]]Bb[\"{{{AI@l{Nl}}{AI@l{Nl}}}CMh}\",[]]Bf[\"{{{AI@l{ABN`}}{AI@l{ABN`}}}CMh}\",[]]Bf[\"{{{AI@l{AHF`}}{AI@l{AHF`}}}CMh}\",[]]Bf[\"{{{AI@l{BAH`}}{AI@l{BAH`}}}CMh}\",[]]Bf[\"{{{AI@l{BF@j}}{AI@l{BF@j}}}CMh}\",[]]Bd[\"{{{AI@l{JGh}}{AI@l{JGh}}}CMh}\",[]]Bf[\"{{{AI@l{BGJf}}{AI@l{BGJf}}}CMh}\",[]]Bd[\"{{{AI@l{JGf}}{AI@l{JGf}}}CMh}\",[]]Bf[\"{{{AI@l{B@D`}}{AI@l{B@D`}}}CMh}\",[]]Ai[\"{{{AI@l{CKf}}}LEn}\",[]]Ak[\"{{{AI@l{AIOn}}}ABMn}\",[]]Ak[\"{{{AI@l{BF@l}}}ABMn}\",[]]Ak[\"{{{AI@l{AJ@j}}}ABMn}\",[]]o[\"{LEbLF`}\",[]]o[\"{LCnLF`}\",[]]A`[\"{AHChLF`}\",[]]A`[\"{BAEbLF`}\",[]]A`[\"{B@FfLF`}\",[]]A`[\"{B@EhLF`}\",[]]Aa[\"{ABMbABN`}\",[]]Aa[\"{ABOlABN`}\",[]]Aa[\"{ABKdABN`}\",[]]A`[\"{OJ`ABN`}\",[]]n[\"{{}OIh}\",[]]A`[\"{CJnABKn}\",[]]Be[\"{{{AI@l{LH`}}{AI@l{LH`}}}ABM`}\",[]]Be[\"{{{AI@l{LEn}}{AI@l{LEn}}}ABM`}\",[]]Bg[\"{{{AI@l{AOBb}}{AI@l{AOBb}}}ABM`}\",[]]Bg[\"{{{AI@l{B@Cb}}{AI@l{B@Cb}}}ABM`}\",[]]Be[\"{{{AI@l{CLd}}{AI@l{CLd}}}ABM`}\",[]]Bc[\"{{{AI@l{Nj}}{AI@l{Nj}}}ABM`}\",[]]Bg[\"{{{AI@l{ABMn}}{AI@l{ABMn}}}ABM`}\",[]]Be[\"{{{AI@l{LHb}}{AI@l{LHb}}}ABM`}\",[]]Bg[\"{{{AI@l{BEDd}}{AI@l{BEDd}}}ABM`}\",[]]Be[\"{{{AI@l{LF`}}{AI@l{LF`}}}ABM`}\",[]]Bg[\"{{{AI@l{AHHl}}{AI@l{AHHl}}}ABM`}\",[]]Bg[\"{{{AI@l{BAGd}}{AI@l{BAGd}}}ABM`}\",[]]Bg[\"{{{AI@l{AJBh}}{AI@l{AJBh}}}ABM`}\",[]]Bg[\"{{{AI@l{AOBd}}{AI@l{AOBd}}}ABM`}\",[]]Bg[\"{{{AI@l{BDHl}}{AI@l{BDHl}}}ABM`}\",[]]Bg[\"{{{AI@l{BED`}}{AI@l{BED`}}}ABM`}\",[]]Bg[\"{{{AI@l{B@Cd}}{AI@l{B@Cd}}}ABM`}\",[]]Be[\"{{{AI@l{CLf}}{AI@l{CLf}}}ABM`}\",[]]Bc[\"{{{AI@l{Nl}}{AI@l{Nl}}}ABM`}\",[]]Bg[\"{{{AI@l{ABN`}}{AI@l{ABN`}}}ABM`}\",[]]o[\"{ABMnNj}\",[]]o[\"{ABN`Nl}\",[]]n[\"{LEdNl}\",[]]o[\"{AHDdNl}\",[]]A`[\"{ABO`LEj}\",[]]A`[\"{ABObLEj}\",[]]A`[\"{ABMjLEj}\",[]]A`[\"{ABOdLEj}\",[]]Bg[\"{{{AI@l{Oj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AO@h}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBEf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BGC`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AILh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AILl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BFHf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BEDb}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{ABK`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BACn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABKh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AJ@d}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BBBl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{CKf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AIOn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BF@l}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBEb}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AHHj}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AJ@b}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BBE`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{B@Hb}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AOCl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AJ@j}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AJBf}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{B@H`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Cd[\"{{{AI@l{LH`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{BFH`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Cd[\"{{{AI@l{JEb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cd[\"{{{AI@l{LEn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{ABOf}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AIMh}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AHHd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BCAf}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cd[\"{{{AI@l{OKf}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{B@B`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{BC@n}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{BCBj}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BCCh}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BEGb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BEI`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AOBb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BBBb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{B@Gd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BFOb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{B@Ad}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AO@n}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{AHFb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{BC@h}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{BDH`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{B@Bl}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{B@Cb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{B@Cl}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BEKf}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{AIMn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BDKn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cd[\"{{{AI@l{CLd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cc[\"{{{AI@l{Nj}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{ABMn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Ce[\"{{{AI@l{ABO`}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cc[\"{{{AI@l{Mn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AHDj}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cd[\"{{{AI@l{JEj}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AOAh}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Cd[\"{{{AI@l{OHb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BAFd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BECb}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BGJd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{AOAd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BEKn}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]Ce[\"{{{AI@l{BFGh}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Cd[\"{{{AI@l{JGd}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Bi[\"{{{AI@l{AILn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABKb}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{LHb}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BFHb}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bh[\"{{{AI@l{JEd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BEDd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bh[\"{{{AI@l{LF`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{B@Fl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AIOf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AHHb}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BEK`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AILd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{ABOh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AIMj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AHHf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BCAh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AHHl}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BAGd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bh[\"{{{AI@l{OKh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AJBh}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AOB`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AODd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBEd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@Bh}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BCA`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AHGl}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BBA`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BGBh}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bh[\"{{{AI@l{JGn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BCBl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BCCj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BEGd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BEIb}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AOBd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBBd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@Gh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BFOd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@Ah}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AOA`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AHFd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BBBn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AINf}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AJAb}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{B@Cf}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BC@l}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BDHd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{ABJd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BDHl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABKj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@Bn}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AO@d}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AJ@f}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BAFj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BCBh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BGBn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABKn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BED`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BCAd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BACl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@Cd}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{B@Cn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBEl}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BEKh}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{AIN`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BGNh}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BDL`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{CLf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bg[\"{{{AI@l{Nl}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{ABN`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{ABOb}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AHF`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BAH`}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BF@j}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{JGh}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bi[\"{{{AI@l{BGJf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AOAf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BELd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BFGj}}{AI@l{CB`AHEn}}}LFb}\",[]]0Cd[\"{{{AI@l{JGf}}{AI@l{CB`AHEn}}}{{LFd{HIjJFd}}}}\",[]]0Bg[\"{{{AI@l{N`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AHDl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{JEl}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{AOAj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{OHd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BAFf}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BECd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bh[\"{{{AI@l{JGj}}{AI@l{CB`AHEn}}}LFb}\",[]]0Bh[\"{{{AI@l{JH`}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{BBEn}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABMj}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{ABOd}}{AI@l{CB`AHEn}}}LFb}\",[]]Bi[\"{{{AI@l{B@D`}}{AI@l{CB`AHEn}}}LFb}\",[]]Ak[\"{{{AI@l{B@Fl}}}ABJl}\",[]]A`[\"{ABOfCHn}\",[]]A`[\"{ABOhCHn}\",[]]Aj[\"{{{AI@l{BAFj}}}LAn}\",[]]A`[\"{BGBnCHn}\",[]]o[\"{CJnCHn}\",[]]A`[\"{ABMjLAn}\",[]]Ce[\"{{{AI@l{CB`BFOb}}{Ml{ABNh}}}{{AI@l{CB`BFOb}}}}\",[]]Ce[\"{{{AI@l{CB`BFOd}}{Ml{ABNj}}}{{AI@l{CB`BFOd}}}}\",[]]8Af[\"{{ABJlCHn}BBEf}\",[]]Ag[\"{{}{{Ml{BGC`}}}}\",[]]Ae[\"{{LAnCHl}BABl}\",[]]Cd[\"{{BBBb{LEj{B@Gd}}AHIb{Ml{OIj}}}{{LFf{ABK`}}}}\",[]]Ah[\"{{{Nd{JJb}}}AIOh}\",[]]A`[\"{OIbBCBj}\",[]]Ah[\"{{{Ml{OG`}}}BBBb}\",[]]Bc[\"{{{Ml{B@Gf}}{Ml{BECf}}}B@Gd}\",[]]Ai[\"{{{Ml{B@Af}}}B@Ad}\",[]]B`[\"{{{AI@l{{KOj{Md}}}}}B@Cb}\",[]]Ae[\"{{CHlLAn}BDKn}\",[]]Ag[\"{{CLdNjCHn}ABMn}\",[]]Aj[\"{{{Ml{AHDj}}CHj}Mn}\",[]]An[\"{{{Ml{AOAh}}OGfCHj}JEj}\",[]]An[\"{{{Ml{BAFd}}OGfCHj}OHb}\",[]]Ae[\"{{LAnCHl}BABn}\",[]]Ah[\"{{{Nd{JJb}}}AIOj}\",[]]Bm[\"{{{Ml{BBBd}}{LEj{{Ml{B@Gh}}}}CMh}ABKb}\",[]]A`[\"{LAnBHAj}\",[]]o[\"{{}BGMn}\",[]]A`[\"{OIbBCBl}\",[]]Ah[\"{{{Ml{OGb}}}BBBd}\",[]]Bc[\"{{{Ml{B@Gj}}{Ml{BECh}}}B@Gh}\",[]]Ai[\"{{{Ml{B@Aj}}}B@Ah}\",[]]Ek[\"{{ABIj{AI@l{c}}{AI@l{{KOj{CHl}}}}CMh}{{LFd{{LAl{BAFj{LEj{ABIj}}}}BED`}}}{BEGnJIj}}\",[\"\"]]B`[\"{{{AI@l{{KOj{Md}}}}}B@Cd}\",[]]Af[\"{{BEKhLGl}BBEl}\",[]]A`[\"{LAnBGNh}\",[]]Ae[\"{{CHlLAn}BDL`}\",[]]Ag[\"{{CLfNlCHn}ABN`}\",[]]Aj[\"{{{Ml{AHDl}}CHj}N`}\",[]]An[\"{{{Ml{AOAj}}OGfCHj}JEl}\",[]]An[\"{{{Ml{BAFf}}OGfCHj}OHd}\",[]]Aj[\"{AJBd{{LFf{BBEn}}}}\",[]]Ai[\"{{{Ml{BDKh}}}BGHh}\",[]]Ai[\"{{{Ml{BDKl}}}BGHj}\",[]]o[\"{CLdCLd}\",[]]o[\"{CLfCLf}\",[]]Ak[\"{{{AI@l{AJAh}}}AJAf}\",[]]Ai[\"{{{AI@l{OIj}}}OGf}\",[]]0Aj[\"{{{AI@l{B@Hb}}}OGf}\",[]]Ak[\"{{{AI@l{BAGl}}}AJAf}\",[]]Ak[\"{{{AI@l{AJAj}}}AJAf}\",[]]Ak[\"{{{AI@l{BAGn}}}AJAf}\",[]]Bg[\"{{{AI@l{AILl}}LEn}{{LEj{ABJl}}}}\",[]]Aa[\"{AHHdABJl}\",[]]Bg[\"{{{AI@l{AILn}}LF`}{{LEj{ABJl}}}}\",[]]Aa[\"{AHHfABJl}\",[]]Ag[\"{Ol{{LFh{HIj}}}}\",[]]Ag[\"{On{{LFh{HIj}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Aa[\"{B@EhAJA`}\",[]]Aa[\"{ABMjAJA`}\",[]]Aa[\"{BBElBEKh}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]A`[\"{ABJdJE`}\",[]]Am[\"{{{AI@l{CB`ABLl}}}HIj}\",[]]Am[\"{{{AI@l{CB`ABKh}}}HIj}\",[]]Am[\"{{{AI@l{CB`AIOn}}}HIj}\",[]]Am[\"{{{AI@l{CB`AJ@j}}}HIj}\",[]]Al[\"{{{AI@l{CB`LCf}}}HIj}\",[]]A`[\"{cc{}}\",[\"T\"]]0000000000000000000000Aa[\"{BC@nBEDb}\",[]]111111111Aa[\"{AHHjBBEb}\",[]]2Aa[\"{BC@nAHHj}\",[]]Aa[\"{AJ@bAHHj}\",[]]4Aa[\"{BC@nAJ@b}\",[]]5Aa[\"{AHHjBBE`}\",[]]66666Aa[\"{BC@nAJBf}\",[]]Aa[\"{B@H`AJBf}\",[]]Aa[\"{B@B`AJBf}\",[]]Aa[\"{BC@nB@H`}\",[]]::::::A`[\"{BEKfLH`}\",[]];o[\"{CHlLH`}\",[]]A`[\"{ABMnLH`}\",[]]n[\"{MdLH`}\",[]]o[\"{CHjLH`}\",[]]???Al[\"{cLEn{{CJh{CHl}}}}\",[\"T\"]]A`[\"{cc{}}\",[\"T\"]]00000Aa[\"{AO@lBC@n}\",[]]A`[\"{JFnBC@n}\",[]]A`[\"{JEhBC@n}\",[]]33333A`[\"{LH`AOBb}\",[]]44444Aa[\"{BFH`AO@n}\",[]]5Aa[\"{BCClAO@n}\",[]]66A`[\"{JFnBC@h}\",[]]77Aa[\"{BEKnB@Bl}\",[]]888A`[\"{LH`BEKf}\",[]]9999A`[\"{LH`ABMn}\",[]]::::::::::::Aa[\"{AODjAOAd}\",[]];;;;;;;;A`[\"{BGKdJGd}\",[]]A`[\"{BDIhJGd}\",[]]A`[\"{BBC`JGd}\",[]]A`[\"{BBDbJGd}\",[]]A`[\"{BDK`JGd}\",[]]A`[\"{BDHfJGd}\",[]]A`[\"{BGEdJGd}\",[]]A`[\"{BGJbJGd}\",[]]A`[\"{BCC`JGd}\",[]]A`[\"{BCAjJGd}\",[]]A`[\"{AINlJGd}\",[]]A`[\"{cc{}}\",[\"T\"]]000000000000000n[\"{MdLHb}\",[]]o[\"{CHjLHb}\",[]]o[\"{CHlLHb}\",[]]3A`[\"{ABN`LHb}\",[]]A`[\"{BEKhLHb}\",[]]5555Al[\"{cLF`{{CJh{CHl}}}}\",[\"T\"]]666666666666666666Aa[\"{BBElBCA`}\",[]]7777A`[\"{AHHbJGn}\",[]]88888A`[\"{LHbAOBd}\",[]]9999Aa[\"{BFHbAOA`}\",[]]:Aa[\"{BCClAOA`}\",[]];;;;;;Aa[\"{BBElBC@l}\",[]]<<<>>>>A`[\"{JGhBBEl}\",[]]A`[\"{LHbBEKh}\",[]]A`[\"{cc{}}\",[\"T\"]]00000A`[\"{LHbABN`}\",[]]11111A`[\"{BAH`JGh}\",[]]2222Aa[\"{AODjAOAf}\",[]]3333333A`[\"{BDHfJGf}\",[]]A`[\"{BDK`JGf}\",[]]A`[\"{BCAjJGf}\",[]]A`[\"{BCC`JGf}\",[]]A`[\"{BGJbJGf}\",[]]A`[\"{BGEdJGf}\",[]]A`[\"{BGKdJGf}\",[]]:A`[\"{BDIhJGf}\",[]]A`[\"{BBC`JGf}\",[]]A`[\"{AINlJGf}\",[]]A`[\"{BBDbJGf}\",[]]>>>>>>>>>An[\"{{{AI@l{c}}}AJBdCId}\",[\"S\"]]????????????????Bm[\"{{{AI@l{LH`}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bk[\"{{{AI@l{LEn}}{AI@l{CB`c}}}HIjLDl}\",[\"H\"]]Bn[\"{{{AI@l{AOBb}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{B@Cb}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bm[\"{{{AI@l{CLd}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bl[\"{{{AI@l{Nj}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{ABMn}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bm[\"{{{AI@l{LHb}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{BEDd}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bk[\"{{{AI@l{LF`}}{AI@l{CB`c}}}HIjLDl}\",[\"H\"]]Bn[\"{{{AI@l{AHHl}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{BAGd}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{AJBh}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{AOBd}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{BDHl}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{BED`}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{B@Cd}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bm[\"{{{AI@l{CLf}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bl[\"{{{AI@l{Nl}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{ABN`}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Bn[\"{{{AI@l{AHF`}}{AI@l{CB`c}}}HIjLDl}\",[\"__H\"]]Aa[\"{{}c{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n[\"{{}HIj}\",[]]0000Cc[\"{{{AI@l{CB`LCf}}LAn}{{LFd{{LEj{JEd}}AHHl}}}}\",[]]El[\"{{{AI@l{OEh}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{{AI@l{CB`{KOj{Md}}}}AOAd}}}}\",[]]Bh[\"{{{AI@l{CB`OJj}}Nl}{{LEj{ABN`}}}}\",[]]El[\"{{{AI@l{OEj}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{{AI@l{CB`{KOj{Md}}}}AOAf}}}}\",[]]Bf[\"{{{AI@l{AILl}}LEn}{{LEj{CKf}}}}\",[]]Bf[\"{{{AI@l{CKf}}}{{LFd{HIjAIKn}}}}\",[]]A`[\"{AIMhCHn}\",[]]Am[\"{{{AI@l{CB`AILn}}}HIj}\",[]]A`[\"{AIMjCHn}\",[]]Ck[\"{{{A@b{{AI@l{CB`AIKf}}}}{AI@l{CB`OFd}}}{{CKn{HIj}}}}\",[]]Dk[\"{{{A@b{{AI@l{CB`BCDb}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`ABLl}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`ABLn}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`AHE`}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`AILh}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@l{CB`OHh}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@l{CB`LE`}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`AHCl}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`ABIh}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`B@Fd}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`B@Fn}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dj[\"{{{A@b{{AI@l{CB`LBf}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Dk[\"{{{A@b{{AI@l{CB`BBBl}}}}{AI@l{CB`OFd}}}{{CKn{c}}}{}}\",[\"Future::Output\"]]Bf[\"{{{AI@l{CB`AILn}}}{{LEj{JGn}}}}\",[]]De[\"{{{AI@l{CB`AIOn}}{AI@l{CB`{KOj{Md}}}}}{{LFd{{LEj{LAn}}AHHj}}}}\",[]]Bm[\"{{{AI@l{CB`AJ@`}}CMh}{{LFd{LCfBAGd}}}}\",[]]Bo[\"{{CLf{AI@l{CB`c}}}{{LFd{BF@jJGh}}}Nf}\",[\"R\"]]Bf[\"{{{AI@l{CB`AHEh}}}{{LEj{JE`}}}}\",[]]Ec[\"{{{AI@l{BBEn}}AJBd{AI@l{CB`{KOj{AINb}}}}{AI@l{CB`{KOj{ABMj}}}}}{{LFf{LAn}}}}\",[]]Dk[\"{{{AI@l{OEh}}{AI@l{CB`{A@h{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjAOAd}}}}\",[]]Dk[\"{{{AI@l{OEj}}{AI@l{CB`{A@h{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjAOAf}}}}\",[]]C`[\"{{{AI@l{CB`AHEh}}JE`CMh}{{LFd{HIjBEDd}}}}\",[]]Cc[\"{{{AI@l{BBEn}}AJBd{AI@l{ABOd}}}{{LFf{HIj}}}}\",[]]Bn[\"{{{AI@l{CB`BFOb}}CHl}{{AI@l{CB`BFOb}}}}\",[]]Bn[\"{{{AI@l{CB`BFOd}}CHl}{{AI@l{CB`BFOd}}}}\",[]]Aj[\"{{{AI@l{AILl}}}CLd}\",[]]Aj[\"{{{AI@l{AILn}}}CLf}\",[]]Cn[\"{{{AI@l{OG`}}{AI@l{{KOj{Md}}}}{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]Cn[\"{{{AI@l{OGb}}{AI@l{{KOj{Md}}}}{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]A`[\"{BBEfCHn}\",[]]A`[\"{ABO`LAn}\",[]]A`[\"{ABObLAn}\",[]]o[\"{JIfCMh}\",[]]Bm[\"{{{AI@l{CB`AIOn}}LH`}{{LFd{HIjB@B`}}}}\",[]]Bm[\"{{{AI@l{CB`BF@l}}LH`}{{LFd{HIjB@B`}}}}\",[]]Bm[\"{{{AI@l{CB`AJ@`}}LHb}{{LFd{HIjB@Bh}}}}\",[]]Bn[\"{{{AI@l{AJB`}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Bn[\"{{{AI@l{BBCn}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Bn[\"{{{AI@l{BDKn}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Bn[\"{{{AI@l{AJBb}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Bn[\"{{{AI@l{BBD`}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Bn[\"{{{AI@l{BDL`}}{AI@l{CHh}}}{{LEj{JE`}}}}\",[]]Aa[\"{{}c{}}\",[\"V\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bg[\"{{{Ml{BEBn}}OGfCHj}{{Nd{AIM`}}}}\",[]]Bg[\"{{{Ml{AHDj}}OGfCHj}{{Nd{AIM`}}}}\",[]]Bg[\"{{{Ml{AOAh}}OGfCHj}{{Nd{AIM`}}}}\",[]]Bg[\"{{{Ml{BAFd}}OGfCHj}{{Nd{AIM`}}}}\",[]]Bg[\"{{{Ml{BEC`}}OGfCHj}{{Nd{AIMb}}}}\",[]]Bg[\"{{{Ml{AHDl}}OGfCHj}{{Nd{AIMb}}}}\",[]]Bg[\"{{{Ml{AOAj}}OGfCHj}{{Nd{AIMb}}}}\",[]]Bg[\"{{{Ml{BAFf}}OGfCHj}{{Nd{AIMb}}}}\",[]]Ai[\"{BEOl{{Ml{BEC`}}}}\",[]]o[\"{JEbJE`}\",[]]A`[\"{ABOfCHn}\",[]]o[\"{OKfLAn}\",[]]o[\"{JEdJE`}\",[]]A`[\"{ABOhCHn}\",[]]o[\"{OKhLAn}\",[]]Aa[\"{AO@dBCA`}\",[]]Ag[\"{{{AI@l{Oj}}}Oj}\",[]]Ak[\"{{{AI@l{AO@h}}}AO@h}\",[]]Ak[\"{{{AI@l{AILl}}}AILl}\",[]]Ak[\"{{{AI@l{BFHf}}}BFHf}\",[]]Ak[\"{{{AI@l{BEDb}}}BEDb}\",[]]Ak[\"{{{AI@l{ABK`}}}ABK`}\",[]]Ak[\"{{{AI@l{BACn}}}BACn}\",[]]Ak[\"{{{AI@l{BBEb}}}BBEb}\",[]]Ak[\"{{{AI@l{AHHj}}}AHHj}\",[]]Ak[\"{{{AI@l{AJ@b}}}AJ@b}\",[]]Ak[\"{{{AI@l{BBE`}}}BBE`}\",[]]Ak[\"{{{AI@l{AJBf}}}AJBf}\",[]]Ak[\"{{{AI@l{B@H`}}}B@H`}\",[]]Ai[\"{{{AI@l{LH`}}}LH`}\",[]]Ak[\"{{{AI@l{BFH`}}}BFH`}\",[]]Ai[\"{{{AI@l{LEn}}}LEn}\",[]]Ak[\"{{{AI@l{ABOf}}}ABOf}\",[]]Ak[\"{{{AI@l{AIMh}}}AIMh}\",[]]Ak[\"{{{AI@l{AHHd}}}AHHd}\",[]]Ak[\"{{{AI@l{BCAf}}}BCAf}\",[]]Ai[\"{{{AI@l{OKf}}}OKf}\",[]]Ak[\"{{{AI@l{B@B`}}}B@B`}\",[]]Ak[\"{{{AI@l{BC@n}}}BC@n}\",[]]Ak[\"{{{AI@l{BCCh}}}BCCh}\",[]]Ak[\"{{{AI@l{BEGb}}}BEGb}\",[]]Ak[\"{{{AI@l{BEI`}}}BEI`}\",[]]Ak[\"{{{AI@l{AOBb}}}AOBb}\",[]]Ak[\"{{{AI@l{BBBb}}}BBBb}\",[]]Ak[\"{{{AI@l{B@Gd}}}B@Gd}\",[]]Ak[\"{{{AI@l{BFOb}}}BFOb}\",[]]Ak[\"{{{AI@l{B@Ad}}}B@Ad}\",[]]Ak[\"{{{AI@l{AO@n}}}AO@n}\",[]]Ak[\"{{{AI@l{AHFb}}}AHFb}\",[]]Ak[\"{{{AI@l{BC@h}}}BC@h}\",[]]Ak[\"{{{AI@l{BDH`}}}BDH`}\",[]]Ak[\"{{{AI@l{B@Bl}}}B@Bl}\",[]]Ak[\"{{{AI@l{B@Cb}}}B@Cb}\",[]]Ak[\"{{{AI@l{B@Cl}}}B@Cl}\",[]]Ak[\"{{{AI@l{BEKf}}}BEKf}\",[]]Ak[\"{{{AI@l{AIMn}}}AIMn}\",[]]Ai[\"{{{AI@l{CLd}}}CLd}\",[]]Ag[\"{{{AI@l{Nj}}}Nj}\",[]]Ak[\"{{{AI@l{ABMn}}}ABMn}\",[]]Ag[\"{{{AI@l{Mn}}}Mn}\",[]]Ak[\"{{{AI@l{AHDj}}}AHDj}\",[]]Ai[\"{{{AI@l{JEj}}}JEj}\",[]]Ak[\"{{{AI@l{AOAh}}}AOAh}\",[]]Ai[\"{{{AI@l{OHb}}}OHb}\",[]]Ak[\"{{{AI@l{BAFd}}}BAFd}\",[]]Ak[\"{{{AI@l{BFGh}}}BFGh}\",[]]Ai[\"{{{AI@l{JGd}}}JGd}\",[]]Ai[\"{{{AI@l{LHb}}}LHb}\",[]]Ak[\"{{{AI@l{BFHb}}}BFHb}\",[]]Ak[\"{{{AI@l{BEDd}}}BEDd}\",[]]Ai[\"{{{AI@l{LF`}}}LF`}\",[]]Ak[\"{{{AI@l{B@Fl}}}B@Fl}\",[]]Ak[\"{{{AI@l{AIOf}}}AIOf}\",[]]Ak[\"{{{AI@l{AHHb}}}AHHb}\",[]]Ak[\"{{{AI@l{BEK`}}}BEK`}\",[]]Ak[\"{{{AI@l{AILd}}}AILd}\",[]]Ak[\"{{{AI@l{ABOh}}}ABOh}\",[]]Ak[\"{{{AI@l{AIMj}}}AIMj}\",[]]Ak[\"{{{AI@l{AHHf}}}AHHf}\",[]]Ak[\"{{{AI@l{BCAh}}}BCAh}\",[]]Ak[\"{{{AI@l{AHHl}}}AHHl}\",[]]Ak[\"{{{AI@l{BAGd}}}BAGd}\",[]]Ai[\"{{{AI@l{OKh}}}OKh}\",[]]Ak[\"{{{AI@l{AJBh}}}AJBh}\",[]]Ak[\"{{{AI@l{AOB`}}}AOB`}\",[]]Ak[\"{{{AI@l{BBEd}}}BBEd}\",[]]Ak[\"{{{AI@l{B@Bh}}}B@Bh}\",[]]Ak[\"{{{AI@l{BCA`}}}BCA`}\",[]]Ak[\"{{{AI@l{AHGl}}}AHGl}\",[]]Ak[\"{{{AI@l{BBA`}}}BBA`}\",[]]Ak[\"{{{AI@l{BGBh}}}BGBh}\",[]]Ak[\"{{{AI@l{BCCj}}}BCCj}\",[]]Ak[\"{{{AI@l{BEGd}}}BEGd}\",[]]Ak[\"{{{AI@l{BEIb}}}BEIb}\",[]]Ak[\"{{{AI@l{AOBd}}}AOBd}\",[]]Ak[\"{{{AI@l{BBBd}}}BBBd}\",[]]Ak[\"{{{AI@l{B@Gh}}}B@Gh}\",[]]Ak[\"{{{AI@l{BFOd}}}BFOd}\",[]]Ak[\"{{{AI@l{B@Ah}}}B@Ah}\",[]]Ak[\"{{{AI@l{AOA`}}}AOA`}\",[]]Ak[\"{{{AI@l{AHFd}}}AHFd}\",[]]Ak[\"{{{AI@l{AINf}}}AINf}\",[]]Ak[\"{{{AI@l{AJAb}}}AJAb}\",[]]Ak[\"{{{AI@l{B@Cf}}}B@Cf}\",[]]Ak[\"{{{AI@l{BC@l}}}BC@l}\",[]]Ak[\"{{{AI@l{BDHd}}}BDHd}\",[]]Ak[\"{{{AI@l{ABJd}}}ABJd}\",[]]Ak[\"{{{AI@l{BDHl}}}BDHl}\",[]]Ak[\"{{{AI@l{B@Bn}}}B@Bn}\",[]]Ak[\"{{{AI@l{BCBh}}}BCBh}\",[]]Ak[\"{{{AI@l{BGBn}}}BGBn}\",[]]Ak[\"{{{AI@l{ABKn}}}ABKn}\",[]]Ak[\"{{{AI@l{BED`}}}BED`}\",[]]Ak[\"{{{AI@l{B@Cd}}}B@Cd}\",[]]Ak[\"{{{AI@l{B@Cn}}}B@Cn}\",[]]Ak[\"{{{AI@l{BBEl}}}BBEl}\",[]]Ak[\"{{{AI@l{BEKh}}}BEKh}\",[]]Ak[\"{{{AI@l{AIN`}}}AIN`}\",[]]Ak[\"{{{AI@l{BGNh}}}BGNh}\",[]]Ai[\"{{{AI@l{CLf}}}CLf}\",[]]Ag[\"{{{AI@l{Nl}}}Nl}\",[]]Ak[\"{{{AI@l{ABN`}}}ABN`}\",[]]Ak[\"{{{AI@l{AHF`}}}AHF`}\",[]]Ak[\"{{{AI@l{BAH`}}}BAH`}\",[]]Ak[\"{{{AI@l{BF@j}}}BF@j}\",[]]Ai[\"{{{AI@l{JGh}}}JGh}\",[]]Ak[\"{{{AI@l{BFGj}}}BFGj}\",[]]Ai[\"{{{AI@l{JGf}}}JGf}\",[]]Ag[\"{{{AI@l{N`}}}N`}\",[]]Ak[\"{{{AI@l{AHDl}}}AHDl}\",[]]Ai[\"{{{AI@l{JEl}}}JEl}\",[]]Ak[\"{{{AI@l{AOAj}}}AOAj}\",[]]Ai[\"{{{AI@l{OHd}}}OHd}\",[]]Ak[\"{{{AI@l{BAFf}}}BAFf}\",[]]Ai[\"{{{AI@l{JH`}}}JH`}\",[]]Ak[\"{{{AI@l{ABMj}}}ABMj}\",[]]Ak[\"{{{AI@l{ABOd}}}ABOd}\",[]]Ak[\"{{{AI@l{B@D`}}}B@D`}\",[]]Ak[\"{{{AI@l{BEOl}}}BEOl}\",[]]Ak[\"{{{AI@l{BCB`}}}BCB`}\",[]]Bn[\"{{{AI@l{AILl}}LH`{AI@l{{KOj{Md}}}}}HIj}\",[]]Bn[\"{{{AI@l{ABK`}}LH`{AI@l{{KOj{Md}}}}}HIj}\",[]]Bg[\"{{{AI@l{CKf}}}{{LFd{AHE`BB@n}}}}\",[]]Bf[\"{{{AI@l{CB`AILn}}OGfLHbJE`}HIj}\",[]]Bj[\"{{{AI@l{B@Cb}}}{{AI@l{{KOj{Md}}}}}}\",[]]Bj[\"{{{AI@l{B@Cd}}}{{AI@l{{KOj{Md}}}}}}\",[]]Aa[\"{BAEbAHGl}\",[]]Aa[\"{BBElAINf}\",[]]A`[\"{ABMnCHn}\",[]]A`[\"{ABN`CHn}\",[]]A`[\"{B@ClCMh}\",[]]A`[\"{B@CnCMh}\",[]]l[\"{OGh}\",[]]l[\"{OGj}\",[]]Ce[\"{{{AI@l{BEGn}}{AI@l{CB`Nf}}}{{LFd{B@CdBED`}}}}\",[]]Ce[\"{{{AI@l{BHAj}}{AI@l{CB`Nf}}}{{LFd{B@CdBED`}}}}\",[]]Bc[\"{{{AI@l{AILn}}}{{A@h{LF`}}}}\",[]]Bd[\"{{{AI@l{JIl}}{AI@l{CHh}}}HIj}\",[]]Am[\"{{{AI@l{AO@j}}LAn}HIj}\",[]]Ai[\"{{{AI@l{JIn}}}HIj}\",[]]Bg[\"{{{A@b{{AI@l{CB`AIKf}}}}OGf}HIj}\",[]]Bm[\"{{{AI@l{CB`AJ@j}}LH`}{{LFd{HIjB@B`}}}}\",[]]Bm[\"{{{AI@l{CB`AJ@l}}LHb}{{LFd{HIjB@Bh}}}}\",[]]Am[\"{ABKh{{LFd{HIjAJ@d}}}}\",[]]Dc[\"{{{AI@l{CB`ABKb}}ABKj{AI@l{CB`{A@h{Md}}}}}{{LFd{ABObAJ@f}}}}\",[]]Bi[\"{{{AI@l{OIj}}{A@b{{Nd{LDj}}}}}HIj}\",[]]Bj[\"{{{AI@l{B@Hb}}{A@b{{Nd{LDj}}}}}HIj}\",[]]Bj[\"{{{AI@l{AOCl}}{A@b{{Nd{LDj}}}}}HIj}\",[]]n[\"{OjCMh}\",[]]Ak[\"{{{AI@l{AILl}}}BCAf}\",[]]Ak[\"{{{AI@l{ABK`}}}BACn}\",[]]An[\"{{{AI@l{CB`AILn}}}BCAh}\",[]]Ce[\"{{{AI@l{CB`AIOh}}{LEj{LGl}}}{{AI@l{CB`AIOh}}}}\",[]]Ce[\"{{{AI@l{CB`AIOj}}{LEj{LGl}}}{{AI@l{CB`AIOj}}}}\",[]]Ck[\"{{{AI@l{CB`AJ@j}}{AI@l{{KOj{Md}}}}}{{LFd{LAnAJBf}}}}\",[]]Ck[\"{{{AI@l{CB`AJ@l}}{AI@l{{KOj{Md}}}}}{{LFd{LAnAJBh}}}}\",[]]Bl[\"{{{AI@l{BF@j}}{AI@l{CB`c}}}HIjLCd}\",[\"W\"]]Aj[\"{{{AI@l{ABK`}}}LBf}\",[]]An[\"{ABKh{{LFd{AILhBC@n}}}}\",[]]Bh[\"{{{AI@l{CB`OJj}}Nl}{{LEj{ABN`}}}}\",[]]Fh[\"{{{AI@l{CB`ABKb}}ABKjOGf{AI@l{CB`{A@h{Md}}}}{LEj{{Ml{B@Gh}}}}}{{LFd{{LAl{BDHlAILn}}{Nd{AO@d}}}}}}\",[]]Am[\"{AI@l{{AI@l{c}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000o[\"{OKfLAn}\",[]]o[\"{OKhLAn}\",[]]o[\"{OjAO@h}\",[]]Bb[\"{{AJA`BB@lOj}{{LFh{AO@j}}}}\",[]]Aj[\"{AJA`{{LFf{ABK`}}}}\",[]]B`[\"{{}{{LFd{HIj{Nd{JFf}}}}}}\",[]]Ak[\"{{{AI@l{AILl}}}BC@n}\",[]]Be[\"{{{AI@l{ABKb}}}{{AI@l{BBBd}}}}\",[]]o[\"{MdBEKf}\",[]]A`[\"{AIMhCHn}\",[]]o[\"{B@GdMl}\",[]]o[\"{MdBEKh}\",[]]A`[\"{AIMjCHn}\",[]]o[\"{B@GhMl}\",[]]A`[\"{BBElLEj}\",[]]Bl[\"{{{AI@l{CB`c}}}{{LFd{LH`BAH`}}}Nf}\",[\"B\"]]Bl[\"{{{AI@l{CB`c}}}{{LFd{LEnBAH`}}}Nf}\",[\"B\"]]Bm[\"{{{AI@l{CB`c}}}{{LFd{AHFbBAH`}}}Nf}\",[\"B\"]]Bm[\"{{{AI@l{CB`c}}}{{LFd{BEKfBAH`}}}Nf}\",[\"B\"]]Bm[\"{{{AI@l{CB`c}}}{{LFd{ABMnBAH`}}}Nf}\",[\"B\"]]Bh[\"{{{AI@l{CB`c}}}{{LFl{LHb}}}Nf}\",[\"B\"]]Bh[\"{{{AI@l{CB`c}}}{{LFl{LF`}}}Nf}\",[\"B\"]]Bi[\"{{{AI@l{CB`c}}}{{LFl{AHFd}}}Nf}\",[\"B\"]]Bi[\"{{{AI@l{CB`c}}}{{LFl{AINf}}}Nf}\",[\"B\"]]Ek[\"{{{AI@l{CB`{LDh{ABIj}}}}{AI@l{c}}{AI@l{{KOj{CHl}}}}CMh}{{LFd{BCBhBED`}}}{BEGnJIj}}\",[\"\"]]Bi[\"{{{AI@l{CB`c}}}{{LFl{BEKh}}}Nf}\",[\"B\"]]Bi[\"{{{AI@l{CB`c}}}{{LFl{ABN`}}}Nf}\",[\"B\"]]Bi[\"{{{AI@l{CB`c}}}{{LFl{AHEj}}}Nf}\",[\"B\"]]A`[\"{ABMjLEj}\",[]]Bk[\"{{{AI@l{LH`}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bk[\"{{{AI@l{LEn}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{AHFb}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{BEKf}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{ABMn}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bk[\"{{{AI@l{LHb}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bk[\"{{{AI@l{LF`}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{AHFd}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{AINf}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{ABJd}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{BEKh}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{ABN`}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bl[\"{{{AI@l{AHEl}}{AI@l{CB`c}}}HIjLCd}\",[\"B\"]]Bj[\"{{{AI@l{CB`AJ@j}}}{{LFd{HIjB@B`}}}}\",[]]Bj[\"{{{AI@l{CB`AJ@l}}}{{LFd{HIjAOB`}}}}\",[]]An[\"{{{AI@l{AODh}}ABJl}HIj}\",[]]Ea[\"{{{AI@l{CB`ABKb}}OGfAHF`{LEj{B@Cn}}ABIj{AI@l{CB`{A@h{Md}}}}}{{LEj{BACj}}}}\",[]]o[\"{CJjOGh}\",[]]o[\"{CJlOGj}\",[]]A`[\"{ABKhHIj}\",[]]Ba[\"{{{AI@l{CB`ABKb}}ABKj}HIj}\",[]]Bh[\"{{{AI@l{AJB`}}{AI@l{CHh}}JE`}HIj}\",[]]Bh[\"{{{AI@l{BBCn}}{AI@l{CHh}}JE`}HIj}\",[]]Bh[\"{{{AI@l{BDKn}}{AI@l{CHh}}JE`}HIj}\",[]]Bh[\"{{{AI@l{AJBb}}{AI@l{CHh}}JE`}HIj}\",[]]Bh[\"{{{AI@l{BBD`}}{AI@l{CHh}}JE`}HIj}\",[]]Bh[\"{{{AI@l{BDL`}}{AI@l{CHh}}JE`}HIj}\",[]]A`[\"{AHEbCMh}\",[]]o[\"{JEbCHn}\",[]]o[\"{JEdCHn}\",[]]Ce[\"{{{AI@l{CB`AIM`}}OGfOGfCHnCMh{AI@l{B@Fj}}}HIj}\",[]]Cc[\"{{{AI@l{CB`Mn}}OGfOGfCHnCMh{AI@l{B@Fj}}}HIj}\",[]]Cd[\"{{{AI@l{CB`JEj}}OGfOGfCHnCMh{AI@l{B@Fj}}}HIj}\",[]]Cd[\"{{{AI@l{CB`OHb}}OGfOGfCHnCMh{AI@l{B@Fj}}}HIj}\",[]]Ce[\"{{{AI@l{CB`AIMb}}OGfOGfCHnCMh{AI@l{B@Fl}}}HIj}\",[]]Cc[\"{{{AI@l{CB`N`}}OGfOGfCHnCMh{AI@l{B@Fl}}}HIj}\",[]]Cd[\"{{{AI@l{CB`JEl}}OGfOGfCHnCMh{AI@l{B@Fl}}}HIj}\",[]]Cd[\"{{{AI@l{CB`OHd}}OGfOGfCHnCMh{AI@l{B@Fl}}}HIj}\",[]]o[\"{CJjOGh}\",[]]o[\"{CJlOGj}\",[]]o[\"{BDLfMd}\",[]]A`[\"{BC@hJE`}\",[]]A`[\"{BDH`JE`}\",[]]A`[\"{BC@lJE`}\",[]]A`[\"{BDHdJE`}\",[]]A`[\"{BBElLGl}\",[]]Aa[\"{BBAdBCA`}\",[]]Bg[\"{{{AI@l{ABK`}}AHIb}{{LFf{HIj}}}}\",[]]A`[\"{ABKhHIj}\",[]]Cf[\"{{{AI@l{CB`ABKb}}ABKj{AI@l{CB`{A@h{Md}}}}}ABOb}\",[]]l[\"{OGh}\",[]]Aa[\"{AHF`AJA`}\",[]]l[\"{OGj}\",[]]Am[\"{{ABJnOj}{{LFh{HIj}}}}\",[]]B`[\"{{B@GdAJA`}{{LFf{ABK`}}}}\",[]]Bb[\"{B@Gb{{LFd{HIj{Nd{JFf}}}}}}\",[]]Bk[\"{{{AI@l{BEDb}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{BBEb}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{AHHj}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{AJ@b}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{BBE`}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{AJBf}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{B@H`}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{BC@n}}}{{LEj{{AI@l{JFf}}}}}}\",[]]Bk[\"{{{AI@l{BCA`}}}{{LEj{{AI@l{JFf}}}}}}\",[]]A`[\"{ABO`LEj}\",[]]A`[\"{ABObLEj}\",[]]A`[\"{ABOdLEj}\",[]]Bg[\"{{{AI@l{CKf}}}{{LFd{AIOdAIKn}}}}\",[]]Aa[\"{B@FfAIOf}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]A`[\"{ABMjLAn}\",[]]Aa[\"{BCAfABOf}\",[]]Aa[\"{BCAhABOh}\",[]]10Dh[\"{{{AI@l{OG`}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjAOAd}}}}\",[]]Dh[\"{{{AI@l{OGb}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjAOAf}}}}\",[]]Aj[\"{{{AI@l{AIM`}}}CHn}\",[]]Ah[\"{{{AI@l{Mn}}}CHn}\",[]]Ai[\"{{{AI@l{JEj}}}CHn}\",[]]Ai[\"{{{AI@l{OHb}}}CHn}\",[]]Aj[\"{{{AI@l{AIMb}}}CHn}\",[]]Ah[\"{{{AI@l{N`}}}CHn}\",[]]Ai[\"{{{AI@l{JEl}}}CHn}\",[]]Ai[\"{{{AI@l{OHd}}}CHn}\",[]]Aj[\"{{{AI@l{BFN`}}}LAn}\",[]]Aj[\"{{{AI@l{BFNb}}}LAn}\",[]]Aj[\"{{{AI@l{BGMn}}}LAn}\",[]]Aj[\"{{{AI@l{BGNh}}}LAn}\",[]]n[\"{OjLAn}\",[]]n[\"{{}OFb}\",[]]0000o[\"{NhABIn}\",[]]Ba[\"{{AI@l{AI@l{c}}}ABM`{}}\",[\"K\"]]0000000000000000000Cg[\"{{{AI@l{ABK`}}AJA`{AI@l{CHh}}}{{LFd{AILhB@Bl}}}}\",[]]Dl[\"{{{AI@l{CB`ABKb}}OGfB@AhAJA`{AI@l{CHh}}}{{LFd{{LAl{BDHlAILn}}B@Bn}}}}\",[]]Dm[\"{{{AI@l{AHGf}}LEnCHn{AI@l{{KOj{Md}}}}{AI@l{CB`ABIj}}}{{LFd{HIjAOAd}}}}\",[]]Ca[\"{{{AI@l{AHFh}}LAn{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]Dm[\"{{{AI@l{AHGh}}LF`CHn{AI@l{{KOj{Md}}}}{AI@l{CB`ABIj}}}{{LFd{HIjAOAf}}}}\",[]]Ca[\"{{{AI@l{AHFj}}LAn{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]o[\"{{}AODf}\",[]]n[\"{{}JIl}\",[]]o[\"{{}AO@j}\",[]]o[\"{{}BABl}\",[]]o[\"{{}BACn}\",[]]n[\"{{}LH`}\",[]]n[\"{{}LEn}\",[]]o[\"{{}ABOf}\",[]]o[\"{{}AIMh}\",[]]o[\"{{}AHHd}\",[]]o[\"{{}BCAf}\",[]]n[\"{{}OKf}\",[]]o[\"{{}B@B`}\",[]]o[\"{{}BCCh}\",[]]o[\"{{}BEGb}\",[]]o[\"{{}BEI`}\",[]]o[\"{{}AOBb}\",[]]o[\"{{}BBBb}\",[]]o[\"{{}BFOb}\",[]]o[\"{{}BDKn}\",[]]o[\"{{}AHDj}\",[]]o[\"{{}AOAh}\",[]]o[\"{{}BAFd}\",[]]o[\"{{}BECb}\",[]]o[\"{{}BABn}\",[]]o[\"{{}BGMn}\",[]]n[\"{{}LHb}\",[]]n[\"{{}LF`}\",[]]o[\"{{}AIOf}\",[]]o[\"{{}AILd}\",[]]o[\"{{}ABOh}\",[]]o[\"{{}AIMj}\",[]]o[\"{{}AHHf}\",[]]o[\"{{}BCAh}\",[]]n[\"{{}OKh}\",[]]o[\"{{}BBEd}\",[]]o[\"{{}B@Bh}\",[]]o[\"{{}BCCj}\",[]]o[\"{{}BEGd}\",[]]o[\"{{}BEIb}\",[]]o[\"{{}AOBd}\",[]]o[\"{{}BBBd}\",[]]o[\"{{}BFOd}\",[]]o[\"{{}BGNh}\",[]]o[\"{{}BDL`}\",[]]o[\"{{}AHDl}\",[]]o[\"{{}AOAj}\",[]]o[\"{{}BAFf}\",[]]o[\"{{}BECd}\",[]]o[\"{{}ABMj}\",[]]n[\"{{}JIn}\",[]]o[\"{{}BCB`}\",[]]o[\"{{}BACl}\",[]]Ak[\"{{{AI@l{BAFj}}}B@Cd}\",[]]Ak[\"{{{AI@l{BCBh}}}B@Cd}\",[]]Aa[\"{BGBnB@Cd}\",[]]Aa[\"{BEKjB@Cd}\",[]]A`[\"{CJnB@Cd}\",[]]A`[\"{JIbB@Cd}\",[]]A`[\"{JIfB@Cd}\",[]]Aa[\"{BDLfB@Cd}\",[]]Cg[\"{{{AI@l{AHGf}}LEnCHn{AI@l{CB`{KOj{Md}}}}LAn}HIj}\",[]]Ca[\"{{{AI@l{AHFh}}LAn{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]Cg[\"{{{AI@l{AHGh}}LF`CHn{AI@l{CB`{KOj{Md}}}}LAn}HIj}\",[]]Ca[\"{{{AI@l{AHFj}}LAn{AI@l{CB`{KOj{Md}}}}}HIj}\",[]]Aj[\"{{{AI@l{AIOn}}}CMh}\",[]]Aj[\"{{{AI@l{BF@l}}}CMh}\",[]]Ak[\"{{{AI@l{AIM`}}}BECb}\",[]]Ai[\"{{{AI@l{Mn}}}BECb}\",[]]Aj[\"{{{AI@l{JEj}}}BECb}\",[]]Aj[\"{{{AI@l{OHb}}}BECb}\",[]]Ak[\"{{{AI@l{AIMb}}}BECd}\",[]]Ai[\"{{{AI@l{N`}}}BECd}\",[]]Aj[\"{{{AI@l{JEl}}}BECd}\",[]]Aj[\"{{{AI@l{OHd}}}BECd}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CHj}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CHj}{{AI@l{CB`BCCj}}}}\",[]]Bf[\"{{{AI@l{CB`AIM`}}OGfCHnCHn}HIj}\",[]]Bd[\"{{{AI@l{CB`Mn}}OGfCHnCHn}HIj}\",[]]Bf[\"{{{AI@l{CB`AIMb}}OGfCHnCHn}HIj}\",[]]Bd[\"{{{AI@l{CB`N`}}OGfCHnCHn}HIj}\",[]]Aj[\"{{{AI@l{AILl}}}LE`}\",[]]Bc[\"{{{AI@l{ABLn}}}{{LEj{LEn}}}}\",[]]Aa[\"{BGBnB@Cd}\",[]]Aa[\"{BEKjB@Cd}\",[]]A`[\"{CJnB@Cd}\",[]]A`[\"{JIbB@Cd}\",[]]Aa[\"{BDLfB@Cd}\",[]]Di[\"{{{AI@l{AJ@j}}}{{`{{LDj{}{{LEl{{LFd{{LEj{LH`}}B@H`}}}}}}CLbCLh}}}}\",[]]Bn[\"{{{AI@l{AJ@l}}}{{LFd{{LEj{LHb}}B@Bh}}}}\",[]]n[\"{OjLAn}\",[]]o[\"{JIlLAn}\",[]]Am[\"{{{AI@l{CB`AILn}}}OJj}\",[]]Aj[\"{{{AI@l{AHGf}}}LAn}\",[]]Aj[\"{{{AI@l{AHGh}}}LAn}\",[]]A`[\"{AI@lLGn}\",[]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bd[\"{{{AI@l{BFHf}}}{{LEj{AILl}}}}\",[]]Bn[\"{{{AI@l{CB`B@Ad}}CHl}{{AI@l{CB`B@Ad}}}}\",[]]Bn[\"{{{AI@l{CB`B@Ah}}CHl}{{AI@l{CB`B@Ah}}}}\",[]]A`[\"{BGBnCHl}\",[]]A`[\"{BEKjCHl}\",[]]o[\"{CJnCHl}\",[]]o[\"{JIbCHl}\",[]]A`[\"{AHEbLEj}\",[]]Aa[\"{ABOdAI@l}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Aa[\"{BBEfABJl}\",[]]A`[\"{LCfBBEd}\",[]]Aa[\"{BCAfAIMh}\",[]]Aa[\"{BCAhAIMj}\",[]]10Bg[\"{{{AI@l{CB`c}}LAn}B@Cb{NfJIj}}\",[\"\"]]Bg[\"{{{AI@l{CB`c}}LAn}B@Cd{NfJIj}}\",[\"\"]]o[\"{{}BCBj}\",[]]o[\"{{}BCBl}\",[]]A`[\"{CHnBGMn}\",[]]Cd[\"{{{AI@l{CHh}}}{{LFh{AO@hc}}}{}}\",[\"FromStr::Err\"]]o[\"{CHlLH`}\",[]]o[\"{CHlLHb}\",[]]Al[\"{CHn{{LFd{LH`BFH`}}}}\",[]]Al[\"{CHn{{LFd{LHbBFHb}}}}\",[]]Aa[\"{BFFjAOAb}\",[]]Aa[\"{BHAlBADj}\",[]]Aa[\"{BFFlAOAb}\",[]]Aa[\"{BHAnBADj}\",[]]Af[\"{{}{{LEj{Ab}}}}\",[]]0000Aj[\"{{{AI@l{AILn}}}CMh}\",[]]Bo[\"{{{AI@l{CB`BEI`}}ABJl}{{AI@l{CB`BEI`}}}}\",[]]Bo[\"{{{AI@l{CB`BEIb}}ABJl}{{AI@l{CB`BEIb}}}}\",[]]Ao[\"{{{Nd{AIM`}}}{{Nd{Mj}}}}\",[]]Am[\"{{{Nd{Mn}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{JEj}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{OHb}}}{{Nd{Mj}}}}\",[]]Ao[\"{{{Nd{AIMb}}}{{Nd{Mj}}}}\",[]]Am[\"{{{Nd{N`}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{JEl}}}{{Nd{Mj}}}}\",[]]An[\"{{{Nd{OHd}}}{{Nd{Mj}}}}\",[]]Aj[\"{{{AI@l{BFHf}}}CMh}\",[]]Bo[\"{{{AI@l{CB`BFOb}}ABJl}{{AI@l{CB`BFOb}}}}\",[]]Bo[\"{{{AI@l{CB`BFOd}}ABJl}{{AI@l{CB`BFOd}}}}\",[]]Bc[\"{{{AI@l{AILh}}}{{LEj{LDn}}}}\",[]]Bc[\"{{{AI@l{AILl}}}{{LEj{LDn}}}}\",[]]Bc[\"{{{AI@l{ABKh}}}{{LEj{LDn}}}}\",[]]Bc[\"{{{AI@l{ABKj}}}{{LEj{LDn}}}}\",[]]A`[\"{AHF`LEj}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Bc[\"{{{AI@l{AHEh}}}{{LEj{LAn}}}}\",[]]Am[\"{{{AI@l{AODh}}LAn}HIj}\",[]]Aj[\"{{{AI@l{AILl}}}OHh}\",[]]Bg[\"{{{AI@l{AJ@j}}}{{LFd{C@jB@B`}}}}\",[]]Bg[\"{{{AI@l{AJ@l}}}{{LFd{C@jB@Bh}}}}\",[]]A`[\"{BADbLEj}\",[]]A`[\"{BADdLEj}\",[]]A`[\"{AHEbLEj}\",[]]A`[\"{AO@dLEj}\",[]]Ck[\"{{{AI@l{CB`BBBb}}{LEj{{JJd{Md}}}}}{{AI@l{CB`BBBb}}}}\",[]]Ck[\"{{{AI@l{CB`BBBd}}{LEj{{JJd{Md}}}}}{{AI@l{CB`BBBd}}}}\",[]]A`[\"{BECbLEj}\",[]]A`[\"{BECdLEj}\",[]]Ac[\"{AI@lc{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Ao[\"{c{{LFd{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000000000000000000000Al[\"{CHn{{LFd{LH`BFH`}}}}\",[]]Al[\"{LAn{{LFd{LH`BFH`}}}}\",[]]2Al[\"{HIh{{LFd{LH`BFH`}}}}\",[]]333333333333333Aj[\"{ABJl{{LFd{AOBb}}}}\",[]]444444Ai[\"{CHn{{LFd{AHFb}}}}\",[]]55555555555555555555555555Bb[\"{{{Ml{B@A`}}}{{LFd{BDKf}}}}\",[]]Aj[\"{B@A`{{LFd{BDKf}}}}\",[]]77Bb[\"{{{Ml{B@G`}}}{{LFd{BDKj}}}}\",[]]Aj[\"{B@G`{{LFd{BDKj}}}}\",[]]999999999999999999Al[\"{LAn{{LFd{LHbBFHb}}}}\",[]]:Al[\"{CHn{{LFd{LHbBFHb}}}}\",[]]Al[\"{HIh{{LFd{LHbBFHb}}}}\",[]]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ce[\"{{{Ml{B@A`}}}{{LFd{BDKhc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@A`{{LFd{BDKhc}}}{}}\",[\"TryFrom::Error\"]]Ce[\"{{{Ml{B@G`}}}{{LFd{BDKlc}}}{}}\",[\"TryFrom::Error\"]]Bm[\"{B@G`{{LFd{BDKlc}}}{}}\",[\"TryFrom::Error\"]]Ao[\"{c{{LFd{e}}}{}{}}\",[\"U\",\"T\"]]000000000000000000000000000Aj[\"{{}{{LFd{c}}}{}}\",[\"U\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cd[\"{{{A@b{{AI@l{CB`c}}}}{AI@l{CB`OFd}}}CKn{}}\",[\"F\"]]000000000Cc[\"{{{AI@l{BBEn}}AJBd{AI@l{ABOd}}}{{LFf{HIj}}}}\",[]]Bk[\"{{{AI@l{BFN`}}B@Cb}{{LFd{HIjAIMn}}}}\",[]]Bk[\"{{{AI@l{BFNb}}B@Cd}{{LFd{HIjAIN`}}}}\",[]]0Bk[\"{{{AI@l{BGMn}}B@Cd}{{LFd{HIjAIN`}}}}\",[]]Ak[\"{{{AI@l{AILl}}}ABIh}\",[]]Bi[\"{{{AI@l{CB`B@Hd}}}{{LFd{LH`JG`}}}}\",[]]Bi[\"{{{AI@l{CB`B@Hd}}}{{LFd{CLdJG`}}}}\",[]]Bh[\"{{{AI@l{CB`B@Hd}}}{{LFd{NjJG`}}}}\",[]]Bj[\"{{{AI@l{CB`B@Hd}}}{{LFd{ABMnJG`}}}}\",[]]Bf[\"{{{AI@l{CB`B@Hd}}}{{LFj{LHb}}}}\",[]]Bf[\"{{{AI@l{CB`B@Hd}}}{{LFj{CLf}}}}\",[]]Be[\"{{{AI@l{CB`B@Hd}}}{{LFj{Nl}}}}\",[]]Bg[\"{{{AI@l{CB`B@Hd}}}{{LFj{ABN`}}}}\",[]]Aa[\"{AO@hBFGn}\",[]]Bc[\"{{{AI@l{AIM`}}}{{Nd{AIM`}}}}\",[]]Ba[\"{{{AI@l{Mn}}}{{Nd{AIM`}}}}\",[]]Bb[\"{{{AI@l{JEj}}}{{Nd{AIM`}}}}\",[]]Bb[\"{{{AI@l{OHb}}}{{Nd{AIM`}}}}\",[]]Bc[\"{{{AI@l{AIMb}}}{{Nd{AIMb}}}}\",[]]Ba[\"{{{AI@l{N`}}}{{Nd{AIMb}}}}\",[]]Bb[\"{{{AI@l{JEl}}}{{Nd{AIMb}}}}\",[]]Bb[\"{{{AI@l{OHd}}}{{Nd{AIMb}}}}\",[]]A`[\"{ABOfCHn}\",[]]An[\"{{{AI@l{CB`AILn}}}AHEh}\",[]]A`[\"{ABOhCHn}\",[]]C`[\"{{{AI@l{CB`B@Cb}}}{{AI@l{CB`{KOj{Md}}}}}}\",[]]C`[\"{{{AI@l{CB`B@Cd}}}{{AI@l{CB`{KOj{Md}}}}}}\",[]]Ah[\"{Md{{LEj{B@Cl}}}}\",[]]Ah[\"{Md{{LEj{B@Cn}}}}\",[]]Ah[\"{Md{{LEj{B@D`}}}}\",[]]A`[\"{ABMnCLd}\",[]]A`[\"{ABN`CLf}\",[]]Bi[\"{AILh{{LFd{{LAl{AILlBCDb}}AILh}}}}\",[]]o[\"{CLdCMh}\",[]]o[\"{CLfCMh}\",[]]Aj[\"{{{AI@l{AILn}}}CMh}\",[]]21Aj[\"{{{AI@l{ABKh}}}CMh}\",[]]Aj[\"{{{AI@l{ABKj}}}CMh}\",[]]Bn[\"{{{AI@l{CB`B@Gd}}CMh}{{AI@l{CB`B@Gd}}}}\",[]]Bn[\"{{{AI@l{CB`B@Gh}}CMh}{{AI@l{CB`B@Gh}}}}\",[]]Bl[\"{{{AI@l{OIj}}OGf}{{A@b{{Nd{AIKf}}}}}}\",[]]Bm[\"{{{AI@l{B@Hb}}OGf}{{A@b{{Nd{AIKf}}}}}}\",[]]Bm[\"{{{AI@l{AOCl}}OGf}{{A@b{{Nd{AIKf}}}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Ak[\"{{{AI@l{AILl}}}ABLl}\",[]]Bc[\"{{{AI@l{AILl}}AJA`AIOd}ABLn}\",[]]Ce[\"{{{AI@l{CB`AILn}}AHF`AIOfOGf}{{LFd{LF`AHGl}}}}\",[]]43Bm[\"{{{AI@l{CB`AILn}}LF`}{{LFd{HIjAILd}}}}\",[]]E`[\"{{{A@b{{AI@l{CB`BDGl}}}}{AI@l{CB`OFd}}}{{CKn{{LEj{c}}}}}{}}\",[\"Stream::Item\"]]Ef[\"{{{A@b{{AI@l{CB`AIOn}}}}{AI@l{CB`OFd}}{AI@l{CB`{KOj{Md}}}}}{{CKn{{LFf{LAn}}}}}}\",[]]E`[\"{{{A@b{{AI@l{CB`AIOn}}}}{AI@l{CB`OFd}}{AI@l{CB`OId}}}{{CKn{{LFf{HIj}}}}}}\",[]]Ec[\"{{{AI@l{CB`AIOn}}{AI@l{CB`OFd}}{AI@l{CB`{KOj{Md}}}}}{{CKn{{LFd{LAnAHHj}}}}}}\",[]]Fg[\"{{{AI@l{CB`BB@j}}{AI@l{CB`OFd}}{AI@l{CB`{KOj{AINb}}}}{AI@l{CB`{KOj{ABMj}}}}}{{CKn{{LFf{LAn}}}}}}\",[]]Dn[\"{{{A@b{{AI@l{CB`AHI`}}}}{AI@l{ABOd}}{AI@l{CB`OFd}}}{{CKn{{LFf{HIj}}}}}}\",[]];:Cd[\"{{{AI@l{CB`BBBb}}{Ml{OG`}}}{{AI@l{CB`BBBb}}}}\",[]]Cd[\"{{{AI@l{CB`BBBd}}{Ml{OGb}}}{{AI@l{CB`BBBd}}}}\",[]]Cj[\"{{{AI@l{B@Gf}}CHlB@Cb{AI@l{{KOj{Md}}}}}{{JJd{Md}}}}\",[]]Cj[\"{{{AI@l{BDKj}}CHlB@Cb{AI@l{{KOj{Md}}}}}{{JJd{Md}}}}\",[]]Cj[\"{{{AI@l{B@Gj}}CHlB@Cd{AI@l{{KOj{Md}}}}}{{JJd{Md}}}}\",[]]Cj[\"{{{AI@l{BDKl}}CHlB@Cd{AI@l{{KOj{Md}}}}}{{JJd{Md}}}}\",[]]Cj[\"{{{AI@l{BGHj}}CHlB@C`{AI@l{{KOj{Md}}}}}{{JJd{Md}}}}\",[]]Bb[\"{LAn{{LAl{LAn{LEj{LAn}}}}}}\",[]]00000Aj[\"{{{AI@l{AILl}}}LAn}\",[]]A`[\"{AI@lLGl}\",[]]000000000000000000000000000000000000000000000000000000000000Ce[\"{{{AI@l{CB`B@Gd}}{Ml{BECf}}}{{AI@l{CB`B@Gd}}}}\",[]]Ce[\"{{{AI@l{CB`B@Gh}}{Ml{BECh}}}{{AI@l{CB`B@Gh}}}}\",[]]A`[\"{BGBnJHh}\",[]]o[\"{B@GdMl}\",[]]o[\"{B@GhMl}\",[]]Aj[\"{{{AI@l{ABK`}}}HIj}\",[]]Ck[\"{{{AI@l{CB`AJ@j}}{AI@l{{KOj{Md}}}}}{{LFd{HIjAJBf}}}}\",[]]n[\"{{}LAn}\",[]]Ak[\"{{{AI@l{AILl}}}AHCl}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CMh}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CMh}{{AI@l{CB`BCCj}}}}\",[]]Bi[\"{{{AI@l{CB`}}}{{AI@l{CB`c}}}{}}\",[\"T\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Bc[\"{{AI@l{AI@l{CB`c}}}HIj{}}\",[\"T\"]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Cc[\"{{{AI@l{CB`AILn}}OGfLF`LHb}{{LFd{HIjBBA`}}}}\",[]]A`[\"{AHEbCMh}\",[]]B`[\"{{AI@l{AI@l{c}}}CMh{}}\",[\"K\"]]00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Aa[\"{BC@hBEKf}\",[]]A`[\"{BDH`LH`}\",[]]Aa[\"{BC@lBEKh}\",[]]A`[\"{BDHdLHb}\",[]]o[\"{LCnLHb}\",[]]o[\"{OJ`LHb}\",[]]Aa[\"{BC@hAINd}\",[]]Aa[\"{BC@lAINf}\",[]]o[\"{LH`CHn}\",[]]o[\"{LHbCHn}\",[]]Aj[\"{{{AI@l{AILn}}}CMh}\",[]]Aj[\"{{{AI@l{BACl}}}CMh}\",[]]Bd[\"{{{AI@l{BB@j}}}{{LFf{AJA`}}}}\",[]]Bd[\"{{{AI@l{ABK`}}}{{LFf{AJA`}}}}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]Cb[\"{{{AI@l{BCB`}}{AI@l{AJ@h}}CHn}{{Ml{AODh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CMh}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CMh}{{AI@l{CB`BCCj}}}}\",[]]Bg[\"{{{AI@l{AILl}}LEn}{{LEj{AHHd}}}}\",[]]Bj[\"{{{AI@l{CB`AILn}}LF`}{{LEj{AHHf}}}}\",[]]Aa[\"{AHChAHHf}\",[]]Db[\"{{{A@b{{AI@l{CB`AJ@j}}}}{AI@l{CB`OFd}}}{{CKn{{LFf{HIj}}}}}}\",[]]00Eg[\"{{{A@b{{AI@l{CB`AJ@j}}}}{AI@l{CB`OFd}}{AI@l{{KOj{Md}}}}}{{CKn{{LFd{LAnAJBf}}}}}}\",[]]Ec[\"{{{A@b{{AI@l{CB`AJ@j}}}}{AI@l{CB`OFd}}{AI@l{{KOj{Md}}}}}{{CKn{{LFf{LAn}}}}}}\",[]]0Bm[\"{{{AI@l{JIn}}{AI@l{CKd}}}{{LFf{HIj}}}}\",[]]Cd[\"{{{AI@l{CB`AIOn}}LAn}{{LFd{{LEj{JEb}}AHHj}}}}\",[]]Cd[\"{{{AI@l{CB`BF@l}}LAn}{{LFd{{LEj{JEb}}AHHj}}}}\",[]]Cn[\"{{{AI@l{CB`AIOn}}{AI@l{CB`{KOj{Md}}}}}{{LFd{HIjBBE`}}}}\",[]]Bj[\"{{{AI@l{CKf}}AIOd}{{LFd{HIjBEJn}}}}\",[]]Bn[\"{{{AI@l{CB`AIOh}}OGf}{{AI@l{CB`AIOh}}}}\",[]]Bn[\"{{{AI@l{CB`AIOj}}OGf}{{AI@l{CB`AIOj}}}}\",[]]A`[\"{BBEfADf}\",[]]o[\"{JIlCHn}\",[]]o[\"{{}AJA`}\",[]]0o[\"{{}AI@l}\",[]]Bj[\"{{ABKh{Ml{B@Gd}}}{{LFd{AILhBC@n}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Bh[\"{{{AI@l{AHEb}}AJA`}{{LFh{AHIb}}}}\",[]]A`[\"{AHHdCHj}\",[]]Aj[\"{{{AI@l{AILn}}}CHj}\",[]]A`[\"{AHHfCHj}\",[]]Ce[\"{{{AI@l{CB`AIOh}}{LEj{LGl}}}{{AI@l{CB`AIOh}}}}\",[]]Ce[\"{{{AI@l{CB`AIOj}}{LEj{LGl}}}{{AI@l{CB`AIOj}}}}\",[]]Aa[\"{ABO`AJA`}\",[]]Aa[\"{ABObAJA`}\",[]]Aa[\"{ABOdAJA`}\",[]]n[\"{OjCHj}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CHj}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CHj}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHEbCHj}\",[]]Bo[\"{{{AI@l{CB`BCCh}}ABJl}{{AI@l{CB`BCCh}}}}\",[]]Bo[\"{{{AI@l{CB`BCCj}}ABJl}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHEbLEj}\",[]]k[\"{{}}\",[]]00000000000Bh[\"{ABKhc{}}\",[\"IntoFuture::IntoFuture\"]]1A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]n[\"{OjLAn}\",[]]Ao[\"{{AI@lc}{{OGl{c}}}{}}\",[\"S\"]]0C`[\"{{{AI@l{CB`AIM`}}OGfCHnCMh{LEj{CHn}}}HIj}\",[]]Bn[\"{{{AI@l{CB`Mn}}OGfCHnCMh{LEj{CHn}}}HIj}\",[]]C`[\"{{{AI@l{CB`AIMb}}OGfCHnCMh{LEj{CHn}}}HIj}\",[]]Bn[\"{{{AI@l{CB`N`}}OGfCHnCMh{LEj{CHn}}}HIj}\",[]]Bk[\"{{{AI@l{CB`JIn}}OGf{AI@l{BCB`}}}HIj}\",[]]A`[\"{BECbLEj}\",[]]A`[\"{BECdLEj}\",[]]Bn[\"{{{AI@l{LH`}}{AI@l{LH`}}}{{LEj{ABM`}}}}\",[]]Bn[\"{{{AI@l{LEn}}{AI@l{LEn}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{AOBb}}{AI@l{AOBb}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{B@Cb}}{AI@l{B@Cb}}}{{LEj{ABM`}}}}\",[]]Bn[\"{{{AI@l{CLd}}{AI@l{CLd}}}{{LEj{ABM`}}}}\",[]]Bl[\"{{{AI@l{Nj}}{AI@l{Nj}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{ABMn}}{AI@l{ABMn}}}{{LEj{ABM`}}}}\",[]]Bn[\"{{{AI@l{LHb}}{AI@l{LHb}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{BEDd}}{AI@l{BEDd}}}{{LEj{ABM`}}}}\",[]]Bn[\"{{{AI@l{LF`}}{AI@l{LF`}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{AHHl}}{AI@l{AHHl}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{BAGd}}{AI@l{BAGd}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{AJBh}}{AI@l{AJBh}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{AOBd}}{AI@l{AOBd}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{BDHl}}{AI@l{BDHl}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{BED`}}{AI@l{BED`}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{B@Cd}}{AI@l{B@Cd}}}{{LEj{ABM`}}}}\",[]]Bn[\"{{{AI@l{CLf}}{AI@l{CLf}}}{{LEj{ABM`}}}}\",[]]Bl[\"{{{AI@l{Nl}}{AI@l{Nl}}}{{LEj{ABM`}}}}\",[]]C`[\"{{{AI@l{ABN`}}{AI@l{ABN`}}}{{LEj{ABM`}}}}\",[]]Be[\"{{{AI@l{AILl}}}{{ABMf{AHH`}}}}\",[]]Bk[\"{{{AI@l{AILn}}LF`}{{LFd{AIOfAILd}}}}\",[]]Df[\"{{{AI@l{CB`AIOn}}{AI@l{CB`{KOj{JE`}}}}}{{LFd{{LEj{LAn}}AHHj}}}}\",[]]Cc[\"{{{AI@l{CB`AIOn}}LAn}{{LFd{{A@h{Md}}BBEb}}}}\",[]]Bb[\"{{{AI@l{CB`AILn}}ABN`}AJ@`}\",[]]Aj[\"{{{AI@l{AHFh}}}LAn}\",[]]Aj[\"{{{AI@l{AHFj}}}LAn}\",[]]Bb[\"{{{AI@l{CB`AILn}}ABN`}AJ@l}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CHn}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CHn}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHEbLEj}\",[]]A`[\"{BADbLEj}\",[]]A`[\"{BADdLEj}\",[]]Ce[\"{{{AI@l{CB`B@Gd}}{Ml{AJAh}}}{{AI@l{CB`B@Gd}}}}\",[]]Ce[\"{{{AI@l{CB`B@Gh}}{Ml{AJAj}}}{{AI@l{CB`B@Gh}}}}\",[]]Ce[\"{{{AI@l{CB`B@Ad}}{Ml{AJB`}}}{{AI@l{CB`B@Ad}}}}\",[]]Ce[\"{{{AI@l{CB`B@Ah}}{Ml{AJBb}}}{{AI@l{CB`B@Ah}}}}\",[]]n[\"{OjCHn}\",[]]Bn[\"{{{AI@l{CB`BEI`}}CHj}{{AI@l{CB`BEI`}}}}\",[]]Bn[\"{{{AI@l{CB`BEIb}}CHj}{{AI@l{CB`BEIb}}}}\",[]]Ak[\"{{{AI@l{AILl}}}BFHf}\",[]]Ai[\"{{{Ml{B@Gf}}}B@Gd}\",[]]Ai[\"{{{Ml{B@Gj}}}B@Gh}\",[]]Aj[\"{{BCBjc}BCBjABNf}\",[\"\"]]Aj[\"{{BCBlc}BCBlABNf}\",[\"\"]]Bm[\"{{{AI@l{CB`AJ@j}}JE`}{{LFd{HIjAJBf}}}}\",[]]n[\"{{}LDn}\",[]]o[\"{OFbOFb}\",[]]0000Bd[\"{{{AI@l{BFN`}}}{{LEj{ABJl}}}}\",[]]Bd[\"{{{AI@l{BFNb}}}{{LEj{ABJl}}}}\",[]]Bd[\"{{{AI@l{BGMn}}}{{LEj{ABJl}}}}\",[]]Bd[\"{{{AI@l{BGNh}}}{{LEj{ABJl}}}}\",[]]Bd[\"{{{AI@l{AILl}}}{{LEj{BC@n}}}}\",[]]Ck[\"{{{AI@l{ABK`}}B@AdAJA`{AI@l{CHh}}}{{LFd{AILhB@Bl}}}}\",[]]Ak[\"{{{AI@l{B@Fl}}}ABJl}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]An[\"{{AIOlCMh}{{LFh{LAn}}}}\",[]]Cj[\"{{{AI@l{OIl}}}{{LEj{{LAl{{Nd{AHFh}}{Nd{AHGf}}}}}}}}\",[]]Ck[\"{{{AI@l{AJAl}}}{{LEj{{LAl{{Nd{AHFh}}{Nd{AHGf}}}}}}}}\",[]]Cj[\"{{{AI@l{OIn}}}{{LEj{{LAl{{Nd{AHFj}}{Nd{AHGh}}}}}}}}\",[]]Ck[\"{{{AI@l{AJAn}}}{{LEj{{LAl{{Nd{AHFj}}{Nd{AHGh}}}}}}}}\",[]]A`[\"{BFFjA@h}\",[]]A`[\"{BHAlA@h}\",[]]A`[\"{BFFlA@h}\",[]]A`[\"{BHAnA@h}\",[]]An[\"{{{AI@l{CB`BFN`}}}B@Cb}\",[]]An[\"{{{AI@l{CB`BFNb}}}B@Cd}\",[]]An[\"{{{AI@l{CB`BGMn}}}B@Cd}\",[]]An[\"{{{AI@l{CB`BGNh}}}B@Cd}\",[]]Ak[\"{{{AI@l{BBEn}}}B@Eb}\",[]]Ba[\"{{{AI@l{CB`AILn}}BCAd}HIj}\",[]]Bo[\"{{{AI@l{CB`ABKb}}BDHlBACl}{{LEj{BCAd}}}}\",[]]B`[\"{{{AI@l{OIl}}B@CbCLd}CJj}\",[]]Bn[\"{{{AI@l{B@Gf}}CHlB@Cb}{{LFd{CJjBEKn}}}}\",[]]Ba[\"{{{AI@l{AJAl}}B@CbCLd}CJj}\",[]]Bn[\"{{{AI@l{BDKj}}CHlB@Cb}{{LFd{CJjBEKn}}}}\",[]]B`[\"{{{AI@l{OIn}}B@CdCLf}CJl}\",[]]Bn[\"{{{AI@l{B@Gj}}CHlB@Cd}{{LFd{CJlBELd}}}}\",[]]Ba[\"{{{AI@l{AJAn}}B@CdCLf}CJl}\",[]]Bn[\"{{{AI@l{BDKl}}CHlB@Cd}{{LFd{CJlBELd}}}}\",[]]Bn[\"{{{AI@l{BGHj}}CHlB@C`}{{LFd{CJlBELd}}}}\",[]]Aj[\"{{}{{OIf{c}}}{}}\",[\"T\"]]0A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]Bn[\"{{{AI@l{CB`B@Gd}}LAn}{{AI@l{CB`B@Gd}}}}\",[]]Bn[\"{{{AI@l{CB`B@Gh}}LAn}{{AI@l{CB`B@Gh}}}}\",[]]Aj[\"{{{AI@l{BB@j}}}CMh}\",[]]0Aj[\"{{{AI@l{BBEn}}}CMh}\",[]]Bk[\"{{{AI@l{AILn}}LF`}{{LFd{AHF`AILd}}}}\",[]]Cb[\"{{{AI@l{BCB`}}{AI@l{AIOl}}CHn}{{Ml{AODh}}}}\",[]]Ak[\"{{{AI@l{ABKh}}}B@Cb}\",[]]Ak[\"{{{AI@l{ABKj}}}B@Cd}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Bf[\"{{{AI@l{CB`AILn}}}{{LEj{OGf}}}}\",[]]Ce[\"{{{AI@l{CB`BCCh}}{Ml{AOC`}}}{{AI@l{CB`BCCh}}}}\",[]]Ce[\"{{{AI@l{CB`BCCj}}{Ml{AOCb}}}{{AI@l{CB`BCCj}}}}\",[]]43A`[\"{ABO`LEj}\",[]]A`[\"{ABObLEj}\",[]]A`[\"{ABOdLEj}\",[]]Ai[\"{{{AI@l{OJj}}}LAn}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]Bo[\"{{{AI@l{CB`BGMn}}ABJl}{{AI@l{CB`BGMn}}}}\",[]]Bo[\"{{{AI@l{CB`BGNh}}ABJl}{{AI@l{CB`BGNh}}}}\",[]]Bj[\"{{{AI@l{AJ@j}}C@j}{{LFd{HIjB@B`}}}}\",[]]Bm[\"{{{AI@l{CB`AJ@l}}C@j}{{LFd{HIjB@Bh}}}}\",[]]>=A`[\"{JIlAODf}\",[]]A`[\"{AO@jJIl}\",[]]Bi[\"{{{Ml{B@A`}}JJ`}{{LFd{BDKfBFGh}}}}\",[]]Bi[\"{{{Ml{B@G`}}JJ`}{{LFd{BDKjBFGh}}}}\",[]]Bi[\"{{{Ml{B@A`}}JJ`}{{LFd{BDKhBFGj}}}}\",[]]Bi[\"{{{Ml{B@G`}}JJ`}{{LFd{BDKlBFGj}}}}\",[]]Co[\"{{{AI@l{CB`AJ@j}}{AI@l{CB`{KOj{JE`}}}}}{{LFd{OKfAJBf}}}}\",[]]Co[\"{{{AI@l{CB`AJ@l}}{AI@l{CB`{KOj{JE`}}}}}{{LFd{OKhAJBh}}}}\",[]]Aj[\"{{{AI@l{AILn}}}CMh}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]A`[\"{AHEbCMh}\",[]]E`[\"{{{AI@l{CB`BBBb}}c}{{AI@l{CB`BBBb}}}{{A`{}{{LEl{{Nd{BFN`}}}}}}CLbCLh}}\",[\"F\"]]E`[\"{{{AI@l{CB`BBBd}}c}{{AI@l{CB`BBBd}}}{{A`{}{{LEl{{Nd{BFNb}}}}}}CLbCLh}}\",[\"F\"]]Bj[\"{{{AI@l{BB@j}}}{{A@b{{Nd{AHI`}}}}}}\",[]]n[\"{OjCHn}\",[]]Aa[\"{AODfAHFl}\",[]]76Aa[\"{AJ@dABKh}\",[]]Aa[\"{AJ@fABKj}\",[]]Cf[\"{{{AI@l{CB`BEGb}}{LEj{ABJl}}}{{AI@l{CB`BEGb}}}}\",[]]Cf[\"{{{AI@l{CB`BEGd}}{LEj{ABJl}}}{{AI@l{CB`BEGd}}}}\",[]]9;:An[\"{{{AI@l{AODh}}ABJl}HIj}\",[]]B`[\"{{{AI@l{CB`AIM`}}CHj}HIj}\",[]]An[\"{{{AI@l{CB`Mn}}CHj}HIj}\",[]]Ao[\"{{{AI@l{CB`JEj}}CHj}HIj}\",[]]Ao[\"{{{AI@l{CB`OHb}}CHj}HIj}\",[]]B`[\"{{{AI@l{CB`AIMb}}CHj}HIj}\",[]]An[\"{{{AI@l{CB`N`}}CHj}HIj}\",[]]Ao[\"{{{AI@l{CB`JEl}}CHj}HIj}\",[]]Ao[\"{{{AI@l{CB`OHd}}CHj}HIj}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]10Bh[\"{{{AI@l{AILl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@l{OIl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@l{AJAl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@l{OIn}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@l{AJAn}}}{{LEj{{Nd{Mj}}}}}}\",[]]Dm[\"{{{AI@l{CB`AIOn}}{AI@l{CB`OFd}}{AI@l{CB`OId}}}{{CKn{{LFd{HIjAHHj}}}}}}\",[]]Db[\"{{{A@b{{AI@l{CB`AJ@j}}}}{AI@l{CB`OFd}}}{{CKn{{LFf{HIj}}}}}}\",[]]Db[\"{{{AI@l{CB`AILn}}OGfB@Eb{AI@l{CB`{A@h{Md}}}}}{{LEj{ABOb}}}}\",[]]Cf[\"{{{AI@l{CB`BCCh}}{AI@l{CHh}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{AI@l{CHh}}}{{AI@l{CB`BCCj}}}}\",[]]Ak[\"{{{AI@l{AILl}}}B@Fd}\",[]]Bj[\"{{{AI@l{AILl}}JE`}{{LFd{HIjBEDb}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CMh}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CMh}{{AI@l{CB`BCCj}}}}\",[]]Ai[\"{{{AI@l{OG`}}}LAn}\",[]]Ai[\"{{{AI@l{OGb}}}LAn}\",[]]De[\"{{{Ml{B@Af}}CHl{AI@l{CHh}}{AI@l{BF@j}}}{{LFd{{Nd{OIl}}B@Bl}}}}\",[]]Bo[\"{{{Ml{B@Gf}}CHl{AI@l{BF@j}}}{{Nd{OIl}}}}\",[]]De[\"{{{Ml{BDKf}}CHl{AI@l{CHh}}{AI@l{BF@j}}}{{LFd{{Nd{OIl}}B@Bl}}}}\",[]]Bo[\"{{{Ml{BDKj}}CHl{AI@l{BF@j}}}{{Nd{OIl}}}}\",[]]De[\"{{{Ml{B@Aj}}CHl{AI@l{CHh}}{AI@l{BF@j}}}{{LFd{{Nd{OIn}}B@Bn}}}}\",[]]Bo[\"{{{Ml{B@Gj}}CHl{AI@l{BF@j}}}{{Nd{OIn}}}}\",[]]De[\"{{{Ml{BDKh}}CHl{AI@l{CHh}}{AI@l{BF@j}}}{{LFd{{Nd{OIn}}B@Bn}}}}\",[]]Bo[\"{{{Ml{BDKl}}CHl{AI@l{BF@j}}}{{Nd{OIn}}}}\",[]]De[\"{{{Ml{BGHh}}CHl{AI@l{CHh}}{AI@l{BF@j}}}{{LFd{{Nd{OIn}}B@Bj}}}}\",[]]Bo[\"{{{Ml{BGHj}}CHl{AI@l{BF@j}}}{{Nd{OIn}}}}\",[]]Bm[\"{LAn{{LFd{{LAl{LAn{LEj{LAn}}}}BEOn}}}}\",[]]0Cc[\"{{{AI@l{BECf}}{AI@l{{KOj{Md}}}}}{{Nd{OEh}}}}\",[]]Cc[\"{{{AI@l{BECh}}{AI@l{{KOj{Md}}}}}{{Nd{OEj}}}}\",[]]Bm[\"{{AJA`BB@lOj}{{LFh{{LAl{ABJnAILj}}}}}}\",[]]Bd[\"{{{AI@l{AILn}}}{{AI@l{OIn}}}}\",[]]A`[\"{AO@jJIl}\",[]]Cb[\"{{{AI@l{AOC`}}CLdAJA`B@CbOGf}{{LEj{AIOh}}}}\",[]]Cb[\"{{{AI@l{BCBj}}CLdAJA`B@CbOGf}{{LEj{AIOh}}}}\",[]]Cb[\"{{{AI@l{AOCb}}CLfAJA`B@CdOGf}{{LEj{AIOj}}}}\",[]]Cb[\"{{{AI@l{BCBl}}CLfAJA`B@CdOGf}{{LEj{AIOj}}}}\",[]]B`[\"{{{AI@l{CB`AILn}}OGf}HIj}\",[]]Bo[\"{{{AI@l{CB`AILh}}}{{LFd{{Nd{Mj}}BC@n}}}}\",[]]Bh[\"{{{AI@l{AILl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@l{OIl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@l{AJAl}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bg[\"{{{AI@l{OIn}}}{{LEj{{Nd{Mj}}}}}}\",[]]Bh[\"{{{AI@l{AJAn}}}{{LEj{{Nd{Mj}}}}}}\",[]]o[\"{AIMhMd}\",[]]o[\"{AIMjMd}\",[]]Ai[\"{{{AI@l{CHh}}}CMh}\",[]]Aj[\"{{{AI@l{AIM`}}}CHn}\",[]]Ah[\"{{{AI@l{Mn}}}CHn}\",[]]Bn[\"{{{AI@l{CB`AHDj}}CHn}{{AI@l{CB`AHDj}}}}\",[]]Ai[\"{{{AI@l{JEj}}}CHn}\",[]]Bn[\"{{{AI@l{CB`AOAh}}CHn}{{AI@l{CB`AOAh}}}}\",[]]Ai[\"{{{AI@l{OHb}}}CHn}\",[]]Bn[\"{{{AI@l{CB`BAFd}}CHn}{{AI@l{CB`BAFd}}}}\",[]]Aj[\"{{{AI@l{AIMb}}}CHn}\",[]]Ah[\"{{{AI@l{N`}}}CHn}\",[]]Bn[\"{{{AI@l{CB`AHDl}}CHn}{{AI@l{CB`AHDl}}}}\",[]]Ai[\"{{{AI@l{JEl}}}CHn}\",[]]Bn[\"{{{AI@l{CB`AOAj}}CHn}{{AI@l{CB`AOAj}}}}\",[]]Ai[\"{{{AI@l{OHd}}}CHn}\",[]]Bn[\"{{{AI@l{CB`BAFf}}CHn}{{AI@l{CB`BAFf}}}}\",[]]Aa[\"{AIOnBF@l}\",[]]Ai[\"{{{AI@l{OIl}}}CMh}\",[]]Aj[\"{{{AI@l{AJAl}}}CMh}\",[]]Aj[\"{{{AI@l{AILn}}}CMh}\",[]]Ai[\"{{{AI@l{OIn}}}CMh}\",[]]Aj[\"{{{AI@l{AJAn}}}CMh}\",[]]Co[\"{{{AI@l{OIl}}B@Cb{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}CMh}\",[]]D`[\"{{{AI@l{AJAl}}B@Cb{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}CMh}\",[]]Co[\"{{{AI@l{OIn}}B@Cd{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}CMh}\",[]]D`[\"{{{AI@l{AJAn}}B@Cd{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}CMh}\",[]]Bn[\"{{{AI@l{CB`BEI`}}CHj}{{AI@l{CB`BEI`}}}}\",[]]Bn[\"{{{AI@l{CB`BEIb}}CHj}{{AI@l{CB`BEIb}}}}\",[]]Cc[\"{{{AI@l{CB`OIl}}}{{LEj{{OGh{{Nd{AHGf}}}}}}}}\",[]]Cd[\"{{{AI@l{CB`AJAl}}}{{LEj{{OGh{{Nd{AHGf}}}}}}}}\",[]]Cc[\"{{{AI@l{CB`OIn}}}{{LEj{{OGj{{Nd{AHGh}}}}}}}}\",[]]Cd[\"{{{AI@l{CB`AJAn}}}{{LEj{{OGj{{Nd{AHGh}}}}}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]De[\"{{{AI@l{CB`BCCh}}c{AI@l{CHh}}}{{AI@l{CB`BCCh}}}{{JDn{CKd}}}}\",[\"\"]]De[\"{{{AI@l{CB`BCCj}}c{AI@l{CHh}}}{{AI@l{CB`BCCj}}}{{JDn{CKd}}}}\",[\"\"]]Ci[\"{{{AI@l{CB`OIl}}{AI@l{{KOj{Md}}}}}{{LFd{CMhJFn}}}}\",[]]Cj[\"{{{AI@l{CB`AJAl}}{AI@l{{KOj{Md}}}}}{{LFd{CMhJFn}}}}\",[]]Cj[\"{{{AI@l{CB`OIn}}{AI@l{{KOj{Md}}}}}{{LFd{CMhBBEl}}}}\",[]]Ck[\"{{{AI@l{CB`AJAn}}{AI@l{{KOj{Md}}}}}{{LFd{CMhBBEl}}}}\",[]]n[\"{OjCMh}\",[]]Bn[\"{{{AI@l{CB`BCCh}}LH`}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}LHb}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHEbLEj}\",[]]Ca[\"{{{AI@l{CB`AIOn}}}{{LFd{{LEj{LH`}}AJ@b}}}}\",[]]Ca[\"{{{AI@l{CB`BF@l}}}{{LFd{{LEj{LH`}}AJ@b}}}}\",[]]Ca[\"{{{AI@l{CB`AJ@`}}}{{LFd{{LEj{LHb}}B@Bh}}}}\",[]]Ak[\"{{{AI@l{AILh}}}AJA`}\",[]]Ak[\"{{{AI@l{AILl}}}AJA`}\",[]]Ak[\"{{{AI@l{ABKh}}}AJA`}\",[]]Bg[\"{{{AI@l{CKf}}}{{LFd{AJA`AIKn}}}}\",[]]Ak[\"{{{AI@l{ABKj}}}AJA`}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]B`[\"{{LH`c}LH`{{CJh{LH`}}}}\",[\"\"]]B`[\"{{LEnc}LEn{{CJh{LEn}}}}\",[\"\"]]B`[\"{{LHbc}LHb{{CJh{LHb}}}}\",[\"\"]]B`[\"{{LF`c}LF`{{CJh{LF`}}}}\",[\"\"]]20Ae[\"{{ABJlCHn}ADf}\",[]]Bn[\"{{{AI@l{CB`BCCh}}ADd}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}ADd}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{JIlABJl}\",[]]B`[\"{{}{{AI@l{{KOj{BEOl}}}}}}\",[]]Ao[\"{{AI@l{A@K`{CB`Md}}}HIj}\",[]]000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000Am[\"{{}{{LEj{{Ml{OIj}}}}}}\",[]]Bn[\"{{{AI@l{CB`BBBb}}CMh}{{AI@l{CB`BBBb}}}}\",[]]Bn[\"{{{AI@l{CB`BBBd}}CMh}{{AI@l{CB`BBBd}}}}\",[]]Aj[\"{{{AI@l{AHGf}}}CHn}\",[]]Aj[\"{{{AI@l{AHGh}}}CHn}\",[]]A`[\"{ABMjLEj}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]10Bf[\"{{{AI@l{CHh}}}{{LFh{CHnBAFh}}}}\",[]]Bm[\"{{{AI@l{ABK`}}{Nd{BB@j}}}{{LFf{HIj}}}}\",[]]Ai[\"{AILj{{LFh{HIj}}}}\",[]]Ci[\"{{{AI@l{OIh}}BB@lBAGb{AI@l{Oj}}}{{LAl{AJA`ABJn}}}}\",[]]Cb[\"{{{AI@l{CB`AILn}}LF`AIOf}{{LFd{AIOfBEK`}}}}\",[]]Am[\"{{{AI@l{AILl}}CHn}HIj}\",[]]B`[\"{{{AI@l{CB`AILn}}CHn}HIj}\",[]]A`[\"{BBEdCMh}\",[]]B`[\"{{{AI@l{CB`JIl}}BBEf}HIj}\",[]]Aa[\"{AODfAHFl}\",[]]Bm[\"{{{AI@l{OIj}}AHIb}{{LFf{{Nd{BB@j}}}}}}\",[]]Bn[\"{{{AI@l{B@Hb}}AHIb}{{LFf{{Nd{BB@j}}}}}}\",[]]Bn[\"{{{AI@l{AOCl}}AHIb}{{LFf{{Nd{BB@j}}}}}}\",[]]Ci[\"{{{AI@l{CB`OIl}}{AI@l{CB`{A@h{Md}}}}}{{LEj{CJj}}}}\",[]]Cj[\"{{{AI@l{CB`AJAl}}{AI@l{CB`{A@h{Md}}}}}{{LEj{CJj}}}}\",[]]Ci[\"{{{AI@l{CB`OIn}}{AI@l{CB`{A@h{Md}}}}}{{LEj{CJl}}}}\",[]]Cj[\"{{{AI@l{CB`AJAn}}{AI@l{CB`{A@h{Md}}}}}{{LEj{CJl}}}}\",[]]Cb[\"{{{AI@l{ABNh}}HIhAJAfABJl}{{LFd{HIjBCCb}}}}\",[]]Cb[\"{{{AI@l{BABl}}HIhAJAfABJl}{{LFd{HIjBCCb}}}}\",[]]Cb[\"{{{AI@l{B@Ed}}HIhAJAfABJl}{{LFd{HIjBCCb}}}}\",[]]Cb[\"{{{AI@l{ABNj}}HIhAJAfABJl}{{LFd{HIjBCCd}}}}\",[]]Cb[\"{{{AI@l{BABn}}HIhAJAfABJl}{{LFd{HIjBCCd}}}}\",[]]Cb[\"{{{AI@l{B@Ef}}HIhAJAfABJl}{{LFd{HIjBCCd}}}}\",[]]Al[\"{{}{{LFd{B@AbBFGj}}}}\",[]]Bm[\"{{{AI@l{AILl}}LEn}{{LEj{{Nd{AIM`}}}}}}\",[]]Bo[\"{{{AI@l{AILn}}LF`}{{LEj{{AI@l{AIMb}}}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Aj[\"{{{AI@l{AILl}}}HIj}\",[]]Am[\"{{{AI@l{CB`AILn}}}HIj}\",[]]Be[\"{{{AI@l{AIKd}}}{{LFd{OjJGb}}}}\",[]]Bg[\"{{{AI@l{AIKd}}}{{LFd{AHEbJGb}}}}\",[]]Be[\"{{{AI@l{AIKd}}}{{LFd{OlJGb}}}}\",[]]Be[\"{{{AI@l{AIKd}}}{{LFd{OnJGb}}}}\",[]]Be[\"{{{AI@l{AIKd}}}{{LFd{NhJGb}}}}\",[]]Bg[\"{{{AI@l{AIKd}}}{{LFd{ABInJGb}}}}\",[]]Ak[\"{{{AI@l{BBEn}}}B@Eb}\",[]]Cf[\"{{{AI@l{CB`BCCh}}{LEj{AOBb}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{LEj{AOBd}}}{{AI@l{CB`BCCj}}}}\",[]]<;Aj[\"{{{AI@l{ABK`}}}LAn}\",[]]Aj[\"{{{AI@l{ABKb}}}LAn}\",[]]Bc[\"{{{AI@l{AILl}}AJA`AIOd}ABLn}\",[]]Co[\"{{{AI@l{CB`AILn}}AHF`AIOfOGf}{{LFd{{LAl{LF`CMh}}AHGl}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CHl}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CHl}{{AI@l{CB`BCCj}}}}\",[]]Ai[\"{AILj{{LFh{HIj}}}}\",[]]Bg[\"{{{AI@l{BBEn}}AJBd}{{LFf{LAn}}}}\",[]]A`[\"{AHEbCHn}\",[]]10Ai[\"{{{AI@l{Oj}}}BCCf}\",[]]Ce[\"{{{AI@l{CB`B@Gd}}{Ml{BCCh}}}{{AI@l{CB`B@Gd}}}}\",[]]Ce[\"{{{AI@l{CB`B@Ad}}{Ml{BCCh}}}{{AI@l{CB`B@Ad}}}}\",[]]Ce[\"{{{AI@l{CB`B@Gh}}{Ml{BCCj}}}{{AI@l{CB`B@Gh}}}}\",[]]Ce[\"{{{AI@l{CB`B@Ah}}{Ml{BCCj}}}{{AI@l{CB`B@Ah}}}}\",[]]Aa[\"{B@GdBFOb}\",[]]Aa[\"{B@GhBFOd}\",[]]Bj[\"{{{A@h{BB@l}}BAGb}{{LFd{B@GdJGd}}}}\",[]]Bj[\"{{{A@h{BB@l}}BAGb}{{LFd{B@GhJGf}}}}\",[]]Co[\"{{{AI@l{CB`AJ@j}}{AI@l{CB`{KOj{JE`}}}}}{{LFd{HIjAJBf}}}}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]A`[\"{BECbCHn}\",[]]A`[\"{BECdCHn}\",[]]Bc[\"{{{AI@l{AILl}}}{{LEj{LAn}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]10Aj[\"{{{AI@l{AHEh}}}LAn}\",[]]Be[\"{{{AI@l{ABK`}}{LEj{B@Gd}}}HIj}\",[]]Bn[\"{{{AI@l{CB`ABKb}}{LEj{{Ml{B@Gh}}}}}HIj}\",[]]Bd[\"{{{AI@l{BEOl}}}{{LEj{BAG`}}}}\",[]]o[\"{{}AI@l}\",[]]n[\"{{}OFb}\",[]]0000Bn[\"{{{AI@l{CB`BCCh}}LAn}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}LAn}{{AI@l{CB`BCCj}}}}\",[]]o[\"{CHnLH`}\",[]]o[\"{CHnLHb}\",[]]A`[\"{BACnCHn}\",[]]Bo[\"{{{AI@l{CB`BBBb}}ABJl}{{AI@l{CB`BBBb}}}}\",[]]Bo[\"{{{AI@l{CB`BBBd}}ABJl}{{AI@l{CB`BBBd}}}}\",[]]Ae[\"{{LAnCHn}BABl}\",[]]Ae[\"{{LAnCHn}BABn}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]6Bg[\"{{{AI@l{AILn}}LF`}{{LEj{AIOf}}}}\",[]]An[\"{{{AI@l{AILl}}JE`}B@Fn}\",[]]Am[\"{{{AI@l{AILl}}LH`}HIj}\",[]]B`[\"{{{AI@l{CB`AILn}}LHb}HIj}\",[]]Ce[\"{{{AI@l{CB`BBBb}}{A@h{CHl}}}{{AI@l{CB`BBBb}}}}\",[]]Ce[\"{{{AI@l{CB`BBBd}}{A@h{CHl}}}{{AI@l{CB`BBBd}}}}\",[]]Fg[\"{{{AI@l{BGC`}}{AI@l{BB@l}}{AI@l{{KOj{BB@l}}}}{AI@l{AJ@n}}{AI@l{{KOj{Md}}}}ABOj}{{LFd{BEJjJFh}}}}\",[]]Af[\"{{BCBjOGf}BCBj}\",[]]Af[\"{{BCBlOGf}BCBl}\",[]]?Al[\"{B@Hd{{LFd{CLdJG`}}}}\",[]]Ak[\"{B@Hd{{LFd{NjJG`}}}}\",[]]Am[\"{B@Hd{{LFd{ABMnJG`}}}}\",[]]Ai[\"{B@Hd{{LFj{CLf}}}}\",[]]Ah[\"{B@Hd{{LFj{Nl}}}}\",[]]Aj[\"{B@Hd{{LFj{ABN`}}}}\",[]]o[\"{OFbOFb}\",[]]Bo[\"{{{AI@l{CB`BEI`}}ABJl}{{AI@l{CB`BEI`}}}}\",[]]Bo[\"{{{AI@l{CB`BEIb}}ABJl}{{AI@l{CB`BEIb}}}}\",[]]Bb[\"{{{AI@l{OIl}}}{{LEj{CMh}}}}\",[]]Bc[\"{{{AI@l{AJAl}}}{{LEj{CMh}}}}\",[]]Bb[\"{{{AI@l{OIn}}}{{LEj{CMh}}}}\",[]]Bc[\"{{{AI@l{AJAn}}}{{LEj{CMh}}}}\",[]]Bg[\"{{{AI@l{AILl}}}{{LFd{HIjBC@n}}}}\",[]]Cf[\"{{{AI@l{CB`BCCh}}{LEj{ABJl}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{LEj{ABJl}}}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]Bl[\"{{{AI@l{CB`AIM`}}OGfOGfCMhCMhCHn}HIj}\",[]]Bj[\"{{{AI@l{CB`Mn}}OGfOGfCMhCMhCHn}HIj}\",[]]Bk[\"{{{AI@l{CB`JEj}}OGfOGfCMhCMhCHn}HIj}\",[]]Bk[\"{{{AI@l{CB`OHb}}OGfOGfCMhCMhCHn}HIj}\",[]]Bl[\"{{{AI@l{CB`AIMb}}OGfOGfCMhCMhCHn}HIj}\",[]]Bj[\"{{{AI@l{CB`N`}}OGfOGfCMhCMhCHn}HIj}\",[]]Bk[\"{{{AI@l{CB`JEl}}OGfOGfCMhCMhCHn}HIj}\",[]]Bk[\"{{{AI@l{CB`OHd}}OGfOGfCMhCMhCHn}HIj}\",[]]A`[\"{BACnCHn}\",[]]Ak[\"{{{AI@l{OJj}}Nl}CHn}\",[]]Bi[\"{{{AI@l{CB`AJ@h}}CHn}{{LFh{HIj}}}}\",[]]<;A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]10Cf[\"{{{AI@l{CB`BCCh}}{LEj{BEGb}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{LEj{BEGd}}}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHHdCHn}\",[]]A`[\"{AHHfCHn}\",[]]Bh[\"{{{AI@l{CB`AIKd}}}{{LFd{OjJGb}}}}\",[]]Bj[\"{{{AI@l{CB`AIKd}}}{{LFd{AHEbJGb}}}}\",[]]Bh[\"{{{AI@l{CB`AIKd}}}{{LFd{OlJGb}}}}\",[]]Bh[\"{{{AI@l{CB`AIKd}}}{{LFd{OnJGb}}}}\",[]]Bh[\"{{{AI@l{CB`AIKd}}}{{LFd{NhJGb}}}}\",[]]Bj[\"{{{AI@l{CB`AIKd}}}{{LFd{ABInJGb}}}}\",[]]Cc[\"{{{Ml{AILj}}CHnCMh}{{LFh{{LAl{BBEfBBEf}}}}}}\",[]]Bn[\"{{{AI@l{CB`B@Gd}}CHn}{{AI@l{CB`B@Gd}}}}\",[]]Bn[\"{{{AI@l{CB`B@Gh}}CHn}{{AI@l{CB`B@Gh}}}}\",[]]Aj[\"{{{AI@l{AILl}}}CMh}\",[]]Ak[\"{{{AI@l{BB@j}}}B@Eb}\",[]]0Ci[\"{{{AI@l{CB`BBBb}}CHj}{{LFd{{AI@l{CB`BBBb}}AO@n}}}}\",[]]Ci[\"{{{AI@l{CB`BBBd}}CHj}{{LFd{{AI@l{CB`BBBd}}AOA`}}}}\",[]]A`[\"{AHEbCHj}\",[]]Cf[\"{{{AI@l{CB`BCCh}}{LEj{BEI`}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{LEj{BEIb}}}{{AI@l{CB`BCCj}}}}\",[]]Bg[\"{{{AI@l{CB`AILn}}}{{LEj{BACl}}}}\",[]]Cf[\"{{{AI@l{CB`B@Gd}}{LEj{B@Gl}}}{{AI@l{CB`B@Gd}}}}\",[]]Cf[\"{{{AI@l{CB`B@Gh}}{LEj{B@Gl}}}{{AI@l{CB`B@Gh}}}}\",[]]Cf[\"{{{AI@l{CB`B@Gd}}{LEj{B@Gn}}}{{AI@l{CB`B@Gd}}}}\",[]]Cf[\"{{{AI@l{CB`B@Gh}}{LEj{B@Gn}}}{{AI@l{CB`B@Gh}}}}\",[]]Cc[\"{{}{{LFd{{LAl{{A@h{BB@l}}BAGb}}{Nd{JFf}}}}}}\",[]]Bn[\"{{{AI@l{CB`BEGb}}LH`}{{AI@l{CB`BEGb}}}}\",[]]Bn[\"{{{AI@l{CB`BEGd}}LHb}{{AI@l{CB`BEGd}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Bo[\"{{{AI@l{CB`B@Gd}}ABJl}{{AI@l{CB`B@Gd}}}}\",[]]Bo[\"{{{AI@l{CB`B@Gh}}ABJl}{{AI@l{CB`B@Gh}}}}\",[]]Ci[\"{{{AI@l{CKf}}{LEj{ABJl}}}{{LFd{{LEj{ABJl}}AIKn}}}}\",[]]Bj[\"{{{AI@l{BBEn}}AJBdLAn}{{LFf{HIj}}}}\",[]]054Bm[\"{{{AI@l{OIl}}}{{LFd{{LEj{BF@j}}JFn}}}}\",[]]Bn[\"{{{AI@l{AJAl}}}{{LFd{{LEj{BF@j}}JFn}}}}\",[]]Bn[\"{{{AI@l{OIn}}}{{LFd{{LEj{BF@j}}BBEl}}}}\",[]]Bo[\"{{{AI@l{AJAn}}}{{LFd{{LEj{BF@j}}BBEl}}}}\",[]]Aj[\"{{{AI@l{AHGf}}}CHn}\",[]]Aj[\"{{{AI@l{AHGh}}}CHn}\",[]]Aj[\"{{{AI@l{ABKb}}}CHn}\",[]]Bf[\"{{{AI@l{AHF`}}{AI@l{AHF`}}}CMh}\",[]]Am[\"{{{AI@l{CB`AILn}}}HIj}\",[]]Bn[\"{{{AI@l{CB`BAFd}}ADd}{{AI@l{CB`BAFd}}}}\",[]]Bn[\"{{{AI@l{CB`BAFf}}ADd}{{AI@l{CB`BAFf}}}}\",[]]Ak[\"{{{AI@l{AHI`}}}B@Eb}\",[]]0Bd[\"{{{AI@l{AILl}}}{{ABMf{JH`}}}}\",[]]Cb[\"{{{AI@l{AILn}}LF`}{{LFd{{LEj{AJA`}}AILd}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]Bn[\"{{{AI@l{CB`BCCh}}LH`}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}LHb}{{AI@l{CB`BCCj}}}}\",[]]A`[\"{AHEbLEj}\",[]]Bo[\"{{{AI@l{AHEb}}{AI@l{CHh}}}{{LFf{BCCf}}}}\",[]]Em[\"{{{AI@l{AILl}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjBGJd}}}}\",[]]El[\"{{{AI@l{OIl}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjBGJd}}}}\",[]]Em[\"{{{AI@l{AJAl}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjBGJd}}}}\",[]]El[\"{{{AI@l{OIn}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjBGJf}}}}\",[]]Em[\"{{{AI@l{AJAn}}{AI@l{CB`{KOj{Md}}}}{AI@l{{KOj{Md}}}}{AI@l{{KOj{Md}}}}}{{LFd{HIjBGJf}}}}\",[]]Am[\"{{{AI@l{CB`AHEf}}}HIj}\",[]]0Al[\"{{{AI@l{AILn}}Nl}CHn}\",[]]Bl[\"{{{AI@l{AILl}}}{{ABMh{{LEj{AJA`}}}}}}\",[]]Bg[\"{{{AI@l{CKf}}}{{LFd{BDGlAIKn}}}}\",[]]>=E`[\"{{{AI@l{BGC`}}{AI@l{{KOj{Md}}}}{AI@l{BB@l}}{AI@l{BFNd}}}{{LFd{BGEfJFh}}}}\",[]]0Bf[\"{{{Ml{BAGj}}}{{LFd{B@AdBFHd}}}}\",[]]Bf[\"{{{Ml{BAGj}}}{{LFd{B@AhBFHd}}}}\",[]]Bn[\"{{{AI@l{CB`BEGb}}LH`}{{AI@l{CB`BEGb}}}}\",[]]Bn[\"{{{AI@l{CB`BEGd}}LHb}{{AI@l{CB`BEGd}}}}\",[]]o[\"{OFbOFb}\",[]]0000Aj[\"{{{AI@l{AILn}}}CMh}\",[]]0Ci[\"{{{AI@l{CKf}}{LEj{ABJl}}}{{LFd{{LEj{ABJl}}AIKn}}}}\",[]]Cc[\"{{{AI@l{CB`Oj}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Ce[\"{{{AI@l{CB`AHEb}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cc[\"{{{AI@l{CB`Ol}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cc[\"{{{AI@l{CB`On}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cc[\"{{{AI@l{CB`Nh}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Ce[\"{{{AI@l{CB`ABIn}}{AI@l{AIKd}}}{{LFd{HIjJGb}}}}\",[]]Bo[\"{{{AI@l{CB`B@Gd}}BFOb}{{AI@l{CB`B@Gd}}}}\",[]]Bo[\"{{{AI@l{CB`B@Gh}}BFOd}{{AI@l{CB`B@Gh}}}}\",[]]Aj[\"{{{AI@l{BBBb}}}CHn}\",[]]Aj[\"{{{AI@l{BBBd}}}CHn}\",[]]Cc[\"{{{AI@l{CB`B@Ad}}{Ml{A`}}}{{AI@l{CB`B@Ad}}}}\",[]]Cc[\"{{{AI@l{CB`B@Ah}}{Ml{A`}}}{{AI@l{CB`B@Ah}}}}\",[]]Cj[\"{{BBBb{LEj{B@Gd}}{Nd{BB@j}}{Ml{OIj}}}{{LFf{ABK`}}}}\",[]]Aj[\"{{{AI@l{ABKh}}}CMh}\",[]]Aj[\"{{{AI@l{ABKj}}}CMh}\",[]]Ca[\"{{{AI@l{CB`AILn}}OGfAINj}{{LFd{HIjBGBh}}}}\",[]]Bd[\"{{{AI@l{BGC`}}}{{A@h{BCBn}}}}\",[]]Bj[\"{{{AI@l{AILl}}AJA`}{{LFd{HIjJGj}}}}\",[]]Bm[\"{{{AI@l{CB`AILn}}AJA`}{{LFd{HIjJGj}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}LAn}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}LAn}{{AI@l{CB`BCCj}}}}\",[]]Bl[\"{{}{{LFd{{LAl{BB@lBEIl}}{Nd{JFf}}}}}}\",[]]Ai[\"{AILj{{LFh{HIj}}}}\",[]]A`[\"{AIMhCHn}\",[]]A`[\"{AIMjCHn}\",[]]An[\"{{{AI@l{ABK`}}B@Ad}HIj}\",[]]D`[\"{{{AI@l{CB`AILn}}LF`{LEj{ABJl}}}{{LFd{{LEj{ABJl}}AILd}}}}\",[]]o[\"{{}AI@l}\",[]]Aj[\"{{{AI@l{AILl}}}LAn}\",[]]Bn[\"{{{AI@l{CB`B@Gd}}CHn}{{AI@l{CB`B@Gd}}}}\",[]]Bn[\"{{{AI@l{CB`B@Gh}}CHn}{{AI@l{CB`B@Gh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}LH`}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}LHb}{{AI@l{CB`BCCj}}}}\",[]]:Bb[\"{{{AI@l{CB`AILn}}NlLHb}HIj}\",[]]Ak[\"{{}{{LFd{B@AdJGd}}}}\",[]]Ak[\"{{}{{LFd{B@AhJGf}}}}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CMh}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CMh}{{AI@l{CB`BCCj}}}}\",[]]65Cf[\"{{{AI@l{CB`Oj}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Ch[\"{{{AI@l{CB`AHEb}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cf[\"{{{AI@l{CB`Ol}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cf[\"{{{AI@l{CB`On}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Cf[\"{{{AI@l{CB`Nh}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Ch[\"{{{AI@l{CB`ABIn}}{AI@l{CB`AIKd}}}{{LFd{HIjJGb}}}}\",[]]Bi[\"{{{AI@l{CB`AHF`}}{AI@l{AHF`}}}CMh}\",[]]n[\"{{}HIj}\",[]]Ce[\"{{{AI@l{CB`BCCh}}{LEj{LAn}}}{{AI@l{CB`BCCh}}}}\",[]]Ce[\"{{{AI@l{CB`BCCj}}{LEj{LAn}}}{{AI@l{CB`BCCj}}}}\",[]]Bn[\"{{{AI@l{AILl}}}{{LFd{{A@h{AJA`}}JGj}}}}\",[]]Cd[\"{{{AI@l{CB`AILn}}OGf}{{LFd{{A@h{AJA`}}JGj}}}}\",[]]Am[\"{{{AI@l{CB`AIM`}}}HIj}\",[]]Al[\"{{{AI@l{CB`JEj}}}HIj}\",[]]Am[\"{{{AI@l{CB`AIMb}}}HIj}\",[]]Al[\"{{{AI@l{CB`JEl}}}HIj}\",[]]Ai[\"{AILj{{LFh{HIj}}}}\",[]]Bj[\"{{{AI@l{AILl}}AJA`}{{LFd{HIjJGj}}}}\",[]]Bm[\"{{{AI@l{CB`AILn}}AJA`}{{LFd{HIjJGj}}}}\",[]]D`[\"{{{AI@l{CB`AILn}}LF`{LEj{ABJl}}}{{LFd{{LEj{ABJl}}AILd}}}}\",[]]Ce[\"{{{AI@l{CB`BCCh}}{Ml{BEBn}}}{{AI@l{CB`BCCh}}}}\",[]]Ce[\"{{{AI@l{CB`BCCj}}{Ml{BEC`}}}{{AI@l{CB`BCCj}}}}\",[]]Cf[\"{{{AI@l{CB`BCCh}}{LEj{ABJl}}}{{AI@l{CB`BCCh}}}}\",[]]Cf[\"{{{AI@l{CB`BCCj}}{LEj{ABJl}}}{{AI@l{CB`BCCj}}}}\",[]]Aa[\"{BADbAINh}\",[]]Aa[\"{BADdAINh}\",[]]9Bn[\"{{{AI@l{CB`BCCh}}CMh}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CMh}{{AI@l{CB`BCCj}}}}\",[]]Am[\"{{{AI@l{AILl}}LH`}HIj}\",[]]o[\"{OFbOFb}\",[]]Bn[\"{{{AI@l{CB`BCCh}}CHl}{{AI@l{CB`BCCh}}}}\",[]]Bn[\"{{{AI@l{CB`BCCj}}CHl}{{AI@l{CB`BCCj}}}}\",[]]3109854Bn[\"{{{AI@l{AILl}}}{{LFd{{A@h{AJA`}}JGj}}}}\",[]]Bn[\"{{{AI@l{AILn}}}{{LFd{{A@h{AJA`}}JGj}}}}\",[]]10A`[\"{BF@jLEj}\",[]]Bm[\"{{{AI@l{CB`BCCh}}Md}{{AI@l{CB`BCCh}}}}\",[]]Bm[\"{{{AI@l{CB`BCCj}}Md}{{AI@l{CB`BCCj}}}}\",[]]o[\"{{}AINj}\",[]]") \ No newline at end of file diff --git a/pr/323/docs/search.index/generic_inverted_index/6ca95edf6c4a.js b/pr/323/docs/search.index/generic_inverted_index/6ca95edf6c4a.js new file mode 100644 index 000000000..6ce44f990 --- /dev/null +++ b/pr/323/docs/search.index/generic_inverted_index/6ca95edf6c4a.js @@ -0,0 +1 @@ +rb_("QU1iADswAAABAAC2AQIALgPdAE0E2AA7MAAAAQAAOgIqAPYBFgAOAggAGAIAABsCAAAdAgAAHwIEACgCBQAvAgAANAICADgCBQBBAgQARwIEAE0CAABPAgEAUgIBAFUCAgBZAgMAXgILAGsCBwB+Ag8AkQIAAJQCAwCZAhEArAIDALECBAC3AgMAvAIAAL4CBQDFAgMAygIHANMCBADaAgUA4QIEAOcCAwDsAgYA+gIAAP8CCAAJAw8Awgl7AC4L3QDFDwAAaRABADswAAABAAC7AQ0ARQbdAD4KLQBuCgAAcAoOAIAKBQCHChkAowoBAKcKEQC6CgAAvQoeAN0KBQDkCikAEgsbADswAAABAAD/AAsANwIAAJgCAAAIAwAA6AcTANwKAADjCgAADwsAABELAAB5DuAAyg8BAAEQAQA7MAAAAQAAYgEFAJAJAAAOCwAAEAsAABcNWQF3EQUAOzAAAAEAAAYAAgBABwMARQcCADswAAABAAApAAUAGQMUABEGAAA7BwQASQcMAIsJAQA7MAAAAQAACgACAC4EAAAMDAkAOzAAAAEAAAwAAQAdBAwAAX0MAAACYxEAAGQRAAAAAscQAADIEAAAAAKoAQAARAcAAENuAAAAOzAAAAEAAN0ADAA+Ci0AbgoAAHAKDgCACgUAhwoZAKMKAQCnChEAugoAAL0KHgDdCgUA5AopABILGwA=") \ No newline at end of file diff --git a/pr/323/docs/search.index/generic_inverted_index/76f3213be5b5.js b/pr/323/docs/search.index/generic_inverted_index/76f3213be5b5.js deleted file mode 100644 index a0bf05304..000000000 --- a/pr/323/docs/search.index/generic_inverted_index/76f3213be5b5.js +++ /dev/null @@ -1 +0,0 @@ -rb_("QUxuADswAAABAAC2AQIALQPdAEwE2AA7MAAAAQAAOgIpAPUBFgANAggAFwIAABoCAAAcAgAAHgIEACcCBQAuAgAAMwICADcCBQBAAgQARgIEAEwCAABOAgEAUQIBAFQCAgBYAgMAXQILAGoCBwB9Ag8AkgIDAJcCEgCrAgMAsAIDALUCBAC8AgYAxAIDAMkCBwDSAgQA2AIAANoCBQDhAgMA5gIDAOsCBgD1AgAA/gIIAAgDDwDBCXsALQvdAMMPAABnEAEAOzAAAAEAALsBDQBEBt0APQotAG0KAABvCg4AfwoFAIYKGQCiCgEApgoRALsKHgDbCgYA4wooAA4LAAARCxsAOzAAAAEAAP8ACwA2AgAAlgIAAAcDAADnBxMA2goAAOIKAAAMCwAAEAsAAHcO4ADIDwEA/w8BADswAAABAABiAQUAjwkAAA0LAAAPCwAAFQ1ZAXURBQA7MAAAAQAABgACAD8HAwBEBwIAOzAAAAEAACkABQAYAxQAEAYAADoHBABIBwwAigkBADswAAABAAAKAAIALQQAAAsMCQA7MAAAAQAADAABABwEDAABfAwAAAJhEQAAYhEAAAACxRAAAMYQAAAAAqcBAABDBwAAQ24AAAA7MAAAAQAA3QAMAD0KLQBtCgAAbwoOAH8KBQCGChkAogoBAKYKEQC7Ch4A2woGAOMKKAAOCwAAEQsbAA==") \ No newline at end of file diff --git a/pr/323/docs/search.index/name/06100f36a877.js b/pr/323/docs/search.index/name/06100f36a877.js new file mode 100644 index 000000000..06db40c92 --- /dev/null +++ b/pr/323/docs/search.index/name/06100f36a877.js @@ -0,0 +1 @@ +rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq00000000000000000000000000000000000000000000000000000000000000000000000000000bid0000000000000brtbtybu8cAck0cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dNonedPath00dPing0dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop0000dfrom00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash00000000000000000000dinto00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnextdopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128dunitdvzip000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eKnowneOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteearrayebenchebuild00000000ebytes00000ecauseeclone000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerror0eframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats00000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosed00fCrypto0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStream000fStringfTypeIdfVarInt0faccept000fborrow00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000fdst_ipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000frebindfrefuse0fremote00frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx0fudp_tx0fvarintfverify0fwindow0000000gAeadKey0gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped0000gStreamsgToOwnedgTooLonggTryFromgTryIntogUnknowngWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000giroh_hp0gis_0rtt0gmetrics0000000gmin_mtu0gon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgradegversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut0hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll000000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiClosePathiCommonOptiConnectediCriterioniDatagramsiDecodableiEncodableiFormatteriFourTupleiFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0ion_closediopen_path0ipath_acks0iping_pathipoll_nextipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint00000istable_idito_string000000000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFrame0jNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjStreamInfojSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code00000jframe_type0jinto_inner0jiroh_quinnjis_drained0jlocal_addr0jlost_bytes0jnew_senderjpad_to_mtu0jpath_stats00jpoll_closejpoll_flush0jpoll_write00jprint_jsonjquinn_perfjread_chunk0jread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kDataBlocked0kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kPathAbandon0kQlogFactory0kResetStream0kSERVER_ADDRkSERVER_NAMEkSmolRuntimekStopSending0kStreamEvent0kStreamStats0kUnspecifiedkaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kinitial_mtu000kinitial_rtt00kinto_future0000000000000kmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_chunkskread_to_endkrecv_stream0ksample_size0ksend_bufferksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handlekwith_crypto0kwith_prefix0kwrite_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDatagramInfolDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lLOCALHOST_V4lLastOpenPathlNatTraversallNonZeroUsizelNoneTokenLog0lObservedAddr0lPathResponse0lPathsBlocked0lReadDatagramlRemoteStatus0lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000lexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets0lmax_incoming0lmay_fragment00lnetwork_pathlnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lsegment_size00lsend_streamslsent_packets0lset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunks0mAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeDone0mHandshakeTypemINVALID_TOKEN0mInvalidHeadermLocallyClosed000mMaxStreamData0mMaxStreamsUni0mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPathChallenge0mPossibleValuemPrivateKeyDermReadableErrormRemoveAddress0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mbench_exports0mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_read_bufmpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000mtry_size_hint0nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidFrameIdnInvalidMessage00nMaxStreamsBidi0nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReachOutAtIpv40nReachOutAtIpv60nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenaead_from_hkdf0nconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000ninto_unorderedniroh_quinn_udpnis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset00nremote_address0000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose000oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoNewConnectionId0oOpenStreamStatsoPathCidsBlocked0oProtectedHeader0oQlogFileFactory0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`AddressDiscoveryA`ApplicationClose000A`CertificateErrorA`ConnectionClosed0A`ConnectionHandleA`DatagramReceivedA`EndpointStopping0A`FINAL_SIZE_ERROR0A`InconsistentKeys00A`KEY_UPDATE_ERROR0A`MaxPathIdReachedA`ObservedIpv4Addr0A`ObservedIpv6Addr0A`PathStatusBackup0A`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailedA`VersionNegotiate0A`check_and_insert00000A`configure_clientA`congestion_state0A`connection_close0A`force_key_update0A`from_arg_matches00000A`iroh_quinn_protoA`max_gso_segmentsA`max_idle_timeout0A`max_streams_bidi0A`open_connections0A`open_path_ensure0A`packet_threshold0A`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed0AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaPacketDecodeError0AaSendDatagramError0AaStreamDataBlocked0AaStreamsBlockedUni0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedReadAbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbRetireConnectionId0AbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbStreamsBlockedBidi0AbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcMaxRecursionReachedAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhaustedAcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest00000Acaugment_subcommandsAcblack_hole_cooldown0Acearly_data_accepted000Achandshake_confirmedAckeep_alive_interval0Aclost_plpmtud_probes0Acon_congestion_event0000000Acoutgoing_handshakesAcremote_open_streamsAcsend_buffer_benches00Acsend_data_on_streamAcsent_plpmtud_probes0Acstream_data_blocked0Acstreams_blocked_uni0AdFRAME_ENCODING_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress00AdNoInitialCipherSuite0AdServerSideNotAllowedAdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_frequency_config0Adblack_holes_detected0Adfrom_arg_matches_mut00000Adhandle_client_streamAdincoming_buffer_size0Adis_multipath_enabledAdmax_receive_segments0Admax_udp_payload_size00Admtu_discovery_config0Adpoll_endpoint_eventsAdpreferred_address_v40Adpreferred_address_v60Adread_certs_from_fileAdreordering_threshold0Adretire_connection_id0Adretry_token_lifetime0Adset_max_idle_timeoutAdset_recv_buffer_sizeAdset_send_buffer_sizeAdstreams_blocked_bidi0Adtransport_parameters00000AeConnectionIdGenerator0AeDigitallySignedStructAeNoApplicationProtocol0AeNoDefaultClientConfig0AePATH_UNSTABLE_OR_POOR0AeValidationTokenConfig0Aeconfidentiality_limit0Aeincoming_buffer_bytesAeis_probably_same_pathAelocal_address_changedAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated00AfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgset_keep_alive_intervalAgupdate_from_arg_matches00000Agvalidation_token_config0AhAPPLICATION_ABANDON_PATH0AhNoProtectionClientConfigAhNoProtectionServerConfigAhget_max_udp_payload_size0Ahinitial_dst_cid_provider0Ahnew_with_abstract_socketAhremote_address_validated0Ahset_max_concurrent_pathsAhsupported_verify_schemesAiCONNECTION_ID_LIMIT_ERROR0AiEncryptedClientHelloErrorAiExportKeyingMaterialError0AiInvalidCertRevocationList0AiNO_CID_AVAILABLE_FOR_PATH0AiTRANSPORT_PARAMETER_ERROR0AiUnsupportedOperationErrorAiadd_nat_traversal_address0Aidatagram_send_buffer_size0Aigenerate_self_signed_certAiopen_bidirectional_streamAipath_retire_connection_id0Aiset_default_client_configAiset_path_max_idle_timeoutAjDEFAULT_SUPPORTED_VERSIONSAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Akupdate_local_if_same_remoteAlconfigure_tracing_subscriberAldatagram_receive_buffer_size0Alinitiate_nat_traversal_round0Alon_spurious_congestion_event000Alreceive_bidirectional_streamAlremove_nat_traversal_address0Alset_path_keep_alive_intervalAmFixedLengthConnectionIdParserAmInappropriateHandshakeMessage000Amcongestion_controller_factory0Amdefault_path_max_idle_timeout0Amnegotiated_key_exchange_group0Amreceive_unidirectional_streamAmsend_observed_address_reports0Amset_max_concurrent_bi_streamsAnaugment_subcommands_for_updateAnmax_concurrent_multipath_paths0Anset_max_concurrent_uni_streamsAopersistent_congestion_threshold0B`default_path_keep_alive_interval0B`receive_observed_address_reports0Baget_local_nat_traversal_addresses0Bbget_remote_nat_traversal_addresses0Bbmax_remote_nat_traversal_addressesBfset_max_remote_nat_traversal_addresses0BoDEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED") \ No newline at end of file diff --git a/pr/323/docs/search.index/name/52ee5dc5acb9.js b/pr/323/docs/search.index/name/52ee5dc5acb9.js deleted file mode 100644 index 405f23b32..000000000 --- a/pr/323/docs/search.index/name/52ee5dc5acb9.js +++ /dev/null @@ -1 +0,0 @@ -rd_("b()b->bBi0bCe00bFnbIdbOkb[]beq00000000000000000000000000000000000000000000000000000000000000000000000000000bid0000000000000brtbtybu8cAck0cAnycArccBbr00cBoxcBufcClicDir0cErrcMAX000cNotcOpt00cOrdcPincUni0cVeccbbrccmp0000000000000000000cdir000cecn000cf32cf64cfmt000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cgetci32cimpcios0clen000clog0cmincmutcnew00000000000000000000000000000000000cnot0cnow000000crtt000crun0cstrcu16cu32cu64cudpdArgsdAsFddDropdEct000dEct100dFromdHashdIntodKeys0dLong0dNonedPath00dPing0dPolldReaddSenddSide0dSyncdVZipdZERO0dacks0daddr0dbooldbulkdcodedcwnd0ddataddrop0000dfrom00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dhash00000000000000000000dinto00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dmain0000dnextdopen00dpath0dperfdping000dpoll00000000000000dqlogdread00drecv00dseal0dsend00dsent0dside0dsign0dsize00dsmoldspindstop00dtake00000du128dunitdvzip000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eAsRefeByteseChunk0eCloneeCloseeCubic00eDebugeDerefeError00000000000eEvent00eKnowneOther0eRangeeReset000eRetry0eShort0eSizedeStats0eSuiteeWriteearrayebenchebuild00000000ebytes00000ecauseeclone000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eclose000ecubicederef0eerror0eframe0eindex0eis_ce0elocal0eparse0epaths0eprint00ereset00eretry0esliceespawn00estats00000000etitle0etokenetokioetupleeusizeewrite00fAcceptfAckEcn0fAes128fAes256fBackupfBorrowfBufMutfChunksfClient00fClosed00fCrypto0fCursorfFuturefHasherfIpAddrfOpenBifOpened000fOptionfOutputfPathId0fResult00000fServer00fStream000fStringfTypeIdfVarInt0faccept000fborrow00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fchunks0fcipherfclient0000fclosedfcoding0fconfig0fcrypto0000000000fdecode000000000000fdst_ipfencode000000000000ffinish00fhandlefheader0fignore0finsert00000fkeylogfoffset0fon_ack0000000fpacket00frandomfreason00000frebindfrefuse0fremote00frustls00000fserver0000fsharedfsource00000000fsrc_ip00fstatus0fstream0fstridefudp_rx0fudp_tx0fvarintfverify0fwindow0000000gAeadKey0gBlocked00gCommandgContextgDefaultgDisplaygFromStrgGeneral0gHmacKey0gInitialgInstantgKeyPair0gLayeredgMaxData0gNewReno00gOpenUnigPadding0gPathAck0gPathBufgReadBufgRequestgRuntime0gSession0gStopped0000gStreamsgToOwnedgTooLonggTryFromgTryIntogUnknowngWritten0gZeroRttgcid_len000gclientsgcommand00000gcompare0000000000000000000gconnect0gdecrypt000gdefault000000000000000000000000000000000000000000000000000gdrainedgdst_cid0000000gencrypt000giroh_hp0gis_0rtt0gmetrics0000000gmin_mtu0gon_sent000gopen_bigpath_idgpointergruntimegsrc_cid0000gstopped0gstreams000gtag_len0gtype_id00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000gupgradegversion00000hAcceptBihBytesMuthChacha20hCommandshDatagram00hDerefMuthDisabled0hDurationhEndpoint00hFinished0hIncoming0hLayerExthLongTypehMAX_SIZE0hNO_ERROR0hNewToken0hOnClosedhOpenPathhOrderinghReadable0hReceiver0hRecvMetahResponsehStreamId0hTimedOut0hToStringhTokenLog0hTooLarge0hTransmit00hUdpStats0hUnixTimehWritable0hcong_alghcontentshdatagram0hdurationhendpoint0hfinalizehframe_rx0hframe_tx0hfrom_buf0hfrom_env0hfrom_keyhfrom_strhfrom_u320hfrom_u640hgot_type000hgroup_id0000hhas_0rtthincominghinterval0hinto_any0000000his_alivehlifetime0hlocal_ip0000hmax_data0hmax_sizehnew_renohon_byteshopen_unihpriority0hprotocol0hqlog_dirhresponsehrng_seed0hssthresh0hto_owned000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_from00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_into00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000htry_poll000000000htry_sendhvalidate000iAbandoned0iAcceptUniiArbitraryiAsyncRead0iAvailable00iBbrConfig0iBorrowMutiClosePathiCommonOptiConnectediCriterioniDatagramsiDecodableiEncodableiFormatteriFourTupleiFrameType0iHandshakeiHeaderKey0iHistogramiMalformediMaxPathId0iMultipathiPacketKey0iPartialEqiPathErroriPathEvent00iPathStats0iReadError00iTryFutureiUdpSenderiUdpSocketiValueEnumiaccept_biiarbitrary0000000ias_rustlsiassemblericlone_box0000000idatagrams000ideref_mut0ifrom_bits00iinitiator0iinto_0rttiis_client0iis_closediis_server0imay_retry0imigration0inew_timer00inew_token0ion_closediopen_path0ipath_acks0iping_pathipoll_nextipoll_read00ipoll_recvipoll_sendireach_out0ireferenceireset_key0iretry_tag0000isize_hint00000istable_idito_string00000000000000000000000000000000000000000000000000000000000itoken_key0itoken_positransport00iwait_idleiwrite_alljArgMatchesjAsyncTimerjAsyncWrite0jBATCH_SIZEjClosedPath000jComparablejConnectingjConnection00jController0jEquivalent0jFrameStats0jIntoFuturejInvalidCid0jIoSliceMutjMaybeFrame0jNamedGroupjNonZeroU32jOtherErrorjPartialOrdjPathAckEcn0jPathStatus0jQlogConfig0jRecvStream00jResetErrorjRetryError0jSendStream00jServerNamejSocketAddrjStreamInfojSubcommandjSystemTimejTimeSource0jTlsSession0jTokenStore0jUdpSockRefjWriteError0jaccept_unijallow_spin0jborrow_mut00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclone_into000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jclose_pathjcongestion000jconn_statsjconnection0jequivalent00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000jerror_code00000jframe_type0jinto_inner0jiroh_quinnjis_drained0jlocal_addr0jlost_bytes0jnew_senderjpad_to_mtu0jpath_stats00jpoll_closejpoll_flush0jpoll_write00jprint_jsonjquinn_perfjread_chunk0jread_exactjset_statusjstart_time0jthroughputjtotal_sizekAcceptErrorkCLIENT_ADDRkCipherSuitekClientStatskCloseReasonkConfigError0kContentTypekCryptoError0kCubicConfig0kDataBlocked0kFinishErrorkIdleTimeout0kIntoRequestkOutOfBounds0kPathAbandon0kQlogFactory0kResetStream0kSERVER_ADDRkSERVER_NAMEkSmolRuntimekStopSending0kStreamEvent0kStreamStats0kUnspecifiedkaccept_withkadd_address0kbind_socketkcertificatekcurrent_mtu00kdescription0kdestination00kinitial_mtu000kinitial_rtt00kinto_future0000000000000kmax_path_id0kmax_streamsknamed_layer0kon_end_acks000kon_intervalkpacing_rate0kpartial_cmp0000000000000000000kpath_eventskpath_statuskread_chunkskread_to_endkrecv_stream0ksample_size0ksend_bufferksend_stream0ksend_window00kserver_name0ktime_source0ktoken_store0kupload_sizekupper_bound0kweak_handlekwith_crypto0kwith_prefix0kwrite_chunklAckFrequency0lAddressAddedlClientConfig00000lClosedStream000000lConnectError00lConnectionId00lDatagramInfolDecryptError0lEcnCodepoint00lEncryptError0lIllegalValuelImmediateAck0lLOCALHOST_V4lLastOpenPathlNatTraversallNonZeroUsizelNoneTokenLog0lObservedAddr0lPathResponse0lPathsBlocked0lReadDatagramlRemoteStatus0lRttEstimator0lSendDatagramlServerConfig00000lSocketAddrV4lSocketAddrV6lStoppedErrorlTokioRuntimelUnstructuredlaugment_args0000lcid_lifetime000lclose_reasonlconnect_withlconservativeldata_blocked0ldrain_streamlearly_crypto000lexpect_types000lgenerate_cid000lgro_segmentslhandle_event0linitial_keys00000000linto_request0llost_packets0lmax_incoming0lmay_fragment00lnetwork_pathlnew_receiverlnoprotection0lorig_dst_cid0lpath_abandon0lpoll_timeoutlqlog_factory0lreset_stream0lsegment_size00lsend_streamslsent_packets0lset_lifetime0lset_priority0lstop_sending0lstream_statslupload_statslwith_initial000lwrite_chunks0mAlertReceived0mBloomTokenLog0mCidsExhausted000mCloneToUninitmDatagramEventmEndpointEventmEndpointStatsmFinishedEarlymHandshakeData0mHandshakeDone0mHandshakeTypemINVALID_TOKEN0mInvalidHeadermLocallyClosed000mMaxStreamData0mMaxStreamsUni0mNewConnectionmNewRenoConfig0mParseIntErrormPartialDecodemPathChallenge0mPossibleValuemPrivateKeyDermReadableErrormRemoveAddress0mRootCertStoremStdSystemTime0mUnexpectedEndmaccepted_0rttmack_frequency00mbench_exports0mcid_generator00mcreate_sendermdata_transfermdownload_sizemduration_histmimmediate_ack0minto_incoming0mmax_ack_delay0mno_protectionmobserved_addr0mon_first_bytemon_mtu_update0000000mpath_response0mpaths_blocked0mpeer_identity0000mpoll_read_bufmpoll_shutdownmpoll_transmitmqlog_from_env0mread_datagrammsend_datagrammsend_fairness0msignature_len0mstart_session000000000mtry_size_hint0nAddIpv4Address0nAddIpv6Address0nAddressRemovednAsyncUdpSocketnCertificateDernClosePathError0nCommandFactorynConnectionLost000000nEndpointConfig0nFromArgMatchesnINTERNAL_ERROR0nIncomingFuturenInvalidFrameIdnInvalidMessage00nMaxStreamsBidi0nNO_VIABLE_PATH0nNoneTokenStore0nPeerMisbehaved00nReachOutAtIpv40nReachOutAtIpv60nReadExactErrornReadToEndErrornShouldTransmitnTransferResultnTransportError00nUdpSocketStatenaead_from_hkdf0nconnect_clientncrypto_sessionndownload_statsnfor_connection000nhandle_timeoutnhandshake_data00000nhandshake_done0nhas_subcommandninitial_window0000000000000ninto_unorderedniroh_quinn_udpnis_handshaking0000nis_valid_retry000nminimum_change0nnext_1rtt_keys000npath_challenge0nqlog_from_path0nread_handshake000nread_unorderednreceive_window00nreceived_reset00nremote_address0000nremove_address0nsaturating_add000nsaturating_sub0nthroughput_bpsntime_threshold0ntotal_durationnvalue_variantsoConnectionClose000oConnectionError0oConnectionEvent0oConnectionStats0oGetRandomFailedoNewConnectionId0oOpenStreamStatsoPathCidsBlocked0oProtectedHeader0oQlogFileFactory0oSignatureSchemeoSystemTimeErroroTokenReuseError0oTransportConfig00oTryFromIntErroroVersionMismatch0oZeroRttAcceptedoZeroRttRejected000obloom_token_logoclone_to_uninit000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000odefault_runtimeogrease_quic_bit0ointegrity_limit0ointerface_indexomax_stream_data0omax_streams_uni0oparse_byte_sizeorebind_abstractosend_unreliableoserver_endpointoset_path_statusoset_send_window0oshould_transmitostream_finishedothroughput_histowrap_udp_socket00owrite_handshake000A`AddressDiscoveryA`ApplicationClose000A`CertificateErrorA`ConnectionClosed0A`ConnectionHandleA`DatagramReceivedA`EndpointStopping0A`FINAL_SIZE_ERROR0A`InconsistentKeys00A`KEY_UPDATE_ERROR0A`MaxPathIdReachedA`ObservedIpv4Addr0A`ObservedIpv6Addr0A`PathStatusBackup0A`PeerIncompatible00A`QuicClientConfig0A`QuicServerConfig0A`TokenMemoryCache0A`TooManyAddressesA`ValidationFailedA`VersionNegotiate0A`check_and_insert00000A`configure_clientA`congestion_state0A`connection_close0A`force_key_update0A`from_arg_matches00000A`iroh_quinn_protoA`max_gso_segmentsA`max_idle_timeout0A`max_streams_bidi0A`open_connections0A`open_path_ensure0A`packet_threshold0A`receive_datagramA`recv_buffer_size0A`send_buffer_size0A`transport_config0000A`validation_token0A`with_single_cert0A`write_all_chunksAaAPPLICATION_ERROR0AaApplicationClosed0AaControllerFactory0AaControllerMetrics0AaHandshakeTokenKey0AaInvalidServerName0AaPacketDecodeError0AaSendDatagramError0AaStreamDataBlocked0AaStreamsBlockedUni0AaUnsupportedByPeer0Aacongestion_events0Aacongestion_window0Aamax_datagram_sizeAanew_connection_id0Aapath_cids_blocked0Aasend_buffer_spaceAaset_server_config0Aaset_up_connectionAato_possible_valueAbAEAD_LIMIT_REACHED0AbAckFrequencyConfig0AbBadMaxFragmentSize0AbCONNECTION_REFUSED0AbConnectionIdParserAbDatagramsUnblockedAbFLOW_CONTROL_ERROR0AbHandshakeConfirmedAbHandshakeDataReadyAbIllegalOrderedReadAbInvalidCertificate0AbMtuDiscoveryConfig0AbNotEnoughAddressesAbPERF_CIPHER_SUITESAbPROTOCOL_VIOLATION0AbPrivatePkcs8KeyDerAbRetireConnectionId0AbSTREAM_LIMIT_ERROR0AbSTREAM_STATE_ERROR0AbServerCertVerifiedAbServerCertVerifierAbSetPathStatusError0AbStreamsBlockedBidi0AbTransportErrorCode0AbUnsupportedVersion00000Abcommand_for_update0000Abcrypto_buffer_size0Abfrom_u64_unchecked0Abignored_handshakesAbmin_reset_interval0Abnew_expected_items0Abpath_status_backup0Abrefused_handshakesAbremote_path_statusAbsend_datagram_waitAbset_receive_window0Absupported_versions0Abtoken_memory_cacheAbverify_server_certAbwith_start_instant0AcCongestionAlgorithmAcMaxRecursionReachedAcPathNewConnectionId0AcPathStatusAvailable0AcRemoteCidsExhaustedAcTransportParametersAcUnorderedRecvStreamAcUnsupportedNameType0AcWrongConnectionSideAcaccepted_handshakesAcarbitrary_take_rest00000Acaugment_subcommandsAcblack_hole_cooldown0Acearly_data_accepted000Achandshake_confirmedAckeep_alive_interval0Aclost_plpmtud_probes0Acon_congestion_event0000000Acoutgoing_handshakesAcremote_open_streamsAcsend_buffer_benches00Acsend_data_on_streamAcsent_plpmtud_probes0Acstream_data_blocked0Acstreams_blocked_uni0AdFRAME_ENCODING_ERROR0AdHandshakeNotComplete0AdInappropriateMessage000AdInvalidRemoteAddress00AdNoInitialCipherSuite0AdServerSideNotAllowedAdSupportedCipherSuiteAdVarIntBoundsExceeded0AdVerifierBuilderErrorAdWeakConnectionHandleAdack_frequency_config0Adblack_holes_detected0Adfrom_arg_matches_mut00000Adhandle_client_streamAdincoming_buffer_size0Adis_multipath_enabledAdmax_receive_segments0Admax_udp_payload_size00Admtu_discovery_config0Adpoll_endpoint_eventsAdpreferred_address_v40Adpreferred_address_v60Adread_certs_from_fileAdreordering_threshold0Adretire_connection_id0Adretry_token_lifetime0Adset_max_idle_timeoutAdset_recv_buffer_sizeAdset_send_buffer_sizeAdstreams_blocked_bidi0Adtransport_parameters00000AeConnectionIdGenerator0AeDigitallySignedStructAeNoApplicationProtocol0AeNoDefaultClientConfig0AePATH_UNSTABLE_OR_POOR0AeValidationTokenConfig0Aeconfidentiality_limit0Aeincoming_buffer_bytesAeis_probably_same_pathAelocal_address_changedAeloss_reduction_factor0Aemax_transmit_segments0Aenat_traversal_updatesAepath_observed_addressAepath_status_available0Aestream_receive_window00AfCRYPTO_BUFFER_EXCEEDED0AfExtensionNotNegotiatedAfFailedToGetCurrentTime0AfFailedToGetRandomBytes0AfMultipathNotNegotiated00AfPathRetireConnectionId0AfProtectedInitialHeaderAfSkipServerVerificationAfbuild_transport_configAfexport_keying_material0000Afget_into_many_segmentsAfget_loop_many_segmentsAfmax_concurrent_streamsAfobserved_external_addr0Afpath_new_connection_id0Afverify_tls12_signatureAfverify_tls13_signatureAfwith_root_certificates0AgCertRevocationListErrorAgHandshakeSignatureValidAgNoCertificatesPresented0AgPeerSentOversizedRecord0Agack_eliciting_threshold0Agaugment_args_for_update0000Aghas_pending_retransmitsAgis_multipath_negotiatedAgset_keep_alive_intervalAgupdate_from_arg_matches00000Agvalidation_token_config0AhAPPLICATION_ABANDON_PATH0AhNoProtectionClientConfigAhNoProtectionServerConfigAhget_max_udp_payload_size0Ahinitial_dst_cid_provider0Ahnew_with_abstract_socketAhremote_address_validated0Ahset_max_concurrent_pathsAhsupported_verify_schemesAiCONNECTION_ID_LIMIT_ERROR0AiEncryptedClientHelloErrorAiExportKeyingMaterialError0AiInvalidCertRevocationList0AiNO_CID_AVAILABLE_FOR_PATH0AiTRANSPORT_PARAMETER_ERROR0AiUnsupportedOperationErrorAiadd_nat_traversal_address0Aidatagram_send_buffer_size0Aigenerate_self_signed_certAiopen_bidirectional_streamAipath_retire_connection_id0Aiset_default_client_configAiset_path_max_idle_timeoutAjDEFAULT_SUPPORTED_VERSIONSAjdatagram_send_buffer_spaceAjincoming_buffer_size_total0Ajmax_concurrent_uni_streams0Ajopen_unidirectional_streamAjset_max_concurrent_streamsAjtry_with_platform_verifier0AkHashedConnectionIdGeneratorAkInvalidEncryptedClientHello0AkPATH_RESOURCE_LIMIT_REACHED0AkRandomConnectionIdGeneratorAkenable_segmentation_offload0Akmax_concurrent_bidi_streams0Akupdate_from_arg_matches_mut00000Akupdate_local_if_same_remoteAlconfigure_tracing_subscriberAldatagram_receive_buffer_size0Alinitiate_nat_traversal_round0Alon_spurious_congestion_event000Alreceive_bidirectional_streamAlremove_nat_traversal_address0Alset_path_keep_alive_intervalAmFixedLengthConnectionIdParserAmInappropriateHandshakeMessage000Amcongestion_controller_factory0Amdefault_path_max_idle_timeout0Amnegotiated_key_exchange_group0Amreceive_unidirectional_streamAmsend_observed_address_reports0Amset_max_concurrent_bi_streamsAnaugment_subcommands_for_updateAnmax_concurrent_multipath_paths0Anset_max_concurrent_uni_streamsAopersistent_congestion_threshold0B`default_path_keep_alive_interval0B`receive_observed_address_reports0Baget_local_nat_traversal_addresses0Bbget_remote_nat_traversal_addresses0Bbmax_remote_nat_traversal_addressesBfset_max_remote_nat_traversal_addresses0BoDEFAULT_CONCURRENT_MULTIPATH_PATHS_WHEN_ENABLED") \ No newline at end of file diff --git a/pr/323/docs/search.index/normalizedName/9cdde8eea858.js b/pr/323/docs/search.index/normalizedName/9cdde8eea858.js deleted file mode 100644 index 2008b2f86..000000000 --- a/pr/323/docs/search.index/normalizedName/9cdde8eea858.js +++ /dev/null @@ -1 +0,0 @@ -rd_("b()b->bbi0bce00bfnbidbokb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000000033333333333333brtbtybu8cack0canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecbbi0bce00bfnbidbokb[]beq0000000000000000000000000000000000000000000000000000000000000000000000000000033333333333333brtbtybu8cack0canycarccbbr00cboxcbufcclicdir0cerrcmax000cnotcopt00cordcpincuni0cvecA`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]Bb[2,\"iroh_quinn_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]4Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]A`[5,\"iroh_quinn\"]Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]8Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]0f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]1Bj[6,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]0Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Af[5,\"iroh_quinn_proto\"]Bj[5,\"iroh_quinn_udp\",\"iroh_quinn_udp::imp\"]?A`[5,\"iroh_quinn\"]8A`[6,\"iroh_quinn\"]=Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]2Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]7Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]A`[5,\"iroh_quinn\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::transport\"]Ae[10,\"tonic::request\"]Aa[10,\"iroh_quinn\"]Ch[10,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bl[5,\"iroh_quinn\",\"iroh_quinn::runtime::smol\"]Af[2,\"iroh_quinn_proto\"]Cn[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Ao[5,\"perf::stats\",\"perf::stats\"]Ci[5,\"ring::error::unspecified\",\"ring::error::unspecified\"]f[3,\"\"]A`[2,\"iroh_quinn\"]Bb[2,\"iroh_quinn_proto::connection\"]1Co[5,\"rustls::client::client_conn\",\"rustls::client::client_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]?Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]A`[5,\"iroh_quinn\"]Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Cm[6,\"iroh_quinn_proto::endpoint\",\"iroh_quinn_proto::endpoint\"]A`[6,\"iroh_quinn\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ci[5,\"iroh_quinn_proto::shared\",\"iroh_quinn_proto::shared\"]5Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]4Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Be[6,\"iroh_quinn_udp\",\"iroh_quinn_udp\"]Bm[8,\"core::num::nonzero\",\"core::num::nonzero\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Bi[5,\"iroh_quinn\",\"iroh_quinn::connection\"]1Do[5,\"iroh_quinn_proto::connection::paths\",\"iroh_quinn_proto::connection::paths\"]Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::paths\"]2Co[5,\"rustls::server::server_conn\",\"rustls::server::server_conn\"]Ci[5,\"iroh_quinn_proto::config\",\"iroh_quinn_proto::config\"]A`[5,\"iroh_quinn\"]Ai[10,\"iroh_quinn::crypto\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Cj[10,\"iroh_quinn_proto::crypto\",\"iroh_quinn_proto::crypto\"]Ce[5,\"core::net::socket_addr\",\"core::net::socket_addr\"]0Bj[6,\"iroh_quinn\",\"iroh_quinn::send_stream\"]Bm[5,\"iroh_quinn\",\"iroh_quinn::runtime::tokio\"]Cg[5,\"arbitrary::unstructured\",\"arbitrary::unstructured\"]j[2,\"perf\"]8Cj[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::bloom_token_log\"]Ab[10,\"core::clone\"]Cc[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::endpoint\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]Bg[5,\"iroh_quinn\",\"iroh_quinn::endpoint\"]B`[5,\"iroh_quinn::crypto::rustls\"]Di[5,\"iroh_quinn_proto::crypto::rustls\",\"iroh_quinn_proto::crypto::rustls\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Bb[15,\"iroh_quinn_proto::PathEvent\"]Al[5,\"iroh_quinn::congestion\"]Dk[5,\"iroh_quinn_proto::congestion\",\"iroh_quinn_proto::congestion::new_reno\"]Bi[5,\"core::num::error\",\"core::num::error\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]Ec[5,\"clap_builder::builder::possible_value\",\"clap_builder::builder::possible_value\"]Bi[6,\"rustls_pki_types\",\"rustls_pki_types\"]Dd[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams::recv\"]Cg[5,\"rustls::webpki::anchors\",\"rustls::webpki::anchors\"]A`[5,\"iroh_quinn\"]Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config\"]Ci[5,\"iroh_quinn_proto::coding\",\"iroh_quinn_proto::coding\"]Af[2,\"iroh_quinn_proto\"]0f[3,\"\"]Bg[10,\"iroh_quinn\",\"iroh_quinn::runtime\"]Bi[5,\"rustls_pki_types\",\"rustls_pki_types\"]Da[6,\"iroh_quinn_proto::connection\",\"iroh_quinn_proto::connection\"]Ce[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection\"]Ak[10,\"clap_builder::derive\"]An[15,\"iroh_quinn_proto::Event\"]:91Bg[5,\"iroh_quinn\",\"iroh_quinn::incoming\"]C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::frame\"]Bc[6,\"rustls::error\",\"rustls::error\"]=C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]1Bj[6,\"iroh_quinn\",\"iroh_quinn::recv_stream\"]0Cn[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::streams\"]Ba[5,\"bench::stats\",\"bench::stats\"]Af[5,\"iroh_quinn_proto\"]Bj[5,\"iroh_quinn_udp\",\"iroh_quinn_udp::imp\"]?A`[5,\"iroh_quinn\"]8A`[6,\"iroh_quinn\"]=Ca[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::shared\"]2Cl[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::connection::stats\"]Ba[5,\"rustls::rand\",\"rustls::rand\"]Ao[5,\"perf::stats\",\"perf::stats\"]Af[2,\"iroh_quinn_proto\"]Ca[6,\"iroh_quinn_proto\",\"iroh_quinn_proto::packet\"]7Cg[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::config::qlog\"]Bc[6,\"rustls::enums\",\"rustls::enums\"]Ak[5,\"std::time\",\"std::time\"]:C`[5,\"iroh_quinn_proto\",\"iroh_quinn_proto::token\"]Do[5,\"iroh_quinn_proto::config::transport\",\"iroh_quinn_proto::config::transport\"]Settings

All

Rustdoc settings

Back
\ No newline at end of file +Settings

All

Rustdoc settings

Back
\ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/datagrams.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/datagrams.rs.html index 6248844ea..8d086e148 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/datagrams.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/datagrams.rs.html @@ -1,13 +1,13 @@ datagrams.rs - source

iroh_quinn_proto/connection/
datagrams.rs

1use std::collections::VecDeque;
 2
-3use bytes::{BufMut, Bytes};
+3use bytes::Bytes;
 4use thiserror::Error;
 5use tracing::{debug, trace};
 6
 7use super::Connection;
 8use crate::{
-9    TransportError,
-10    coding::Encodable,
+9    FrameStats, TransportError,
+10    connection::PacketBuilder,
 11    frame::{Datagram, FrameStruct},
 12};
 13
@@ -172,49 +172,51 @@
 172    ///
 173    /// Returns whether a frame was written. At most `max_size` bytes will be written, including
 174    /// framing.
-175    pub(super) fn write(&mut self, buf: &mut impl BufMut) -> bool {
-176        let datagram = match self.outgoing.pop_front() {
-177            Some(x) => x,
-178            None => return false,
-179        };
-180
-181        if buf.remaining_mut() < datagram.size(true) {
-182            // Future work: we could be more clever about cramming small datagrams into
-183            // mostly-full packets when a larger one is queued first
-184            self.outgoing.push_front(datagram);
-185            return false;
-186        }
-187
-188        trace!(len = datagram.data.len(), "DATAGRAM");
-189
-190        self.outgoing_total -= datagram.data.len();
-191        datagram.encode(buf);
-192        true
-193    }
-194
-195    pub(super) fn recv(&mut self) -> Option<Bytes> {
-196        let x = self.incoming.pop_front()?.data;
-197        self.recv_buffered -= x.len();
-198        Some(x)
-199    }
-200}
-201
-202/// Errors that can arise when sending a datagram
-203#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-204pub enum SendDatagramError {
-205    /// The peer does not support receiving datagram frames
-206    #[error("datagrams not supported by peer")]
-207    UnsupportedByPeer,
-208    /// Datagram support is disabled locally
-209    #[error("datagram support disabled")]
-210    Disabled,
-211    /// The datagram is larger than the connection can currently accommodate
-212    ///
-213    /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been
-214    /// exceeded.
-215    #[error("datagram too large")]
-216    TooLarge,
-217    /// Send would block
-218    #[error("datagram send blocked")]
-219    Blocked(Bytes),
-220}
\ No newline at end of file +175 pub(super) fn write<'a, 'b>( +176 &mut self, +177 buf: &mut PacketBuilder<'a, 'b>, +178 stat: &mut FrameStats, +179 ) -> bool { +180 let datagram = match self.outgoing.pop_front() { +181 Some(x) => x, +182 None => return false, +183 }; +184 +185 if buf.frame_space_remaining() < datagram.size(true) { +186 // Future work: we could be more clever about cramming small datagrams into +187 // mostly-full packets when a larger one is queued first +188 self.outgoing.push_front(datagram); +189 return false; +190 } +191 +192 self.outgoing_total -= datagram.data.len(); +193 buf.write_frame(datagram, stat); +194 true +195 } +196 +197 pub(super) fn recv(&mut self) -> Option<Bytes> { +198 let x = self.incoming.pop_front()?.data; +199 self.recv_buffered -= x.len(); +200 Some(x) +201 } +202} +203 +204/// Errors that can arise when sending a datagram +205#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +206pub enum SendDatagramError { +207 /// The peer does not support receiving datagram frames +208 #[error("datagrams not supported by peer")] +209 UnsupportedByPeer, +210 /// Datagram support is disabled locally +211 #[error("datagram support disabled")] +212 Disabled, +213 /// The datagram is larger than the connection can currently accommodate +214 /// +215 /// Indicates that the path MTU minus overhead or the limit advertised by the peer has been +216 /// exceeded. +217 #[error("datagram too large")] +218 TooLarge, +219 /// Send would block +220 #[error("datagram send blocked")] +221 Blocked(Bytes), +222}

\ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/mod.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/mod.rs.html index 44cb4dec1..132fd1fa3 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/mod.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/mod.rs.html @@ -23,6855 +23,6682 @@ 23 TransportError, TransportErrorCode, VarInt, 24 cid_generator::ConnectionIdGenerator, 25 cid_queue::CidQueue, -26 coding::{BufMutExt, Encodable}, -27 config::{ServerConfig, TransportConfig}, -28 congestion::Controller, -29 connection::{ -30 qlog::{QlogRecvPacket, QlogSentPacket, QlogSink}, -31 spaces::LostPacket, -32 timer::{ConnTimer, PathTimer}, -33 }, -34 crypto::{self, KeyPair, Keys, PacketKey}, -35 frame::{self, Close, Datagram, FrameStruct, NewToken, ObservedAddr}, -36 iroh_hp, -37 packet::{ -38 FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet, -39 PacketNumber, PartialDecode, SpaceId, -40 }, -41 range_set::ArrayRangeSet, -42 shared::{ -43 ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint, -44 EndpointEvent, EndpointEventInner, -45 }, -46 token::{ResetToken, Token, TokenPayload}, -47 transport_parameters::TransportParameters, -48}; -49 -50mod ack_frequency; -51use ack_frequency::AckFrequencyState; -52 -53mod assembler; -54pub use assembler::Chunk; -55 -56mod cid_state; -57use cid_state::CidState; -58 -59mod datagrams; -60use datagrams::DatagramState; -61pub use datagrams::{Datagrams, SendDatagramError}; -62 -63mod mtud; -64mod pacing; -65 -66mod packet_builder; -67use packet_builder::{PacketBuilder, PadDatagram}; -68 -69mod packet_crypto; -70use packet_crypto::{PrevCrypto, ZeroRttCrypto}; -71 -72mod paths; -73pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError}; -74use paths::{PathData, PathState}; -75 -76pub(crate) mod qlog; -77pub(crate) mod send_buffer; -78 -79mod spaces; -80#[cfg(fuzzing)] -81pub use spaces::Retransmits; -82#[cfg(not(fuzzing))] -83use spaces::Retransmits; -84use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; -85 -86mod stats; -87pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats}; -88 -89mod streams; -90#[cfg(fuzzing)] -91pub use streams::StreamsState; -92#[cfg(not(fuzzing))] -93use streams::StreamsState; -94pub use streams::{ -95 Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream, -96 ShouldTransmit, StreamEvent, Streams, WriteError, Written, -97}; -98 -99mod timer; -100use timer::{Timer, TimerTable}; -101 -102mod transmit_buf; -103use transmit_buf::TransmitBuf; -104 -105mod state; -106 -107#[cfg(not(fuzzing))] -108use state::State; -109#[cfg(fuzzing)] -110pub use state::State; -111use state::StateType; -112 -113/// Protocol state and logic for a single QUIC connection -114/// -115/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application -116/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and -117/// expects timeouts through various methods. A number of simple getter methods are exposed -118/// to allow callers to inspect some of the connection state. -119/// -120/// `Connection` has roughly 4 types of methods: -121/// -122/// - A. Simple getters, taking `&self` -123/// - B. Handlers for incoming events from the network or system, named `handle_*`. -124/// - C. State machine mutators, for incoming commands from the application. For convenience we -125/// refer to this as "performing I/O" below, however as per the design of this library none of the -126/// functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and -127/// [`write`](SendStream::write), but also things like [`reset`](SendStream::reset). -128/// - D. Polling functions for outgoing events or actions for the caller to -129/// take, named `poll_*`. -130/// -131/// The simplest way to use this API correctly is to call (B) and (C) whenever -132/// appropriate, then after each of those calls, as soon as feasible call all -133/// polling methods (D) and deal with their outputs appropriately, e.g. by -134/// passing it to the application or by making a system-level I/O call. You -135/// should call the polling functions in this order: -136/// -137/// 1. [`poll_transmit`](Self::poll_transmit) -138/// 2. [`poll_timeout`](Self::poll_timeout) -139/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events) -140/// 4. [`poll`](Self::poll) -141/// -142/// Currently the only actual dependency is from (2) to (1), however additional -143/// dependencies may be added in future, so the above order is recommended. -144/// -145/// (A) may be called whenever desired. -146/// -147/// Care should be made to ensure that the input events represent monotonically -148/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout) -149/// with events of the same [`Instant`] may be interleaved in any order with a -150/// call to [`handle_event`](Self::handle_event) at that same instant; however -151/// events or timeouts with different instants must not be interleaved. -152pub struct Connection { -153 endpoint_config: Arc<EndpointConfig>, -154 config: Arc<TransportConfig>, -155 rng: StdRng, -156 crypto: Box<dyn crypto::Session>, -157 /// The CID we initially chose, for use during the handshake -158 handshake_cid: ConnectionId, -159 /// The CID the peer initially chose, for use during the handshake -160 rem_handshake_cid: ConnectionId, -161 /// The [`PathData`] for each path -162 /// -163 /// This needs to be ordered because [`Connection::poll_transmit`] needs to -164 /// deterministically select the next PathId to send on. -165 // TODO(flub): well does it really? But deterministic is nice for now. -166 paths: BTreeMap<PathId, PathState>, -167 /// Counter to uniquely identify every [`PathData`] created in this connection. -168 /// -169 /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all -170 /// [`PathData`]s created in the lifetime of this connection. This helps identify the -171 /// correct path when RFC9000-style migrations happen, even when they are -172 /// aborted. -173 /// -174 /// Multipath does not change this, each path can also undergo RFC9000-style -175 /// migrations. So a single multipath path ID could see several [`PathData`]s each with -176 /// their unique [`PathData::generation]. -177 path_generation_counter: u64, -178 /// Whether MTU detection is supported in this environment -179 allow_mtud: bool, -180 state: State, -181 side: ConnectionSide, -182 /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance. -183 zero_rtt_enabled: bool, -184 /// Set if 0-RTT is supported, then cleared when no longer needed. -185 zero_rtt_crypto: Option<ZeroRttCrypto>, -186 key_phase: bool, -187 /// How many packets are in the current key phase. Used only for `Data` space. -188 key_phase_size: u64, -189 /// Transport parameters set by the peer -190 peer_params: TransportParameters, -191 /// Source ConnectionId of the first packet received from the peer -192 orig_rem_cid: ConnectionId, -193 /// Destination ConnectionId sent by the client on the first Initial -194 initial_dst_cid: ConnectionId, -195 /// The value that the server included in the Source Connection ID field of a Retry packet, if -196 /// one was received -197 retry_src_cid: Option<ConnectionId>, -198 /// Events returned by [`Connection::poll`] -199 events: VecDeque<Event>, -200 endpoint_events: VecDeque<EndpointEventInner>, -201 /// Whether the spin bit is in use for this connection -202 spin_enabled: bool, -203 /// Outgoing spin bit state -204 spin: bool, -205 /// Packet number spaces: initial, handshake, 1-RTT -206 spaces: [PacketSpace; 3], -207 /// Highest usable [`SpaceId`] -208 highest_space: SpaceId, -209 /// 1-RTT keys used prior to a key update -210 prev_crypto: Option<PrevCrypto>, -211 /// 1-RTT keys to be used for the next key update -212 /// -213 /// These are generated in advance to prevent timing attacks and/or DoS by third-party attackers -214 /// spoofing key updates. -215 next_crypto: Option<KeyPair<Box<dyn PacketKey>>>, -216 accepted_0rtt: bool, -217 /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted. -218 permit_idle_reset: bool, -219 /// Negotiated idle timeout -220 idle_timeout: Option<Duration>, -221 timers: TimerTable, -222 /// Number of packets received which could not be authenticated -223 authentication_failures: u64, -224 -225 // -226 // Queued non-retransmittable 1-RTT data -227 // -228 /// If the CONNECTION_CLOSE frame needs to be sent -229 close: bool, -230 -231 // -232 // ACK frequency -233 // -234 ack_frequency: AckFrequencyState, -235 -236 // -237 // Congestion Control -238 // -239 /// Whether the most recently received packet had an ECN codepoint set -240 receiving_ecn: bool, -241 /// Number of packets authenticated -242 total_authed_packets: u64, -243 /// Whether the last `poll_transmit` call yielded no data because there was -244 /// no outgoing application data. -245 app_limited: bool, -246 -247 // -248 // ObservedAddr -249 // -250 /// Sequence number for the next observed address frame sent to the peer. -251 next_observed_addr_seq_no: VarInt, -252 -253 streams: StreamsState, -254 /// Surplus remote CIDs for future use on new paths -255 /// -256 /// These are given out before multiple paths exist, also for paths that will never -257 /// exist. So if multipath is supported the number of paths here will be higher than -258 /// the actual number of paths in use. -259 rem_cids: FxHashMap<PathId, CidQueue>, -260 /// Attributes of CIDs generated by local endpoint -261 /// -262 /// Any path that is allowed to be opened is present in this map, as well as the already -263 /// opened paths. However since CIDs are issued async by the endpoint driver via -264 /// connection events it can not be used to know if CIDs have been issued for a path or -265 /// not. See [`Connection::max_path_id_with_cids`] for this. -266 local_cid_state: FxHashMap<PathId, CidState>, -267 /// State of the unreliable datagram extension -268 datagrams: DatagramState, -269 /// Connection level statistics -270 stats: ConnectionStats, -271 /// Path level statistics -272 path_stats: FxHashMap<PathId, PathStats>, -273 /// QUIC version used for the connection. -274 version: u32, -275 -276 // -277 // Multipath -278 // -279 /// Maximum number of concurrent paths -280 /// -281 /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even -282 /// when multipath is disabled this will be set to 1, it is not used in that case -283 /// though. -284 max_concurrent_paths: NonZeroU32, -285 /// Local maximum [`PathId`] to be used -286 /// -287 /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath -288 /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of -289 /// the highest MAX_PATH_ID frame sent. -290 /// -291 /// Any path with an ID equal or below this [`PathId`] is either: -292 /// -293 /// - Abandoned, if it is also in [`Connection::abandoned_paths`]. -294 /// - Open, in this case it is present in [`Connection::paths`] -295 /// - Not yet opened, if it is in neither of these two places. -296 /// -297 /// Note that for not-yet-open there may or may not be any CIDs issued. See -298 /// [`Connection::max_path_id_with_cids`]. -299 local_max_path_id: PathId, -300 /// Remote's maximum [`PathId`] to be used -301 /// -302 /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when -303 /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit -304 /// by sending [`Frame::MaxPathId`] frames. -305 remote_max_path_id: PathId, -306 /// The greatest [`PathId`] we have issued CIDs for -307 /// -308 /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not -309 /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued -310 /// since they are issued asynchronously by the endpoint driver. -311 max_path_id_with_cids: PathId, -312 /// The paths already abandoned -313 /// -314 /// They may still have some state left in [`Connection::paths`] or -315 /// [`Connection::local_cid_state`] since some of this has to be kept around for some -316 /// time after a path is abandoned. -317 // TODO(flub): Make this a more efficient data structure. Like ranges of abandoned -318 // paths. Or a set together with a minimum. Or something. -319 abandoned_paths: FxHashSet<PathId>, -320 -321 iroh_hp: iroh_hp::State, -322 qlog: QlogSink, -323} -324 -325impl Connection { -326 pub(crate) fn new( -327 endpoint_config: Arc<EndpointConfig>, -328 config: Arc<TransportConfig>, -329 init_cid: ConnectionId, -330 loc_cid: ConnectionId, -331 rem_cid: ConnectionId, -332 network_path: FourTuple, -333 crypto: Box<dyn crypto::Session>, -334 cid_gen: &dyn ConnectionIdGenerator, -335 now: Instant, -336 version: u32, -337 allow_mtud: bool, -338 rng_seed: [u8; 32], -339 side_args: SideArgs, -340 qlog: QlogSink, -341 ) -> Self { -342 let pref_addr_cid = side_args.pref_addr_cid(); -343 let path_validated = side_args.path_validated(); -344 let connection_side = ConnectionSide::from(side_args); -345 let side = connection_side.side(); -346 let mut rng = StdRng::from_seed(rng_seed); -347 let initial_space = { -348 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut rng); -349 space.crypto = Some(crypto.initial_keys(init_cid, side)); -350 space -351 }; -352 let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); -353 #[cfg(test)] -354 let data_space = match config.deterministic_packet_numbers { -355 true => PacketSpace::new_deterministic(now, SpaceId::Data), -356 false => PacketSpace::new(now, SpaceId::Data, &mut rng), -357 }; -358 #[cfg(not(test))] -359 let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); -360 let state = State::handshake(state::Handshake { -361 rem_cid_set: side.is_server(), -362 expected_token: Bytes::new(), -363 client_hello: None, -364 allow_server_migration: side.is_client(), -365 }); -366 let local_cid_state = FxHashMap::from_iter([( -367 PathId::ZERO, -368 CidState::new( -369 cid_gen.cid_len(), -370 cid_gen.cid_lifetime(), -371 now, -372 if pref_addr_cid.is_some() { 2 } else { 1 }, -373 ), -374 )]); -375 -376 let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config); -377 // TODO(@divma): consider if we want to delay this until the path is validated -378 path.open = true; -379 let mut this = Self { -380 endpoint_config, -381 crypto, -382 handshake_cid: loc_cid, -383 rem_handshake_cid: rem_cid, -384 local_cid_state, -385 paths: BTreeMap::from_iter([( -386 PathId::ZERO, -387 PathState { -388 data: path, -389 prev: None, -390 }, -391 )]), -392 path_generation_counter: 0, -393 allow_mtud, -394 state, -395 side: connection_side, -396 zero_rtt_enabled: false, -397 zero_rtt_crypto: None, -398 key_phase: false, -399 // A small initial key phase size ensures peers that don't handle key updates correctly -400 // fail sooner rather than later. It's okay for both peers to do this, as the first one -401 // to perform an update will reset the other's key phase size in `update_keys`, and a -402 // simultaneous key update by both is just like a regular key update with a really fast -403 // response. Inspired by quic-go's similar behavior of performing the first key update -404 // at the 100th short-header packet. -405 key_phase_size: rng.random_range(10..1000), -406 peer_params: TransportParameters::default(), -407 orig_rem_cid: rem_cid, -408 initial_dst_cid: init_cid, -409 retry_src_cid: None, -410 events: VecDeque::new(), -411 endpoint_events: VecDeque::new(), -412 spin_enabled: config.allow_spin && rng.random_ratio(7, 8), -413 spin: false, -414 spaces: [initial_space, handshake_space, data_space], -415 highest_space: SpaceId::Initial, -416 prev_crypto: None, -417 next_crypto: None, -418 accepted_0rtt: false, -419 permit_idle_reset: true, -420 idle_timeout: match config.max_idle_timeout { -421 None | Some(VarInt(0)) => None, -422 Some(dur) => Some(Duration::from_millis(dur.0)), -423 }, -424 timers: TimerTable::default(), -425 authentication_failures: 0, -426 close: false, -427 -428 ack_frequency: AckFrequencyState::new(get_max_ack_delay( -429 &TransportParameters::default(), -430 )), -431 -432 app_limited: false, -433 receiving_ecn: false, -434 total_authed_packets: 0, -435 -436 next_observed_addr_seq_no: 0u32.into(), -437 -438 streams: StreamsState::new( -439 side, -440 config.max_concurrent_uni_streams, -441 config.max_concurrent_bidi_streams, -442 config.send_window, -443 config.receive_window, -444 config.stream_receive_window, -445 ), -446 datagrams: DatagramState::default(), -447 config, -448 rem_cids: FxHashMap::from_iter([(PathId::ZERO, CidQueue::new(rem_cid))]), -449 rng, -450 stats: ConnectionStats::default(), -451 path_stats: Default::default(), -452 version, -453 -454 // peer params are not yet known, so multipath is not enabled -455 max_concurrent_paths: NonZeroU32::MIN, -456 local_max_path_id: PathId::ZERO, -457 remote_max_path_id: PathId::ZERO, -458 max_path_id_with_cids: PathId::ZERO, -459 abandoned_paths: Default::default(), -460 -461 // iroh's nat traversal -462 iroh_hp: Default::default(), -463 qlog, -464 }; -465 if path_validated { -466 this.on_path_validated(PathId::ZERO); -467 } -468 if side.is_client() { -469 // Kick off the connection -470 this.write_crypto(); -471 this.init_0rtt(now); -472 } -473 this.qlog -474 .emit_tuple_assigned(PathId::ZERO, network_path, now); -475 this -476 } -477 -478 /// Returns the next time at which `handle_timeout` should be called -479 /// -480 /// The value returned may change after: -481 /// - the application performed some I/O on the connection -482 /// - a call was made to `handle_event` -483 /// - a call to `poll_transmit` returned `Some` -484 /// - a call was made to `handle_timeout` -485 #[must_use] -486 pub fn poll_timeout(&mut self) -> Option<Instant> { -487 self.timers.peek() -488 } -489 -490 /// Returns application-facing events -491 /// -492 /// Connections should be polled for events after: -493 /// - a call was made to `handle_event` -494 /// - a call was made to `handle_timeout` -495 #[must_use] -496 pub fn poll(&mut self) -> Option<Event> { -497 if let Some(x) = self.events.pop_front() { -498 return Some(x); -499 } -500 -501 if let Some(event) = self.streams.poll() { -502 return Some(Event::Stream(event)); -503 } -504 -505 if let Some(reason) = self.state.take_error() { -506 return Some(Event::ConnectionLost { reason }); -507 } -508 -509 None -510 } -511 -512 /// Return endpoint-facing events -513 #[must_use] -514 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { -515 self.endpoint_events.pop_front().map(EndpointEvent) -516 } -517 -518 /// Provide control over streams -519 #[must_use] -520 pub fn streams(&mut self) -> Streams<'_> { -521 Streams { -522 state: &mut self.streams, -523 conn_state: &self.state, -524 } -525 } -526 -527 /// Provide control over streams -528 #[must_use] -529 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { -530 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); -531 RecvStream { -532 id, -533 state: &mut self.streams, -534 pending: &mut self.spaces[SpaceId::Data].pending, -535 } -536 } -537 -538 /// Provide control over streams -539 #[must_use] -540 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { -541 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); -542 SendStream { -543 id, -544 state: &mut self.streams, -545 pending: &mut self.spaces[SpaceId::Data].pending, -546 conn_state: &self.state, -547 } -548 } -549 -550 /// Opens a new path only if no path on the same network path currently exists. -551 /// -552 /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path` -553 /// and pass it existing path's network paths. -554 /// -555 /// This means that you can pass `local_ip: None` to make the comparison only compare -556 /// remote addresses. -557 /// -558 /// This avoids having to guess which local interface will be used to communicate with the -559 /// remote, should it not be known yet. We assume that if we already have a path to the remote, -560 /// the OS is likely to use the same interface to talk to said remote. -561 /// -562 /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id, -563 /// false)` if was opened. -564 /// -565 /// [`open_path`]: Connection::open_path -566 pub fn open_path_ensure( -567 &mut self, -568 network_path: FourTuple, -569 initial_status: PathStatus, -570 now: Instant, -571 ) -> Result<(PathId, bool), PathError> { -572 Ok( -573 match self -574 .paths -575 .iter() -576 .find(|(_id, path)| network_path.is_probably_same_path(&path.data.network_path)) -577 { -578 Some((path_id, _state)) => (*path_id, true), -579 None => (self.open_path(network_path, initial_status, now)?, false), -580 }, -581 ) -582 } -583 -584 /// Opens a new path -585 /// -586 /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events. -587 /// When the path is opened it will be reported as an [`PathEvent::Opened`]. -588 pub fn open_path( -589 &mut self, -590 network_path: FourTuple, -591 initial_status: PathStatus, -592 now: Instant, -593 ) -> Result<PathId, PathError> { -594 if !self.is_multipath_negotiated() { -595 return Err(PathError::MultipathNotNegotiated); -596 } -597 if self.side().is_server() { -598 return Err(PathError::ServerSideNotAllowed); -599 } -600 -601 let max_abandoned = self.abandoned_paths.iter().max().copied(); -602 let max_used = self.paths.keys().last().copied(); -603 let path_id = max_abandoned -604 .max(max_used) -605 .unwrap_or(PathId::ZERO) -606 .saturating_add(1u8); -607 -608 if Some(path_id) > self.max_path_id() { -609 return Err(PathError::MaxPathIdReached); -610 } -611 if path_id > self.remote_max_path_id { -612 self.spaces[SpaceId::Data].pending.paths_blocked = true; -613 return Err(PathError::MaxPathIdReached); -614 } -615 if self.rem_cids.get(&path_id).map(CidQueue::active).is_none() { -616 self.spaces[SpaceId::Data] -617 .pending -618 .path_cids_blocked -619 .insert(path_id); -620 return Err(PathError::RemoteCidsExhausted); -621 } -622 -623 let path = self.ensure_path(path_id, network_path, now, None); -624 path.status.local_update(initial_status); -625 -626 Ok(path_id) -627 } -628 -629 /// Closes a path by sending a PATH_ABANDON frame -630 /// -631 /// This will not allow closing the last path. It does allow closing paths which have -632 /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer -633 /// for a path that was never opened locally. -634 pub fn close_path( -635 &mut self, -636 now: Instant, -637 path_id: PathId, -638 error_code: VarInt, -639 ) -> Result<(), ClosePathError> { -640 if self.abandoned_paths.contains(&path_id) -641 || Some(path_id) > self.max_path_id() -642 || !self.paths.contains_key(&path_id) -643 { -644 return Err(ClosePathError::ClosedPath); -645 } -646 if self -647 .paths -648 .iter() -649 // Would there be any remaining, non-abandoned, validated paths -650 .any(|(id, path)| { -651 *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated -652 }) -653 .not() -654 { -655 return Err(ClosePathError::LastOpenPath); -656 } -657 -658 // Send PATH_ABANDON -659 self.spaces[SpaceId::Data] -660 .pending -661 .path_abandon -662 .insert(path_id, error_code.into()); -663 -664 // Remove pending NEW CIDs for this path -665 let pending_space = &mut self.spaces[SpaceId::Data].pending; -666 pending_space.new_cids.retain(|cid| cid.path_id != path_id); -667 pending_space.path_cids_blocked.retain(|&id| id != path_id); -668 pending_space.path_status.retain(|&id| id != path_id); -669 -670 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) -671 for space in self.spaces[SpaceId::Data].iter_paths_mut() { -672 for sent_packet in space.sent_packets.values_mut() { -673 if let Some(retransmits) = sent_packet.retransmits.get_mut() { -674 retransmits.new_cids.retain(|cid| cid.path_id != path_id); -675 retransmits.path_cids_blocked.retain(|&id| id != path_id); -676 retransmits.path_status.retain(|&id| id != path_id); -677 } -678 } -679 } -680 -681 // Consider remotely issued CIDs as retired. -682 // Technically we don't have to do this just yet. We only need to do this *after* -683 // the ABANDON_PATH frame is sent, allowing us to still send it on the -684 // to-be-abandoned path. However it is recommended to send it on another path, and -685 // we do not allow abandoning the last path anyway. -686 self.rem_cids.remove(&path_id); -687 self.endpoint_events -688 .push_back(EndpointEventInner::RetireResetToken(path_id)); -689 -690 let pto = self.pto_max_path(SpaceId::Data, false); -691 -692 let path = self.paths.get_mut(&path_id).expect("checked above"); -693 -694 // We record the time after which receiving data on this path generates a transport error. -695 path.data.last_allowed_receive = Some(now + 3 * pto); -696 self.abandoned_paths.insert(path_id); -697 -698 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); -699 -700 // The peer MUST respond with a corresponding PATH_ABANDON frame. -701 // If we receive packets on the abandoned path after 3 * PTO we trigger a transport error. -702 // In any case, we completely discard the path after 6 * PTO whether we receive a -703 // PATH_ABANDON or not. -704 self.timers.set( -705 Timer::PerPath(path_id, PathTimer::DiscardPath), -706 now + 6 * pto, -707 self.qlog.with_time(now), -708 ); -709 Ok(()) -710 } -711 -712 /// Gets the [`PathData`] for a known [`PathId`]. -713 /// -714 /// Will panic if the path_id does not reference any known path. -715 #[track_caller] -716 fn path_data(&self, path_id: PathId) -> &PathData { -717 if let Some(data) = self.paths.get(&path_id) { -718 &data.data -719 } else { -720 panic!( -721 "unknown path: {path_id}, currently known paths: {:?}", -722 self.paths.keys().collect::<Vec<_>>() -723 ); -724 } -725 } -726 -727 /// Gets a reference to the [`PathData`] for a [`PathId`] -728 fn path(&self, path_id: PathId) -> Option<&PathData> { -729 self.paths.get(&path_id).map(|path_state| &path_state.data) -730 } -731 -732 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] -733 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { -734 self.paths -735 .get_mut(&path_id) -736 .map(|path_state| &mut path_state.data) -737 } -738 -739 /// Returns all known paths. -740 /// -741 /// There is no guarantee any of these paths are open or usable. -742 pub fn paths(&self) -> Vec<PathId> { -743 self.paths.keys().copied().collect() -744 } -745 -746 /// Gets the local [`PathStatus`] for a known [`PathId`] -747 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { -748 self.path(path_id) -749 .map(PathData::local_status) -750 .ok_or(ClosedPath { _private: () }) -751 } -752 -753 /// Returns the path's network path represented as a 4-tuple. -754 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { -755 self.path(path_id) -756 .map(|path| path.network_path) -757 .ok_or(ClosedPath { _private: () }) -758 } -759 -760 /// Sets the [`PathStatus`] for a known [`PathId`] -761 /// -762 /// Returns the previous path status on success. -763 pub fn set_path_status( -764 &mut self, -765 path_id: PathId, -766 status: PathStatus, -767 ) -> Result<PathStatus, SetPathStatusError> { -768 if !self.is_multipath_negotiated() { -769 return Err(SetPathStatusError::MultipathNotNegotiated); -770 } -771 let path = self -772 .path_mut(path_id) -773 .ok_or(SetPathStatusError::ClosedPath)?; -774 let prev = match path.status.local_update(status) { -775 Some(prev) => { -776 self.spaces[SpaceId::Data] -777 .pending -778 .path_status -779 .insert(path_id); -780 prev -781 } -782 None => path.local_status(), -783 }; -784 Ok(prev) -785 } -786 -787 /// Returns the remote path status -788 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like -789 // this as an API, but for now it allows me to write a test easily. -790 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? -791 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { -792 self.path(path_id).and_then(|path| path.remote_status()) -793 } -794 -795 /// Sets the max_idle_timeout for a specific path -796 /// -797 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. -798 /// -799 /// Returns the previous value of the setting. -800 pub fn set_path_max_idle_timeout( -801 &mut self, -802 path_id: PathId, -803 timeout: Option<Duration>, -804 ) -> Result<Option<Duration>, ClosedPath> { -805 let path = self -806 .paths -807 .get_mut(&path_id) -808 .ok_or(ClosedPath { _private: () })?; -809 Ok(std::mem::replace(&mut path.data.idle_timeout, timeout)) -810 } -811 -812 /// Sets the keep_alive_interval for a specific path -813 /// -814 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. -815 /// -816 /// Returns the previous value of the setting. -817 pub fn set_path_keep_alive_interval( -818 &mut self, -819 path_id: PathId, -820 interval: Option<Duration>, -821 ) -> Result<Option<Duration>, ClosedPath> { -822 let path = self -823 .paths -824 .get_mut(&path_id) -825 .ok_or(ClosedPath { _private: () })?; -826 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) -827 } -828 -829 /// Gets the [`PathData`] for a known [`PathId`]. -830 /// -831 /// Will panic if the path_id does not reference any known path. -832 #[track_caller] -833 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { -834 &mut self.paths.get_mut(&path_id).expect("known path").data -835 } -836 -837 /// Find an open, validated path that's on the same network path as the given network path. -838 /// -839 /// Returns the first path matching, even if there's multiple. -840 fn find_validated_path_on_network_path( -841 &self, -842 network_path: FourTuple, -843 ) -> Option<(&PathId, &PathState)> { -844 self.paths.iter().find(|(path_id, path_state)| { -845 path_state.data.validated -846 // Would this use the same network path, if network_path were used to send right now? -847 && network_path.is_probably_same_path(&path_state.data.network_path) -848 && !self.abandoned_paths.contains(path_id) -849 }) -850 // TODO(@divma): we might want to ensure the path has been recently active to consider the -851 // address validated -852 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? -853 } -854 -855 fn ensure_path( -856 &mut self, -857 path_id: PathId, -858 network_path: FourTuple, -859 now: Instant, -860 pn: Option<u64>, -861 ) -> &mut PathData { -862 let valid_path = self.find_validated_path_on_network_path(network_path); -863 let validated = valid_path.is_some(); -864 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); -865 let vacant_entry = match self.paths.entry(path_id) { -866 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, -867 btree_map::Entry::Occupied(occupied_entry) => { -868 return &mut occupied_entry.into_mut().data; -869 } -870 }; -871 -872 debug!(%validated, %path_id, %network_path, "path added"); -873 let peer_max_udp_payload_size = -874 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -875 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -876 let mut data = PathData::new( -877 network_path, -878 self.allow_mtud, -879 Some(peer_max_udp_payload_size), -880 self.path_generation_counter, -881 now, -882 &self.config, -883 ); -884 -885 data.validated = validated; -886 if let Some(initial_rtt) = initial_rtt { -887 data.rtt.reset_initial_rtt(initial_rtt); -888 } -889 -890 let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base(); -891 self.timers.set( -892 Timer::PerPath(path_id, PathTimer::PathOpen), -893 now + 3 * pto, -894 self.qlog.with_time(now), -895 ); -896 -897 // for the path to be opened we need to send a packet on the path. Sending a challenge -898 // guarantees this -899 data.send_new_challenge = true; -900 -901 let path = vacant_entry.insert(PathState { data, prev: None }); -902 -903 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); -904 if let Some(pn) = pn { -905 pn_space.dedup.insert(pn); -906 } -907 self.spaces[SpaceId::Data] -908 .number_spaces -909 .insert(path_id, pn_space); -910 self.qlog.emit_tuple_assigned(path_id, network_path, now); -911 &mut path.data -912 } -913 -914 /// Returns packets to transmit -915 /// -916 /// Connections should be polled for transmit after: -917 /// - the application performed some I/O on the connection -918 /// - a call was made to `handle_event` -919 /// - a call was made to `handle_timeout` -920 /// -921 /// `max_datagrams` specifies how many datagrams can be returned inside a -922 /// single Transmit using GSO. This must be at least 1. -923 #[must_use] -924 pub fn poll_transmit( -925 &mut self, -926 now: Instant, -927 max_datagrams: NonZeroUsize, -928 buf: &mut Vec<u8>, -929 ) -> Option<Transmit> { -930 if let Some(probing) = self -931 .iroh_hp -932 .server_side_mut() -933 .ok() -934 .and_then(iroh_hp::ServerState::next_probe) -935 { -936 let destination = probing.remote(); -937 trace!(%destination, "RAND_DATA packet"); -938 let token: u64 = self.rng.random(); -939 buf.put_u64(token); -940 probing.finish(token); -941 return Some(Transmit { -942 destination, -943 ecn: None, -944 size: 8, -945 segment_size: None, -946 src_ip: None, -947 }); -948 } -949 -950 let max_datagrams = match self.config.enable_segmentation_offload { -951 false => NonZeroUsize::MIN, -952 true => max_datagrams, -953 }; -954 -955 // Each call to poll_transmit can only send datagrams to one destination, because -956 // all datagrams in a GSO batch are for the same destination. Therefore only -957 // datagrams for one Path ID are produced for each poll_transmit call. -958 -959 // TODO(flub): this is wishful thinking and not actually implemented, but perhaps it -960 // should be: -961 -962 // First, if we have to send a close, select a path for that. -963 // Next, all paths that have a PATH_CHALLENGE or PATH_RESPONSE pending. -964 -965 // For all, open, validated and AVAILABLE paths: -966 // - Is the path congestion blocked or pacing blocked? -967 // - call maybe_queue_ to ensure a tail-loss probe would be sent? -968 // - do we need to send a close message? -969 // - call can_send -970 // Once there's nothing more to send on the AVAILABLE paths, do the same for BACKUP paths -971 -972 // Check whether we need to send a close message -973 let close = match self.state.as_type() { -974 StateType::Drained => { -975 self.app_limited = true; -976 return None; -977 } -978 StateType::Draining | StateType::Closed => { -979 // self.close is only reset once the associated packet had been -980 // encoded successfully -981 if !self.close { -982 self.app_limited = true; -983 return None; -984 } -985 true -986 } -987 _ => false, -988 }; -989 -990 // Check whether we need to send an ACK_FREQUENCY frame -991 if let Some(config) = &self.config.ack_frequency_config { -992 let rtt = self -993 .paths -994 .values() -995 .map(|p| p.data.rtt.get()) -996 .min() -997 .expect("one path exists"); -998 self.spaces[SpaceId::Data].pending.ack_frequency = self -999 .ack_frequency -1000 .should_send_ack_frequency(rtt, config, &self.peer_params) -1001 && self.highest_space == SpaceId::Data -1002 && self.peer_supports_ack_frequency(); -1003 } -1004 -1005 // Whether this packet can be coalesced with another one in the same datagram. -1006 let mut coalesce = true; -1007 -1008 // Whether the last packet in the datagram must be padded so the datagram takes up -1009 // to at least MIN_INITIAL_SIZE, or to the maximum segment size if this is smaller. -1010 let mut pad_datagram = PadDatagram::No; -1011 -1012 // Whether congestion control stopped the next packet from being sent. Further -1013 // packets could still be built, as e.g. tail-loss probes are not congestion -1014 // limited. -1015 let mut congestion_blocked = false; -1016 -1017 // The packet number of the last built packet. -1018 let mut last_packet_number = None; -1019 -1020 let mut path_id = *self.paths.first_key_value().expect("one path must exist").0; -1021 -1022 // If there is any open, validated and available path we only want to send frames to -1023 // any backup path that must be sent on that backup path exclusively. -1024 let have_available_path = self.paths.iter().any(|(id, path)| { -1025 path.data.validated -1026 && path.data.local_status() == PathStatus::Available -1027 && self.rem_cids.contains_key(id) -1028 }); -1029 -1030 // Setup for the first path_id -1031 let mut transmit = TransmitBuf::new( -1032 buf, -1033 max_datagrams, -1034 self.path_data(path_id).current_mtu().into(), -1035 ); -1036 if let Some(challenge) = self.send_prev_path_challenge(now, &mut transmit, path_id) { -1037 return Some(challenge); -1038 } -1039 let mut space_id = match path_id { -1040 PathId::ZERO => SpaceId::Initial, -1041 _ => SpaceId::Data, -1042 }; -1043 -1044 loop { -1045 // check if there is at least one active CID to use for sending -1046 let Some(remote_cid) = self.rem_cids.get(&path_id).map(CidQueue::active) else { -1047 let err = PathError::RemoteCidsExhausted; -1048 if !self.abandoned_paths.contains(&path_id) { -1049 debug!(?err, %path_id, "no active CID for path"); -1050 self.events.push_back(Event::Path(PathEvent::LocallyClosed { -1051 id: path_id, -1052 error: err, -1053 })); -1054 // Locally we should have refused to open this path, the remote should -1055 // have given us CIDs for this path before opening it. So we can always -1056 // abandon this here. -1057 self.close_path( -1058 now, -1059 path_id, -1060 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH.into(), -1061 ) -1062 .ok(); -1063 self.spaces[SpaceId::Data] -1064 .pending -1065 .path_cids_blocked -1066 .insert(path_id); -1067 } else { -1068 trace!(%path_id, "remote CIDs retired for abandoned path"); -1069 } -1070 -1071 match self.paths.keys().find(|&&next| next > path_id) { -1072 Some(next_path_id) => { -1073 // See if this next path can send anything. -1074 path_id = *next_path_id; -1075 space_id = SpaceId::Data; -1076 -1077 // update per path state -1078 transmit.set_segment_size(self.path_data(path_id).current_mtu().into()); -1079 if let Some(challenge) = -1080 self.send_prev_path_challenge(now, &mut transmit, path_id) -1081 { -1082 return Some(challenge); -1083 } -1084 -1085 continue; -1086 } -1087 None => { -1088 // Nothing more to send. -1089 trace!( -1090 ?space_id, -1091 %path_id, -1092 "no CIDs to send on path, no more paths" -1093 ); -1094 break; -1095 } -1096 } -1097 }; -1098 -1099 // Determine if anything can be sent in this packet number space (SpaceId + -1100 // PathId). -1101 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { -1102 // We are trying to coalesce another packet into this datagram. -1103 transmit.datagram_remaining_mut() -1104 } else { -1105 // A new datagram needs to be started. -1106 transmit.segment_size() -1107 }; -1108 let can_send = self.space_can_send(space_id, path_id, max_packet_size, close); -1109 let path_should_send = { -1110 let path_exclusive_only = space_id == SpaceId::Data -1111 && have_available_path -1112 && self.path_data(path_id).local_status() == PathStatus::Backup; -1113 let path_should_send = if path_exclusive_only { -1114 can_send.path_exclusive -1115 } else { -1116 !can_send.is_empty() -1117 }; -1118 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1119 path_should_send || needs_loss_probe -1120 }; -1121 -1122 if !path_should_send && space_id < SpaceId::Data { -1123 if self.spaces[space_id].crypto.is_some() { -1124 trace!(?space_id, %path_id, "nothing to send in space"); -1125 } -1126 space_id = space_id.next(); -1127 continue; -1128 } -1129 -1130 let send_blocked = if path_should_send && transmit.datagram_remaining_mut() == 0 { -1131 // Only check congestion control if a new datagram is needed. -1132 self.path_congestion_check(space_id, path_id, &transmit, &can_send, now) -1133 } else { -1134 PathBlocked::No -1135 }; -1136 if send_blocked != PathBlocked::No { -1137 trace!(?space_id, %path_id, ?send_blocked, "congestion blocked"); -1138 congestion_blocked = true; -1139 } -1140 if send_blocked != PathBlocked::No && space_id < SpaceId::Data { -1141 // Higher spaces might still have tail-loss probes to send, which are not -1142 // congestion blocked. -1143 space_id = space_id.next(); -1144 continue; -1145 } -1146 if !path_should_send || send_blocked != PathBlocked::No { -1147 // Nothing more to send on this path, check the next path if possible. -1148 -1149 // If there are any datagrams in the transmit, packets for another path can -1150 // not be built. -1151 if transmit.num_datagrams() > 0 { -1152 break; -1153 } -1154 -1155 match self.paths.keys().find(|&&next| next > path_id) { -1156 Some(next_path_id) => { -1157 // See if this next path can send anything. -1158 trace!( -1159 ?space_id, -1160 %path_id, -1161 %next_path_id, -1162 "nothing to send on path" -1163 ); -1164 path_id = *next_path_id; -1165 space_id = SpaceId::Data; -1166 -1167 // update per path state -1168 transmit.set_segment_size(self.path_data(path_id).current_mtu().into()); -1169 if let Some(challenge) = -1170 self.send_prev_path_challenge(now, &mut transmit, path_id) -1171 { -1172 return Some(challenge); -1173 } -1174 -1175 continue; -1176 } -1177 None => { -1178 // Nothing more to send. -1179 trace!( -1180 ?space_id, -1181 %path_id, -1182 next_path_id=?None::<PathId>, -1183 "nothing to send on path" -1184 ); -1185 break; -1186 } -1187 } -1188 } -1189 -1190 // If the datagram is full, we need to start a new one. -1191 if transmit.datagram_remaining_mut() == 0 { -1192 if transmit.num_datagrams() >= transmit.max_datagrams().get() { -1193 // No more datagrams allowed -1194 break; -1195 } -1196 -1197 match self.spaces[space_id].for_path(path_id).loss_probes { -1198 0 => transmit.start_new_datagram(), -1199 _ => { -1200 // We need something to send for a tail-loss probe. -1201 let request_immediate_ack = -1202 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); -1203 self.spaces[space_id].maybe_queue_probe( -1204 path_id, -1205 request_immediate_ack, -1206 &self.streams, -1207 ); -1208 -1209 self.spaces[space_id].for_path(path_id).loss_probes -= 1; -1210 -1211 // Clamp the datagram to at most the minimum MTU to ensure that loss -1212 // probes can get through and enable recovery even if the path MTU -1213 // has shrank unexpectedly. -1214 transmit.start_new_datagram_with_size(std::cmp::min( -1215 usize::from(INITIAL_MTU), -1216 transmit.segment_size(), -1217 )); -1218 } -1219 } -1220 trace!(count = transmit.num_datagrams(), "new datagram started"); -1221 coalesce = true; -1222 pad_datagram = PadDatagram::No; -1223 } -1224 -1225 // If coalescing another packet into the existing datagram, there should -1226 // still be enough space for a whole packet. -1227 if transmit.datagram_start_offset() < transmit.len() { -1228 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); -1229 } -1230 -1231 // -1232 // From here on, we've determined that a packet will definitely be sent. -1233 // -1234 -1235 if self.spaces[SpaceId::Initial].crypto.is_some() -1236 && space_id == SpaceId::Handshake -1237 && self.side.is_client() -1238 { -1239 // A client stops both sending and processing Initial packets when it -1240 // sends its first Handshake packet. -1241 self.discard_space(now, SpaceId::Initial); -1242 } -1243 if let Some(ref mut prev) = self.prev_crypto { -1244 prev.update_unacked = false; -1245 } -1246 -1247 let mut qlog = QlogSentPacket::default(); -1248 let mut builder = PacketBuilder::new( -1249 now, -1250 space_id, -1251 path_id, -1252 remote_cid, -1253 &mut transmit, -1254 can_send.other, -1255 self, -1256 &mut qlog, -1257 )?; -1258 last_packet_number = Some(builder.exact_number); -1259 coalesce = coalesce && !builder.short_header; -1260 -1261 if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) { -1262 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 -1263 pad_datagram |= PadDatagram::ToMinMtu; +26 config::{ServerConfig, TransportConfig}, +27 congestion::Controller, +28 connection::{ +29 qlog::{QlogRecvPacket, QlogSink}, +30 spaces::LostPacket, +31 timer::{ConnTimer, PathTimer}, +32 }, +33 crypto::{self, KeyPair, Keys, PacketKey}, +34 frame::{self, Close, Datagram, FrameStruct, NewToken, ObservedAddr}, +35 iroh_hp, +36 packet::{ +37 FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, LongType, Packet, +38 PacketNumber, PartialDecode, SpaceId, +39 }, +40 range_set::ArrayRangeSet, +41 shared::{ +42 ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint, +43 EndpointEvent, EndpointEventInner, +44 }, +45 token::{ResetToken, Token, TokenPayload}, +46 transport_parameters::TransportParameters, +47}; +48 +49mod ack_frequency; +50use ack_frequency::AckFrequencyState; +51 +52mod assembler; +53pub use assembler::Chunk; +54 +55mod cid_state; +56use cid_state::CidState; +57 +58mod datagrams; +59use datagrams::DatagramState; +60pub use datagrams::{Datagrams, SendDatagramError}; +61 +62mod mtud; +63mod pacing; +64 +65mod packet_builder; +66use packet_builder::{PacketBuilder, PadDatagram}; +67 +68mod packet_crypto; +69use packet_crypto::{PrevCrypto, ZeroRttCrypto}; +70 +71mod paths; +72pub use paths::{ClosedPath, PathEvent, PathId, PathStatus, RttEstimator, SetPathStatusError}; +73use paths::{PathData, PathState}; +74 +75pub(crate) mod qlog; +76pub(crate) mod send_buffer; +77 +78mod spaces; +79#[cfg(fuzzing)] +80pub use spaces::Retransmits; +81#[cfg(not(fuzzing))] +82use spaces::Retransmits; +83use spaces::{PacketSpace, SendableFrames, SentPacket, ThinRetransmits}; +84 +85mod stats; +86pub use stats::{ConnectionStats, FrameStats, PathStats, UdpStats}; +87 +88mod streams; +89#[cfg(fuzzing)] +90pub use streams::StreamsState; +91#[cfg(not(fuzzing))] +92use streams::StreamsState; +93pub use streams::{ +94 Chunks, ClosedStream, FinishError, ReadError, ReadableError, RecvStream, SendStream, +95 ShouldTransmit, StreamEvent, Streams, WriteError, Written, +96}; +97 +98mod timer; +99use timer::{Timer, TimerTable}; +100 +101mod transmit_buf; +102use transmit_buf::TransmitBuf; +103 +104mod state; +105 +106#[cfg(not(fuzzing))] +107use state::State; +108#[cfg(fuzzing)] +109pub use state::State; +110use state::StateType; +111 +112/// Protocol state and logic for a single QUIC connection +113/// +114/// Objects of this type receive [`ConnectionEvent`]s and emit [`EndpointEvent`]s and application +115/// [`Event`]s to make progress. To handle timeouts, a `Connection` returns timer updates and +116/// expects timeouts through various methods. A number of simple getter methods are exposed +117/// to allow callers to inspect some of the connection state. +118/// +119/// `Connection` has roughly 4 types of methods: +120/// +121/// - A. Simple getters, taking `&self` +122/// - B. Handlers for incoming events from the network or system, named `handle_*`. +123/// - C. State machine mutators, for incoming commands from the application. For convenience we +124/// refer to this as "performing I/O" below, however as per the design of this library none of the +125/// functions actually perform system-level I/O. For example, [`read`](RecvStream::read) and +126/// [`write`](SendStream::write), but also things like [`reset`](SendStream::reset). +127/// - D. Polling functions for outgoing events or actions for the caller to +128/// take, named `poll_*`. +129/// +130/// The simplest way to use this API correctly is to call (B) and (C) whenever +131/// appropriate, then after each of those calls, as soon as feasible call all +132/// polling methods (D) and deal with their outputs appropriately, e.g. by +133/// passing it to the application or by making a system-level I/O call. You +134/// should call the polling functions in this order: +135/// +136/// 1. [`poll_transmit`](Self::poll_transmit) +137/// 2. [`poll_timeout`](Self::poll_timeout) +138/// 3. [`poll_endpoint_events`](Self::poll_endpoint_events) +139/// 4. [`poll`](Self::poll) +140/// +141/// Currently the only actual dependency is from (2) to (1), however additional +142/// dependencies may be added in future, so the above order is recommended. +143/// +144/// (A) may be called whenever desired. +145/// +146/// Care should be made to ensure that the input events represent monotonically +147/// increasing time. Specifically, calling [`handle_timeout`](Self::handle_timeout) +148/// with events of the same [`Instant`] may be interleaved in any order with a +149/// call to [`handle_event`](Self::handle_event) at that same instant; however +150/// events or timeouts with different instants must not be interleaved. +151pub struct Connection { +152 endpoint_config: Arc<EndpointConfig>, +153 config: Arc<TransportConfig>, +154 rng: StdRng, +155 crypto: Box<dyn crypto::Session>, +156 /// The CID we initially chose, for use during the handshake +157 handshake_cid: ConnectionId, +158 /// The CID the peer initially chose, for use during the handshake +159 rem_handshake_cid: ConnectionId, +160 /// The [`PathData`] for each path +161 /// +162 /// This needs to be ordered because [`Connection::poll_transmit`] needs to +163 /// deterministically select the next PathId to send on. +164 // TODO(flub): well does it really? But deterministic is nice for now. +165 paths: BTreeMap<PathId, PathState>, +166 /// Counter to uniquely identify every [`PathData`] created in this connection. +167 /// +168 /// Each [`PathData`] gets a [`PathData::generation`] that is unique among all +169 /// [`PathData`]s created in the lifetime of this connection. This helps identify the +170 /// correct path when RFC9000-style migrations happen, even when they are +171 /// aborted. +172 /// +173 /// Multipath does not change this, each path can also undergo RFC9000-style +174 /// migrations. So a single multipath path ID could see several [`PathData`]s each with +175 /// their unique [`PathData::generation]. +176 path_generation_counter: u64, +177 /// Whether MTU detection is supported in this environment +178 allow_mtud: bool, +179 state: State, +180 side: ConnectionSide, +181 /// Whether or not 0-RTT was enabled during the handshake. Does not imply acceptance. +182 zero_rtt_enabled: bool, +183 /// Set if 0-RTT is supported, then cleared when no longer needed. +184 zero_rtt_crypto: Option<ZeroRttCrypto>, +185 key_phase: bool, +186 /// How many packets are in the current key phase. Used only for `Data` space. +187 key_phase_size: u64, +188 /// Transport parameters set by the peer +189 peer_params: TransportParameters, +190 /// Source ConnectionId of the first packet received from the peer +191 orig_rem_cid: ConnectionId, +192 /// Destination ConnectionId sent by the client on the first Initial +193 initial_dst_cid: ConnectionId, +194 /// The value that the server included in the Source Connection ID field of a Retry packet, if +195 /// one was received +196 retry_src_cid: Option<ConnectionId>, +197 /// Events returned by [`Connection::poll`] +198 events: VecDeque<Event>, +199 endpoint_events: VecDeque<EndpointEventInner>, +200 /// Whether the spin bit is in use for this connection +201 spin_enabled: bool, +202 /// Outgoing spin bit state +203 spin: bool, +204 /// Packet number spaces: initial, handshake, 1-RTT +205 spaces: [PacketSpace; 3], +206 /// Highest usable [`SpaceId`] +207 highest_space: SpaceId, +208 /// 1-RTT keys used prior to a key update +209 prev_crypto: Option<PrevCrypto>, +210 /// 1-RTT keys to be used for the next key update +211 /// +212 /// These are generated in advance to prevent timing attacks and/or DoS by third-party attackers +213 /// spoofing key updates. +214 next_crypto: Option<KeyPair<Box<dyn PacketKey>>>, +215 accepted_0rtt: bool, +216 /// Whether the idle timer should be reset the next time an ack-eliciting packet is transmitted. +217 permit_idle_reset: bool, +218 /// Negotiated idle timeout +219 idle_timeout: Option<Duration>, +220 timers: TimerTable, +221 /// Number of packets received which could not be authenticated +222 authentication_failures: u64, +223 +224 // +225 // Queued non-retransmittable 1-RTT data +226 // +227 /// If the CONNECTION_CLOSE frame needs to be sent +228 close: bool, +229 +230 // +231 // ACK frequency +232 // +233 ack_frequency: AckFrequencyState, +234 +235 // +236 // Congestion Control +237 // +238 /// Whether the most recently received packet had an ECN codepoint set +239 receiving_ecn: bool, +240 /// Number of packets authenticated +241 total_authed_packets: u64, +242 /// Whether the last `poll_transmit` call yielded no data because there was +243 /// no outgoing application data. +244 app_limited: bool, +245 +246 // +247 // ObservedAddr +248 // +249 /// Sequence number for the next observed address frame sent to the peer. +250 next_observed_addr_seq_no: VarInt, +251 +252 streams: StreamsState, +253 /// Surplus remote CIDs for future use on new paths +254 /// +255 /// These are given out before multiple paths exist, also for paths that will never +256 /// exist. So if multipath is supported the number of paths here will be higher than +257 /// the actual number of paths in use. +258 rem_cids: FxHashMap<PathId, CidQueue>, +259 /// Attributes of CIDs generated by local endpoint +260 /// +261 /// Any path that is allowed to be opened is present in this map, as well as the already +262 /// opened paths. However since CIDs are issued async by the endpoint driver via +263 /// connection events it can not be used to know if CIDs have been issued for a path or +264 /// not. See [`Connection::max_path_id_with_cids`] for this. +265 local_cid_state: FxHashMap<PathId, CidState>, +266 /// State of the unreliable datagram extension +267 datagrams: DatagramState, +268 /// Connection level statistics +269 stats: ConnectionStats, +270 /// Path level statistics +271 path_stats: FxHashMap<PathId, PathStats>, +272 /// QUIC version used for the connection. +273 version: u32, +274 +275 // +276 // Multipath +277 // +278 /// Maximum number of concurrent paths +279 /// +280 /// Initially set from the [`TransportConfig::max_concurrent_multipath_paths`]. Even +281 /// when multipath is disabled this will be set to 1, it is not used in that case +282 /// though. +283 max_concurrent_paths: NonZeroU32, +284 /// Local maximum [`PathId`] to be used +285 /// +286 /// This is initially set to [`TransportConfig::get_initial_max_path_id`] when multipath +287 /// is negotiated, or to [`PathId::ZERO`] otherwise. This is essentially the value of +288 /// the highest MAX_PATH_ID frame sent. +289 /// +290 /// Any path with an ID equal or below this [`PathId`] is either: +291 /// +292 /// - Abandoned, if it is also in [`Connection::abandoned_paths`]. +293 /// - Open, in this case it is present in [`Connection::paths`] +294 /// - Not yet opened, if it is in neither of these two places. +295 /// +296 /// Note that for not-yet-open there may or may not be any CIDs issued. See +297 /// [`Connection::max_path_id_with_cids`]. +298 local_max_path_id: PathId, +299 /// Remote's maximum [`PathId`] to be used +300 /// +301 /// This is initially set to the peer's [`TransportParameters::initial_max_path_id`] when +302 /// multipath is negotiated, or to [`PathId::ZERO`] otherwise. A peer may increase this limit +303 /// by sending [`Frame::MaxPathId`] frames. +304 remote_max_path_id: PathId, +305 /// The greatest [`PathId`] we have issued CIDs for +306 /// +307 /// CIDs are only issued for `min(local_max_path_id, remote_max_path_id)`. It is not +308 /// possible to use [`Connection::local_cid_state`] to know if CIDs have been issued +309 /// since they are issued asynchronously by the endpoint driver. +310 max_path_id_with_cids: PathId, +311 /// The paths already abandoned +312 /// +313 /// They may still have some state left in [`Connection::paths`] or +314 /// [`Connection::local_cid_state`] since some of this has to be kept around for some +315 /// time after a path is abandoned. +316 // TODO(flub): Make this a more efficient data structure. Like ranges of abandoned +317 // paths. Or a set together with a minimum. Or something. +318 abandoned_paths: FxHashSet<PathId>, +319 +320 iroh_hp: iroh_hp::State, +321 qlog: QlogSink, +322} +323 +324impl Connection { +325 pub(crate) fn new( +326 endpoint_config: Arc<EndpointConfig>, +327 config: Arc<TransportConfig>, +328 init_cid: ConnectionId, +329 loc_cid: ConnectionId, +330 rem_cid: ConnectionId, +331 network_path: FourTuple, +332 crypto: Box<dyn crypto::Session>, +333 cid_gen: &dyn ConnectionIdGenerator, +334 now: Instant, +335 version: u32, +336 allow_mtud: bool, +337 rng_seed: [u8; 32], +338 side_args: SideArgs, +339 qlog: QlogSink, +340 ) -> Self { +341 let pref_addr_cid = side_args.pref_addr_cid(); +342 let path_validated = side_args.path_validated(); +343 let connection_side = ConnectionSide::from(side_args); +344 let side = connection_side.side(); +345 let mut rng = StdRng::from_seed(rng_seed); +346 let initial_space = { +347 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut rng); +348 space.crypto = Some(crypto.initial_keys(init_cid, side)); +349 space +350 }; +351 let handshake_space = PacketSpace::new(now, SpaceId::Handshake, &mut rng); +352 #[cfg(test)] +353 let data_space = match config.deterministic_packet_numbers { +354 true => PacketSpace::new_deterministic(now, SpaceId::Data), +355 false => PacketSpace::new(now, SpaceId::Data, &mut rng), +356 }; +357 #[cfg(not(test))] +358 let data_space = PacketSpace::new(now, SpaceId::Data, &mut rng); +359 let state = State::handshake(state::Handshake { +360 rem_cid_set: side.is_server(), +361 expected_token: Bytes::new(), +362 client_hello: None, +363 allow_server_migration: side.is_client(), +364 }); +365 let local_cid_state = FxHashMap::from_iter([( +366 PathId::ZERO, +367 CidState::new( +368 cid_gen.cid_len(), +369 cid_gen.cid_lifetime(), +370 now, +371 if pref_addr_cid.is_some() { 2 } else { 1 }, +372 ), +373 )]); +374 +375 let mut path = PathData::new(network_path, allow_mtud, None, 0, now, &config); +376 // TODO(@divma): consider if we want to delay this until the path is validated +377 path.open = true; +378 let mut this = Self { +379 endpoint_config, +380 crypto, +381 handshake_cid: loc_cid, +382 rem_handshake_cid: rem_cid, +383 local_cid_state, +384 paths: BTreeMap::from_iter([( +385 PathId::ZERO, +386 PathState { +387 data: path, +388 prev: None, +389 }, +390 )]), +391 path_generation_counter: 0, +392 allow_mtud, +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 +473 .emit_tuple_assigned(PathId::ZERO, network_path, now); +474 this +475 } +476 +477 /// Returns the next time at which `handle_timeout` should be called +478 /// +479 /// The value returned may change after: +480 /// - the application performed some I/O on the connection +481 /// - a call was made to `handle_event` +482 /// - a call to `poll_transmit` returned `Some` +483 /// - a call was made to `handle_timeout` +484 #[must_use] +485 pub fn poll_timeout(&mut self) -> Option<Instant> { +486 self.timers.peek() +487 } +488 +489 /// Returns application-facing events +490 /// +491 /// Connections should be polled for events after: +492 /// - a call was made to `handle_event` +493 /// - a call was made to `handle_timeout` +494 #[must_use] +495 pub fn poll(&mut self) -> Option<Event> { +496 if let Some(x) = self.events.pop_front() { +497 return Some(x); +498 } +499 +500 if let Some(event) = self.streams.poll() { +501 return Some(Event::Stream(event)); +502 } +503 +504 if let Some(reason) = self.state.take_error() { +505 return Some(Event::ConnectionLost { reason }); +506 } +507 +508 None +509 } +510 +511 /// Return endpoint-facing events +512 #[must_use] +513 pub fn poll_endpoint_events(&mut self) -> Option<EndpointEvent> { +514 self.endpoint_events.pop_front().map(EndpointEvent) +515 } +516 +517 /// Provide control over streams +518 #[must_use] +519 pub fn streams(&mut self) -> Streams<'_> { +520 Streams { +521 state: &mut self.streams, +522 conn_state: &self.state, +523 } +524 } +525 +526 /// Provide control over streams +527 #[must_use] +528 pub fn recv_stream(&mut self, id: StreamId) -> RecvStream<'_> { +529 assert!(id.dir() == Dir::Bi || id.initiator() != self.side.side()); +530 RecvStream { +531 id, +532 state: &mut self.streams, +533 pending: &mut self.spaces[SpaceId::Data].pending, +534 } +535 } +536 +537 /// Provide control over streams +538 #[must_use] +539 pub fn send_stream(&mut self, id: StreamId) -> SendStream<'_> { +540 assert!(id.dir() == Dir::Bi || id.initiator() == self.side.side()); +541 SendStream { +542 id, +543 state: &mut self.streams, +544 pending: &mut self.spaces[SpaceId::Data].pending, +545 conn_state: &self.state, +546 } +547 } +548 +549 /// Opens a new path only if no path on the same network path currently exists. +550 /// +551 /// This comparison will use [`FourTuple::is_probably_same_path`] on the given `network_path` +552 /// and pass it existing path's network paths. +553 /// +554 /// This means that you can pass `local_ip: None` to make the comparison only compare +555 /// remote addresses. +556 /// +557 /// This avoids having to guess which local interface will be used to communicate with the +558 /// remote, should it not be known yet. We assume that if we already have a path to the remote, +559 /// the OS is likely to use the same interface to talk to said remote. +560 /// +561 /// See also [`open_path`]. Returns `(path_id, true)` if the path already existed. `(path_id, +562 /// false)` if was opened. +563 /// +564 /// [`open_path`]: Connection::open_path +565 pub fn open_path_ensure( +566 &mut self, +567 network_path: FourTuple, +568 initial_status: PathStatus, +569 now: Instant, +570 ) -> Result<(PathId, bool), PathError> { +571 Ok( +572 match self +573 .paths +574 .iter() +575 .find(|(_id, path)| network_path.is_probably_same_path(&path.data.network_path)) +576 { +577 Some((path_id, _state)) => (*path_id, true), +578 None => (self.open_path(network_path, initial_status, now)?, false), +579 }, +580 ) +581 } +582 +583 /// Opens a new path +584 /// +585 /// Further errors might occur and they will be emitted in [`PathEvent::LocallyClosed`] events. +586 /// When the path is opened it will be reported as an [`PathEvent::Opened`]. +587 pub fn open_path( +588 &mut self, +589 network_path: FourTuple, +590 initial_status: PathStatus, +591 now: Instant, +592 ) -> Result<PathId, PathError> { +593 if !self.is_multipath_negotiated() { +594 return Err(PathError::MultipathNotNegotiated); +595 } +596 if self.side().is_server() { +597 return Err(PathError::ServerSideNotAllowed); +598 } +599 +600 let max_abandoned = self.abandoned_paths.iter().max().copied(); +601 let max_used = self.paths.keys().last().copied(); +602 let path_id = max_abandoned +603 .max(max_used) +604 .unwrap_or(PathId::ZERO) +605 .saturating_add(1u8); +606 +607 if Some(path_id) > self.max_path_id() { +608 return Err(PathError::MaxPathIdReached); +609 } +610 if path_id > self.remote_max_path_id { +611 self.spaces[SpaceId::Data].pending.paths_blocked = true; +612 return Err(PathError::MaxPathIdReached); +613 } +614 if self.rem_cids.get(&path_id).map(CidQueue::active).is_none() { +615 self.spaces[SpaceId::Data] +616 .pending +617 .path_cids_blocked +618 .insert(path_id); +619 return Err(PathError::RemoteCidsExhausted); +620 } +621 +622 let path = self.ensure_path(path_id, network_path, now, None); +623 path.status.local_update(initial_status); +624 +625 Ok(path_id) +626 } +627 +628 /// Closes a path by sending a PATH_ABANDON frame +629 /// +630 /// This will not allow closing the last path. It does allow closing paths which have +631 /// not yet been opened, as e.g. is the case when receiving a PATH_ABANDON from the peer +632 /// for a path that was never opened locally. +633 pub fn close_path( +634 &mut self, +635 now: Instant, +636 path_id: PathId, +637 error_code: VarInt, +638 ) -> Result<(), ClosePathError> { +639 if self.abandoned_paths.contains(&path_id) +640 || Some(path_id) > self.max_path_id() +641 || !self.paths.contains_key(&path_id) +642 { +643 return Err(ClosePathError::ClosedPath); +644 } +645 if self +646 .paths +647 .iter() +648 // Would there be any remaining, non-abandoned, validated paths +649 .any(|(id, path)| { +650 *id != path_id && !self.abandoned_paths.contains(id) && path.data.validated +651 }) +652 .not() +653 { +654 return Err(ClosePathError::LastOpenPath); +655 } +656 +657 // Send PATH_ABANDON +658 self.spaces[SpaceId::Data] +659 .pending +660 .path_abandon +661 .insert(path_id, error_code.into()); +662 +663 // Remove pending NEW CIDs for this path +664 let pending_space = &mut self.spaces[SpaceId::Data].pending; +665 pending_space.new_cids.retain(|cid| cid.path_id != path_id); +666 pending_space.path_cids_blocked.retain(|&id| id != path_id); +667 pending_space.path_status.retain(|&id| id != path_id); +668 +669 // Cleanup retransmits across ALL paths (CIDs for path_id may have been transmitted on other paths) +670 for space in self.spaces[SpaceId::Data].iter_paths_mut() { +671 for sent_packet in space.sent_packets.values_mut() { +672 if let Some(retransmits) = sent_packet.retransmits.get_mut() { +673 retransmits.new_cids.retain(|cid| cid.path_id != path_id); +674 retransmits.path_cids_blocked.retain(|&id| id != path_id); +675 retransmits.path_status.retain(|&id| id != path_id); +676 } +677 } +678 } +679 +680 // Consider remotely issued CIDs as retired. +681 // Technically we don't have to do this just yet. We only need to do this *after* +682 // the ABANDON_PATH frame is sent, allowing us to still send it on the +683 // to-be-abandoned path. However it is recommended to send it on another path, and +684 // we do not allow abandoning the last path anyway. +685 self.rem_cids.remove(&path_id); +686 self.endpoint_events +687 .push_back(EndpointEventInner::RetireResetToken(path_id)); +688 +689 let pto = self.pto_max_path(SpaceId::Data, false); +690 +691 let path = self.paths.get_mut(&path_id).expect("checked above"); +692 +693 // We record the time after which receiving data on this path generates a transport error. +694 path.data.last_allowed_receive = Some(now + 3 * pto); +695 self.abandoned_paths.insert(path_id); +696 +697 self.set_max_path_id(now, self.local_max_path_id.saturating_add(1u8)); +698 +699 // The peer MUST respond with a corresponding PATH_ABANDON frame. +700 // If we receive packets on the abandoned path after 3 * PTO we trigger a transport error. +701 // In any case, we completely discard the path after 6 * PTO whether we receive a +702 // PATH_ABANDON or not. +703 self.timers.set( +704 Timer::PerPath(path_id, PathTimer::DiscardPath), +705 now + 6 * pto, +706 self.qlog.with_time(now), +707 ); +708 Ok(()) +709 } +710 +711 /// Gets the [`PathData`] for a known [`PathId`]. +712 /// +713 /// Will panic if the path_id does not reference any known path. +714 #[track_caller] +715 fn path_data(&self, path_id: PathId) -> &PathData { +716 if let Some(data) = self.paths.get(&path_id) { +717 &data.data +718 } else { +719 panic!( +720 "unknown path: {path_id}, currently known paths: {:?}", +721 self.paths.keys().collect::<Vec<_>>() +722 ); +723 } +724 } +725 +726 /// Gets a reference to the [`PathData`] for a [`PathId`] +727 fn path(&self, path_id: PathId) -> Option<&PathData> { +728 self.paths.get(&path_id).map(|path_state| &path_state.data) +729 } +730 +731 /// Gets a mutable reference to the [`PathData`] for a [`PathId`] +732 fn path_mut(&mut self, path_id: PathId) -> Option<&mut PathData> { +733 self.paths +734 .get_mut(&path_id) +735 .map(|path_state| &mut path_state.data) +736 } +737 +738 /// Returns all known paths. +739 /// +740 /// There is no guarantee any of these paths are open or usable. +741 pub fn paths(&self) -> Vec<PathId> { +742 self.paths.keys().copied().collect() +743 } +744 +745 /// Gets the local [`PathStatus`] for a known [`PathId`] +746 pub fn path_status(&self, path_id: PathId) -> Result<PathStatus, ClosedPath> { +747 self.path(path_id) +748 .map(PathData::local_status) +749 .ok_or(ClosedPath { _private: () }) +750 } +751 +752 /// Returns the path's network path represented as a 4-tuple. +753 pub fn network_path(&self, path_id: PathId) -> Result<FourTuple, ClosedPath> { +754 self.path(path_id) +755 .map(|path| path.network_path) +756 .ok_or(ClosedPath { _private: () }) +757 } +758 +759 /// Sets the [`PathStatus`] for a known [`PathId`] +760 /// +761 /// Returns the previous path status on success. +762 pub fn set_path_status( +763 &mut self, +764 path_id: PathId, +765 status: PathStatus, +766 ) -> Result<PathStatus, SetPathStatusError> { +767 if !self.is_multipath_negotiated() { +768 return Err(SetPathStatusError::MultipathNotNegotiated); +769 } +770 let path = self +771 .path_mut(path_id) +772 .ok_or(SetPathStatusError::ClosedPath)?; +773 let prev = match path.status.local_update(status) { +774 Some(prev) => { +775 self.spaces[SpaceId::Data] +776 .pending +777 .path_status +778 .insert(path_id); +779 prev +780 } +781 None => path.local_status(), +782 }; +783 Ok(prev) +784 } +785 +786 /// Returns the remote path status +787 // TODO(flub): Probably should also be some kind of path event? Not even sure if I like +788 // this as an API, but for now it allows me to write a test easily. +789 // TODO(flub): Technically this should be a Result<Option<PathSTatus>>? +790 pub fn remote_path_status(&self, path_id: PathId) -> Option<PathStatus> { +791 self.path(path_id).and_then(|path| path.remote_status()) +792 } +793 +794 /// Sets the max_idle_timeout for a specific path +795 /// +796 /// See [`TransportConfig::default_path_max_idle_timeout`] for details. +797 /// +798 /// Returns the previous value of the setting. +799 pub fn set_path_max_idle_timeout( +800 &mut self, +801 path_id: PathId, +802 timeout: Option<Duration>, +803 ) -> Result<Option<Duration>, ClosedPath> { +804 let path = self +805 .paths +806 .get_mut(&path_id) +807 .ok_or(ClosedPath { _private: () })?; +808 Ok(std::mem::replace(&mut path.data.idle_timeout, timeout)) +809 } +810 +811 /// Sets the keep_alive_interval for a specific path +812 /// +813 /// See [`TransportConfig::default_path_keep_alive_interval`] for details. +814 /// +815 /// Returns the previous value of the setting. +816 pub fn set_path_keep_alive_interval( +817 &mut self, +818 path_id: PathId, +819 interval: Option<Duration>, +820 ) -> Result<Option<Duration>, ClosedPath> { +821 let path = self +822 .paths +823 .get_mut(&path_id) +824 .ok_or(ClosedPath { _private: () })?; +825 Ok(std::mem::replace(&mut path.data.keep_alive, interval)) +826 } +827 +828 /// Gets the [`PathData`] for a known [`PathId`]. +829 /// +830 /// Will panic if the path_id does not reference any known path. +831 #[track_caller] +832 fn path_data_mut(&mut self, path_id: PathId) -> &mut PathData { +833 &mut self.paths.get_mut(&path_id).expect("known path").data +834 } +835 +836 /// Find an open, validated path that's on the same network path as the given network path. +837 /// +838 /// Returns the first path matching, even if there's multiple. +839 fn find_validated_path_on_network_path( +840 &self, +841 network_path: FourTuple, +842 ) -> Option<(&PathId, &PathState)> { +843 self.paths.iter().find(|(path_id, path_state)| { +844 path_state.data.validated +845 // Would this use the same network path, if network_path were used to send right now? +846 && network_path.is_probably_same_path(&path_state.data.network_path) +847 && !self.abandoned_paths.contains(path_id) +848 }) +849 // TODO(@divma): we might want to ensure the path has been recently active to consider the +850 // address validated +851 // matheus23: Perhaps looking at !self.abandoned_paths.contains(path_id) is enough, given keep-alives? +852 } +853 +854 fn ensure_path( +855 &mut self, +856 path_id: PathId, +857 network_path: FourTuple, +858 now: Instant, +859 pn: Option<u64>, +860 ) -> &mut PathData { +861 let valid_path = self.find_validated_path_on_network_path(network_path); +862 let validated = valid_path.is_some(); +863 let initial_rtt = valid_path.map(|(_, path)| path.data.rtt.conservative()); +864 let vacant_entry = match self.paths.entry(path_id) { +865 btree_map::Entry::Vacant(vacant_entry) => vacant_entry, +866 btree_map::Entry::Occupied(occupied_entry) => { +867 return &mut occupied_entry.into_mut().data; +868 } +869 }; +870 +871 debug!(%validated, %path_id, %network_path, "path added"); +872 let peer_max_udp_payload_size = +873 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +874 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +875 let mut data = PathData::new( +876 network_path, +877 self.allow_mtud, +878 Some(peer_max_udp_payload_size), +879 self.path_generation_counter, +880 now, +881 &self.config, +882 ); +883 +884 data.validated = validated; +885 if let Some(initial_rtt) = initial_rtt { +886 data.rtt.reset_initial_rtt(initial_rtt); +887 } +888 +889 let pto = self.ack_frequency.max_ack_delay_for_pto() + data.rtt.pto_base(); +890 self.timers.set( +891 Timer::PerPath(path_id, PathTimer::PathOpen), +892 now + 3 * pto, +893 self.qlog.with_time(now), +894 ); +895 +896 // for the path to be opened we need to send a packet on the path. Sending a challenge +897 // guarantees this +898 data.send_new_challenge = true; +899 +900 let path = vacant_entry.insert(PathState { data, prev: None }); +901 +902 let mut pn_space = spaces::PacketNumberSpace::new(now, SpaceId::Data, &mut self.rng); +903 if let Some(pn) = pn { +904 pn_space.dedup.insert(pn); +905 } +906 self.spaces[SpaceId::Data] +907 .number_spaces +908 .insert(path_id, pn_space); +909 self.qlog.emit_tuple_assigned(path_id, network_path, now); +910 &mut path.data +911 } +912 +913 /// Returns packets to transmit +914 /// +915 /// Connections should be polled for transmit after: +916 /// - the application performed some I/O on the connection +917 /// - a call was made to `handle_event` +918 /// - a call was made to `handle_timeout` +919 /// +920 /// `max_datagrams` specifies how many datagrams can be returned inside a +921 /// single Transmit using GSO. This must be at least 1. +922 #[must_use] +923 pub fn poll_transmit( +924 &mut self, +925 now: Instant, +926 max_datagrams: NonZeroUsize, +927 buf: &mut Vec<u8>, +928 ) -> Option<Transmit> { +929 if let Some(probing) = self +930 .iroh_hp +931 .server_side_mut() +932 .ok() +933 .and_then(iroh_hp::ServerState::next_probe) +934 { +935 let destination = probing.remote(); +936 trace!(%destination, "RAND_DATA packet"); +937 let token: u64 = self.rng.random(); +938 buf.put_u64(token); +939 probing.finish(token); +940 return Some(Transmit { +941 destination, +942 ecn: None, +943 size: 8, +944 segment_size: None, +945 src_ip: None, +946 }); +947 } +948 +949 let max_datagrams = match self.config.enable_segmentation_offload { +950 false => NonZeroUsize::MIN, +951 true => max_datagrams, +952 }; +953 +954 // Each call to poll_transmit can only send datagrams to one destination, because +955 // all datagrams in a GSO batch are for the same destination. Therefore only +956 // datagrams for one Path ID are produced for each poll_transmit call. +957 +958 // TODO(flub): this is wishful thinking and not actually implemented, but perhaps it +959 // should be: +960 +961 // First, if we have to send a close, select a path for that. +962 // Next, all paths that have a PATH_CHALLENGE or PATH_RESPONSE pending. +963 +964 // For all, open, validated and AVAILABLE paths: +965 // - Is the path congestion blocked or pacing blocked? +966 // - call maybe_queue_ to ensure a tail-loss probe would be sent? +967 // - do we need to send a close message? +968 // - call can_send +969 // Once there's nothing more to send on the AVAILABLE paths, do the same for BACKUP paths +970 +971 // Check whether we need to send a close message +972 let close = match self.state.as_type() { +973 StateType::Drained => { +974 self.app_limited = true; +975 return None; +976 } +977 StateType::Draining | StateType::Closed => { +978 // self.close is only reset once the associated packet had been +979 // encoded successfully +980 if !self.close { +981 self.app_limited = true; +982 return None; +983 } +984 true +985 } +986 _ => false, +987 }; +988 +989 // Check whether we need to send an ACK_FREQUENCY frame +990 if let Some(config) = &self.config.ack_frequency_config { +991 let rtt = self +992 .paths +993 .values() +994 .map(|p| p.data.rtt.get()) +995 .min() +996 .expect("one path exists"); +997 self.spaces[SpaceId::Data].pending.ack_frequency = self +998 .ack_frequency +999 .should_send_ack_frequency(rtt, config, &self.peer_params) +1000 && self.highest_space == SpaceId::Data +1001 && self.peer_supports_ack_frequency(); +1002 } +1003 +1004 // Whether this packet can be coalesced with another one in the same datagram. +1005 let mut coalesce = true; +1006 +1007 // Whether the last packet in the datagram must be padded so the datagram takes up +1008 // to at least MIN_INITIAL_SIZE, or to the maximum segment size if this is smaller. +1009 let mut pad_datagram = PadDatagram::No; +1010 +1011 // Whether congestion control stopped the next packet from being sent. Further +1012 // packets could still be built, as e.g. tail-loss probes are not congestion +1013 // limited. +1014 let mut congestion_blocked = false; +1015 +1016 // The packet number of the last built packet. +1017 let mut last_packet_number = None; +1018 +1019 let mut path_id = *self.paths.first_key_value().expect("one path must exist").0; +1020 +1021 // If there is any open, validated and available path we only want to send frames to +1022 // any backup path that must be sent on that backup path exclusively. +1023 let have_available_path = self.paths.iter().any(|(id, path)| { +1024 path.data.validated +1025 && path.data.local_status() == PathStatus::Available +1026 && self.rem_cids.contains_key(id) +1027 }); +1028 +1029 // Setup for the first path_id +1030 let mut transmit = TransmitBuf::new( +1031 buf, +1032 max_datagrams, +1033 self.path_data(path_id).current_mtu().into(), +1034 ); +1035 if let Some(challenge) = self.send_prev_path_challenge(now, &mut transmit, path_id) { +1036 return Some(challenge); +1037 } +1038 let mut space_id = match path_id { +1039 PathId::ZERO => SpaceId::Initial, +1040 _ => SpaceId::Data, +1041 }; +1042 +1043 loop { +1044 // check if there is at least one active CID to use for sending +1045 let Some(remote_cid) = self.rem_cids.get(&path_id).map(CidQueue::active) else { +1046 let err = PathError::RemoteCidsExhausted; +1047 if !self.abandoned_paths.contains(&path_id) { +1048 debug!(?err, %path_id, "no active CID for path"); +1049 self.events.push_back(Event::Path(PathEvent::LocallyClosed { +1050 id: path_id, +1051 error: err, +1052 })); +1053 // Locally we should have refused to open this path, the remote should +1054 // have given us CIDs for this path before opening it. So we can always +1055 // abandon this here. +1056 self.close_path( +1057 now, +1058 path_id, +1059 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH.into(), +1060 ) +1061 .ok(); +1062 self.spaces[SpaceId::Data] +1063 .pending +1064 .path_cids_blocked +1065 .insert(path_id); +1066 } else { +1067 trace!(%path_id, "remote CIDs retired for abandoned path"); +1068 } +1069 +1070 match self.paths.keys().find(|&&next| next > path_id) { +1071 Some(next_path_id) => { +1072 // See if this next path can send anything. +1073 path_id = *next_path_id; +1074 space_id = SpaceId::Data; +1075 +1076 // update per path state +1077 transmit.set_segment_size(self.path_data(path_id).current_mtu().into()); +1078 if let Some(challenge) = +1079 self.send_prev_path_challenge(now, &mut transmit, path_id) +1080 { +1081 return Some(challenge); +1082 } +1083 +1084 continue; +1085 } +1086 None => { +1087 // Nothing more to send. +1088 trace!( +1089 ?space_id, +1090 %path_id, +1091 "no CIDs to send on path, no more paths" +1092 ); +1093 break; +1094 } +1095 } +1096 }; +1097 +1098 // Determine if anything can be sent in this packet number space (SpaceId + +1099 // PathId). +1100 let max_packet_size = if transmit.datagram_remaining_mut() > 0 { +1101 // We are trying to coalesce another packet into this datagram. +1102 transmit.datagram_remaining_mut() +1103 } else { +1104 // A new datagram needs to be started. +1105 transmit.segment_size() +1106 }; +1107 let can_send = self.space_can_send(space_id, path_id, max_packet_size, close); +1108 let path_should_send = { +1109 let path_exclusive_only = space_id == SpaceId::Data +1110 && have_available_path +1111 && self.path_data(path_id).local_status() == PathStatus::Backup; +1112 let path_should_send = if path_exclusive_only { +1113 can_send.path_exclusive +1114 } else { +1115 !can_send.is_empty() +1116 }; +1117 let needs_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1118 path_should_send || needs_loss_probe +1119 }; +1120 +1121 if !path_should_send && space_id < SpaceId::Data { +1122 if self.spaces[space_id].crypto.is_some() { +1123 trace!(?space_id, %path_id, "nothing to send in space"); +1124 } +1125 space_id = space_id.next(); +1126 continue; +1127 } +1128 +1129 let send_blocked = if path_should_send && transmit.datagram_remaining_mut() == 0 { +1130 // Only check congestion control if a new datagram is needed. +1131 self.path_congestion_check(space_id, path_id, &transmit, &can_send, now) +1132 } else { +1133 PathBlocked::No +1134 }; +1135 if send_blocked != PathBlocked::No { +1136 trace!(?space_id, %path_id, ?send_blocked, "congestion blocked"); +1137 congestion_blocked = true; +1138 } +1139 if send_blocked != PathBlocked::No && space_id < SpaceId::Data { +1140 // Higher spaces might still have tail-loss probes to send, which are not +1141 // congestion blocked. +1142 space_id = space_id.next(); +1143 continue; +1144 } +1145 if !path_should_send || send_blocked != PathBlocked::No { +1146 // Nothing more to send on this path, check the next path if possible. +1147 +1148 // If there are any datagrams in the transmit, packets for another path can +1149 // not be built. +1150 if transmit.num_datagrams() > 0 { +1151 break; +1152 } +1153 +1154 match self.paths.keys().find(|&&next| next > path_id) { +1155 Some(next_path_id) => { +1156 // See if this next path can send anything. +1157 trace!( +1158 ?space_id, +1159 %path_id, +1160 %next_path_id, +1161 "nothing to send on path" +1162 ); +1163 path_id = *next_path_id; +1164 space_id = SpaceId::Data; +1165 +1166 // update per path state +1167 transmit.set_segment_size(self.path_data(path_id).current_mtu().into()); +1168 if let Some(challenge) = +1169 self.send_prev_path_challenge(now, &mut transmit, path_id) +1170 { +1171 return Some(challenge); +1172 } +1173 +1174 continue; +1175 } +1176 None => { +1177 // Nothing more to send. +1178 trace!( +1179 ?space_id, +1180 %path_id, +1181 next_path_id=?None::<PathId>, +1182 "nothing to send on path" +1183 ); +1184 break; +1185 } +1186 } +1187 } +1188 +1189 // If the datagram is full, we need to start a new one. +1190 if transmit.datagram_remaining_mut() == 0 { +1191 if transmit.num_datagrams() >= transmit.max_datagrams().get() { +1192 // No more datagrams allowed +1193 break; +1194 } +1195 +1196 match self.spaces[space_id].for_path(path_id).loss_probes { +1197 0 => transmit.start_new_datagram(), +1198 _ => { +1199 // We need something to send for a tail-loss probe. +1200 let request_immediate_ack = +1201 space_id == SpaceId::Data && self.peer_supports_ack_frequency(); +1202 self.spaces[space_id].maybe_queue_probe( +1203 path_id, +1204 request_immediate_ack, +1205 &self.streams, +1206 ); +1207 +1208 self.spaces[space_id].for_path(path_id).loss_probes -= 1; +1209 +1210 // Clamp the datagram to at most the minimum MTU to ensure that loss +1211 // probes can get through and enable recovery even if the path MTU +1212 // has shrank unexpectedly. +1213 transmit.start_new_datagram_with_size(std::cmp::min( +1214 usize::from(INITIAL_MTU), +1215 transmit.segment_size(), +1216 )); +1217 } +1218 } +1219 trace!(count = transmit.num_datagrams(), "new datagram started"); +1220 coalesce = true; +1221 pad_datagram = PadDatagram::No; +1222 } +1223 +1224 // If coalescing another packet into the existing datagram, there should +1225 // still be enough space for a whole packet. +1226 if transmit.datagram_start_offset() < transmit.len() { +1227 debug_assert!(transmit.datagram_remaining_mut() >= MIN_PACKET_SPACE); +1228 } +1229 +1230 // +1231 // From here on, we've determined that a packet will definitely be sent. +1232 // +1233 +1234 if self.spaces[SpaceId::Initial].crypto.is_some() +1235 && space_id == SpaceId::Handshake +1236 && self.side.is_client() +1237 { +1238 // A client stops both sending and processing Initial packets when it +1239 // sends its first Handshake packet. +1240 self.discard_space(now, SpaceId::Initial); +1241 } +1242 if let Some(ref mut prev) = self.prev_crypto { +1243 prev.update_unacked = false; +1244 } +1245 +1246 let mut builder = PacketBuilder::new( +1247 now, +1248 space_id, +1249 path_id, +1250 remote_cid, +1251 &mut transmit, +1252 can_send.other, +1253 self, +1254 )?; +1255 last_packet_number = Some(builder.exact_number); +1256 coalesce = coalesce && !builder.short_header; +1257 +1258 if space_id == SpaceId::Initial && (self.side.is_client() || can_send.other) { +1259 // https://www.rfc-editor.org/rfc/rfc9000.html#section-14.1 +1260 pad_datagram |= PadDatagram::ToMinMtu; +1261 } +1262 if space_id == SpaceId::Data && self.config.pad_to_mtu { +1263 pad_datagram |= PadDatagram::ToSegmentSize; 1264 } -1265 if space_id == SpaceId::Data && self.config.pad_to_mtu { -1266 pad_datagram |= PadDatagram::ToSegmentSize; -1267 } -1268 -1269 if can_send.close { -1270 trace!("sending CONNECTION_CLOSE"); -1271 // Encode ACKs before the ConnectionClose message, to give the receiver -1272 // a better approximate on what data has been processed. This is -1273 // especially important with ack delay, since the peer might not -1274 // have gotten any other ACK for the data earlier on. -1275 let mut sent_frames = SentFrames::default(); -1276 let is_multipath_negotiated = self.is_multipath_negotiated(); -1277 for path_id in self.spaces[space_id] -1278 .number_spaces -1279 .iter() -1280 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) -1281 .map(|(&path_id, _)| path_id) -1282 .collect::<Vec<_>>() -1283 { -1284 Self::populate_acks( -1285 now, -1286 self.receiving_ecn, -1287 &mut sent_frames, -1288 path_id, -1289 space_id, -1290 &mut self.spaces[space_id], -1291 is_multipath_negotiated, -1292 &mut builder.frame_space_mut(), -1293 &mut self.stats, -1294 &mut qlog, -1295 ); -1296 } -1297 -1298 // Since there only 64 ACK frames there will always be enough space -1299 // to encode the ConnectionClose frame too. However we still have the -1300 // check here to prevent crashes if something changes. -1301 debug_assert!( -1302 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, -1303 "ACKs should leave space for ConnectionClose" -1304 ); -1305 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { -1306 let max_frame_size = builder.frame_space_remaining(); -1307 match self.state.as_type() { -1308 StateType::Closed => { -1309 let reason: Close = -1310 self.state.as_closed().expect("checked").clone().into(); -1311 if space_id == SpaceId::Data || reason.is_transport_layer() { -1312 reason -1313 .encoder(max_frame_size) -1314 .encode(&mut builder.frame_space_mut()); -1315 qlog.frame(&Frame::Close(reason)); -1316 } else { -1317 let frame = frame::ConnectionClose { -1318 error_code: TransportErrorCode::APPLICATION_ERROR, -1319 frame_type: frame::MaybeFrame::None, -1320 reason: Bytes::new(), -1321 }; -1322 frame.encode(&mut builder.frame_space_mut(), max_frame_size); -1323 qlog.frame(&Frame::Close(frame::Close::Connection(frame))); -1324 } -1325 } -1326 StateType::Draining => { -1327 let frame = frame::ConnectionClose { -1328 error_code: TransportErrorCode::NO_ERROR, -1329 frame_type: frame::MaybeFrame::None, -1330 reason: Bytes::new(), -1331 }; -1332 frame.encode(&mut builder.frame_space_mut(), max_frame_size); -1333 qlog.frame(&Frame::Close(frame::Close::Connection(frame))); -1334 } -1335 _ => unreachable!( -1336 "tried to make a close packet when the connection wasn't closed" -1337 ), -1338 }; -1339 } -1340 builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog); -1341 if space_id == self.highest_space { -1342 // Don't send another close packet. Even with multipath we only send -1343 // CONNECTION_CLOSE on a single path since we expect our paths to work. -1344 self.close = false; -1345 // `CONNECTION_CLOSE` is the final packet -1346 break; -1347 } else { -1348 // Send a close frame in every possible space for robustness, per -1349 // RFC9000 "Immediate Close during the Handshake". Don't bother trying -1350 // to send anything else. -1351 space_id = space_id.next(); -1352 continue; -1353 } -1354 } -1355 -1356 // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that -1357 // path validation can occur while the link is saturated. -1358 if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 { -1359 let path = self.path_data_mut(path_id); -1360 if let Some((token, network_path)) = -1361 path.path_responses.pop_off_path(path.network_path) -1362 { -1363 // TODO(flub): We need to use the right CID! We shouldn't use the same -1364 // CID as the current active one for the path. Though see also -1365 // https://github.com/quinn-rs/quinn/issues/2184 -1366 let response = frame::PathResponse(token); -1367 trace!(%response, "(off-path)"); -1368 builder.frame_space_mut().write(response); -1369 qlog.frame(&Frame::PathResponse(response)); -1370 self.stats.frame_tx.path_response += 1; -1371 builder.finish_and_track( -1372 now, -1373 self, -1374 path_id, -1375 SentFrames { -1376 non_retransmits: true, -1377 ..SentFrames::default() -1378 }, -1379 PadDatagram::ToMinMtu, -1380 qlog, -1381 ); -1382 self.stats.udp_tx.on_sent(1, transmit.len()); -1383 return Some(Transmit { -1384 destination: network_path.remote, -1385 size: transmit.len(), -1386 ecn: None, -1387 segment_size: None, -1388 src_ip: network_path.local_ip, -1389 }); -1390 } -1391 } -1392 -1393 let sent_frames = { -1394 let path_exclusive_only = have_available_path -1395 && self.path_data(path_id).local_status() == PathStatus::Backup; -1396 let pn = builder.exact_number; -1397 self.populate_packet( -1398 now, -1399 space_id, -1400 path_id, -1401 path_exclusive_only, -1402 &mut builder.frame_space_mut(), -1403 pn, -1404 &mut qlog, -1405 ) -1406 }; -1407 -1408 // ACK-only packets should only be sent when explicitly allowed. If we write them due to -1409 // any other reason, there is a bug which leads to one component announcing write -1410 // readiness while not writing any data. This degrades performance. The condition is -1411 // only checked if the full MTU is available and when potentially large fixed-size -1412 // frames aren't queued, so that lack of space in the datagram isn't the reason for just -1413 // writing ACKs. -1414 debug_assert!( -1415 !(sent_frames.is_ack_only(&self.streams) -1416 && !can_send.acks -1417 && can_send.other -1418 && builder.buf.segment_size() -1419 == self.path_data(path_id).current_mtu() as usize -1420 && self.datagrams.outgoing.is_empty()), -1421 "SendableFrames was {can_send:?}, but only ACKs have been written" -1422 ); -1423 if sent_frames.requires_padding { -1424 pad_datagram |= PadDatagram::ToMinMtu; -1425 } -1426 -1427 for (path_id, _pn) in sent_frames.largest_acked.iter() { -1428 self.spaces[space_id] -1429 .for_path(*path_id) -1430 .pending_acks -1431 .acks_sent(); -1432 self.timers.stop( -1433 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -1434 self.qlog.with_time(now), -1435 ); -1436 } +1265 +1266 if can_send.close { +1267 trace!("sending CONNECTION_CLOSE"); +1268 // Encode ACKs before the ConnectionClose message, to give the receiver +1269 // a better approximate on what data has been processed. This is +1270 // especially important with ack delay, since the peer might not +1271 // have gotten any other ACK for the data earlier on. +1272 let is_multipath_negotiated = self.is_multipath_negotiated(); +1273 for path_id in self.spaces[space_id] +1274 .number_spaces +1275 .iter() +1276 .filter(|(_, pns)| !pns.pending_acks.ranges().is_empty()) +1277 .map(|(&path_id, _)| path_id) +1278 .collect::<Vec<_>>() +1279 { +1280 Self::populate_acks( +1281 now, +1282 self.receiving_ecn, +1283 path_id, +1284 space_id, +1285 &mut self.spaces[space_id], +1286 is_multipath_negotiated, +1287 &mut builder, +1288 &mut self.stats.frame_tx, +1289 ); +1290 } +1291 +1292 // Since there only 64 ACK frames there will always be enough space +1293 // to encode the ConnectionClose frame too. However we still have the +1294 // check here to prevent crashes if something changes. +1295 debug_assert!( +1296 builder.frame_space_remaining() > frame::ConnectionClose::SIZE_BOUND, +1297 "ACKs should leave space for ConnectionClose" +1298 ); +1299 let stats = &mut self.stats.frame_tx; +1300 if frame::ConnectionClose::SIZE_BOUND < builder.frame_space_remaining() { +1301 let max_frame_size = builder.frame_space_remaining(); +1302 let close: Close = match self.state.as_type() { +1303 StateType::Closed => { +1304 let reason: Close = +1305 self.state.as_closed().expect("checked").clone().into(); +1306 if space_id == SpaceId::Data || reason.is_transport_layer() { +1307 reason +1308 } else { +1309 TransportError::APPLICATION_ERROR("").into() +1310 } +1311 } +1312 StateType::Draining => TransportError::NO_ERROR("").into(), +1313 _ => unreachable!( +1314 "tried to make a close packet when the connection wasn't closed" +1315 ), +1316 }; +1317 builder.write_frame(close.encoder(max_frame_size), stats); +1318 } +1319 builder.finish_and_track(now, self, path_id, pad_datagram); +1320 if space_id == self.highest_space { +1321 // Don't send another close packet. Even with multipath we only send +1322 // CONNECTION_CLOSE on a single path since we expect our paths to work. +1323 self.close = false; +1324 // `CONNECTION_CLOSE` is the final packet +1325 break; +1326 } else { +1327 // Send a close frame in every possible space for robustness, per +1328 // RFC9000 "Immediate Close during the Handshake". Don't bother trying +1329 // to send anything else. +1330 space_id = space_id.next(); +1331 continue; +1332 } +1333 } +1334 +1335 // Send an off-path PATH_RESPONSE. Prioritized over on-path data to ensure that +1336 // path validation can occur while the link is saturated. +1337 if space_id == SpaceId::Data && builder.buf.num_datagrams() == 1 { +1338 let path = self.path_data_mut(path_id); +1339 if let Some((token, network_path)) = +1340 path.path_responses.pop_off_path(path.network_path) +1341 { +1342 // TODO(flub): We need to use the right CID! We shouldn't use the same +1343 // CID as the current active one for the path. Though see also +1344 // https://github.com/quinn-rs/quinn/issues/2184 +1345 // +1346 let stats = &mut self.stats.frame_tx; +1347 let frame = frame::PathResponse(token); +1348 builder.write_frame_with_log_msg(frame, stats, Some("(off-path)")); +1349 builder.finish_and_track(now, self, path_id, PadDatagram::ToMinMtu); +1350 self.stats.udp_tx.on_sent(1, transmit.len()); +1351 return Some(Transmit { +1352 destination: network_path.remote, +1353 size: transmit.len(), +1354 ecn: None, +1355 segment_size: None, +1356 src_ip: network_path.local_ip, +1357 }); +1358 } +1359 } +1360 +1361 let path_exclusive_only = +1362 have_available_path && self.path_data(path_id).local_status() == PathStatus::Backup; +1363 self.populate_packet(now, space_id, path_id, path_exclusive_only, &mut builder); +1364 +1365 // ACK-only packets should only be sent when explicitly allowed. If we write them due to +1366 // any other reason, there is a bug which leads to one component announcing write +1367 // readiness while not writing any data. This degrades performance. The condition is +1368 // only checked if the full MTU is available and when potentially large fixed-size +1369 // frames aren't queued, so that lack of space in the datagram isn't the reason for just +1370 // writing ACKs. +1371 debug_assert!( +1372 !(builder.sent_frames().is_ack_only(&self.streams) +1373 && !can_send.acks +1374 && can_send.other +1375 && builder.buf.segment_size() +1376 == self.path_data(path_id).current_mtu() as usize +1377 && self.datagrams.outgoing.is_empty()), +1378 "SendableFrames was {can_send:?}, but only ACKs have been written" +1379 ); +1380 if builder.sent_frames().requires_padding { +1381 pad_datagram |= PadDatagram::ToMinMtu; +1382 } +1383 +1384 for (path_id, _pn) in builder.sent_frames().largest_acked.iter() { +1385 self.spaces[space_id] +1386 .for_path(*path_id) +1387 .pending_acks +1388 .acks_sent(); +1389 self.timers.stop( +1390 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +1391 self.qlog.with_time(now), +1392 ); +1393 } +1394 +1395 // Now we need to finish the packet. Before we do so we need to know if we will +1396 // be coalescing the next packet into this one, or will be ending the datagram +1397 // as well. Because if this is the last packet in the datagram more padding +1398 // might be needed because of the packet type, or to fill the GSO segment size. +1399 +1400 // Are we allowed to coalesce AND is there enough space for another *packet* in +1401 // this datagram AND is there another packet to send in this or the next space? +1402 if coalesce +1403 && builder +1404 .buf +1405 .datagram_remaining_mut() +1406 .saturating_sub(builder.predict_packet_end()) +1407 > MIN_PACKET_SPACE +1408 && self +1409 .next_send_space(space_id, path_id, builder.buf, close) +1410 .is_some() +1411 { +1412 // We can append/coalesce the next packet into the current +1413 // datagram. Finish the current packet without adding extra padding. +1414 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1415 } else { +1416 // We need a new datagram for the next packet. Finish the current +1417 // packet with padding. +1418 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { +1419 // If too many padding bytes would be required to continue the +1420 // GSO batch after this packet, end the GSO batch here. Ensures +1421 // that fixed-size frames with heterogeneous sizes +1422 // (e.g. application datagrams) won't inadvertently waste large +1423 // amounts of bandwidth. The exact threshold is a bit arbitrary +1424 // and might benefit from further tuning, though there's no +1425 // universally optimal value. +1426 const MAX_PADDING: usize = 32; +1427 if builder.buf.datagram_remaining_mut() +1428 > builder.predict_packet_end() + MAX_PADDING +1429 { +1430 trace!( +1431 "GSO truncated by demand for {} padding bytes", +1432 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() +1433 ); +1434 builder.finish_and_track(now, self, path_id, PadDatagram::No); +1435 break; +1436 } 1437 -1438 // Now we need to finish the packet. Before we do so we need to know if we will -1439 // be coalescing the next packet into this one, or will be ending the datagram -1440 // as well. Because if this is the last packet in the datagram more padding -1441 // might be needed because of the packet type, or to fill the GSO segment size. -1442 -1443 // Are we allowed to coalesce AND is there enough space for another *packet* in -1444 // this datagram AND is there another packet to send in this or the next space? -1445 if coalesce -1446 && builder -1447 .buf -1448 .datagram_remaining_mut() -1449 .saturating_sub(builder.predict_packet_end()) -1450 > MIN_PACKET_SPACE -1451 && self -1452 .next_send_space(space_id, path_id, builder.buf, close) -1453 .is_some() -1454 { -1455 // We can append/coalesce the next packet into the current -1456 // datagram. Finish the current packet without adding extra padding. -1457 builder.finish_and_track(now, self, path_id, sent_frames, PadDatagram::No, qlog); -1458 } else { -1459 // We need a new datagram for the next packet. Finish the current -1460 // packet with padding. -1461 if builder.buf.num_datagrams() > 1 && matches!(pad_datagram, PadDatagram::No) { -1462 // If too many padding bytes would be required to continue the -1463 // GSO batch after this packet, end the GSO batch here. Ensures -1464 // that fixed-size frames with heterogeneous sizes -1465 // (e.g. application datagrams) won't inadvertently waste large -1466 // amounts of bandwidth. The exact threshold is a bit arbitrary -1467 // and might benefit from further tuning, though there's no -1468 // universally optimal value. -1469 const MAX_PADDING: usize = 32; -1470 if builder.buf.datagram_remaining_mut() -1471 > builder.predict_packet_end() + MAX_PADDING -1472 { -1473 trace!( -1474 "GSO truncated by demand for {} padding bytes", -1475 builder.buf.datagram_remaining_mut() - builder.predict_packet_end() -1476 ); -1477 builder.finish_and_track( -1478 now, -1479 self, -1480 path_id, -1481 sent_frames, -1482 PadDatagram::No, -1483 qlog, -1484 ); -1485 break; -1486 } -1487 -1488 // Pad the current datagram to GSO segment size so it can be -1489 // included in the GSO batch. -1490 builder.finish_and_track( -1491 now, -1492 self, -1493 path_id, -1494 sent_frames, -1495 PadDatagram::ToSegmentSize, -1496 qlog, -1497 ); -1498 } else { -1499 builder.finish_and_track(now, self, path_id, sent_frames, pad_datagram, qlog); -1500 } -1501 if transmit.num_datagrams() == 1 { -1502 transmit.clip_datagram_size(); -1503 } -1504 } -1505 } -1506 -1507 if let Some(last_packet_number) = last_packet_number { -1508 // Note that when sending in multiple packet spaces the last packet number will -1509 // be the one from the highest packet space. -1510 self.path_data_mut(path_id).congestion.on_sent( -1511 now, -1512 transmit.len() as u64, -1513 last_packet_number, -1514 ); -1515 } -1516 -1517 self.qlog.emit_recovery_metrics( -1518 path_id, -1519 &mut self.paths.get_mut(&path_id).unwrap().data, -1520 now, -1521 ); -1522 -1523 self.app_limited = transmit.is_empty() && !congestion_blocked; +1438 // Pad the current datagram to GSO segment size so it can be +1439 // included in the GSO batch. +1440 builder.finish_and_track(now, self, path_id, PadDatagram::ToSegmentSize); +1441 } else { +1442 builder.finish_and_track(now, self, path_id, pad_datagram); +1443 } +1444 if transmit.num_datagrams() == 1 { +1445 transmit.clip_datagram_size(); +1446 } +1447 } +1448 } +1449 +1450 if let Some(last_packet_number) = last_packet_number { +1451 // Note that when sending in multiple packet spaces the last packet number will +1452 // be the one from the highest packet space. +1453 self.path_data_mut(path_id).congestion.on_sent( +1454 now, +1455 transmit.len() as u64, +1456 last_packet_number, +1457 ); +1458 } +1459 +1460 self.qlog.emit_recovery_metrics( +1461 path_id, +1462 &mut self.paths.get_mut(&path_id).unwrap().data, +1463 now, +1464 ); +1465 +1466 self.app_limited = transmit.is_empty() && !congestion_blocked; +1467 +1468 // Send MTU probe if necessary +1469 if transmit.is_empty() && self.state.is_established() { +1470 // MTU probing happens only in Data space. +1471 let space_id = SpaceId::Data; +1472 path_id = *self.paths.first_key_value().expect("one path must exist").0; +1473 let probe_data = loop { +1474 // We MTU probe all paths for which all of the following is true: +1475 // - We have an active destination CID for the path. +1476 // - The remote address *and* path are validated. +1477 // - The path is not abandoned. +1478 // - The MTU Discovery subsystem wants to probe the path. +1479 let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active); +1480 let eligible = self.path_data(path_id).validated +1481 && !self.path_data(path_id).is_validating_path() +1482 && !self.abandoned_paths.contains(&path_id); +1483 let probe_size = eligible +1484 .then(|| { +1485 let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number(); +1486 self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn) +1487 }) +1488 .flatten(); +1489 match (active_cid, probe_size) { +1490 (Some(active_cid), Some(probe_size)) => { +1491 // Let's send an MTUD probe! +1492 break Some((active_cid, probe_size)); +1493 } +1494 _ => { +1495 // Find the next path to check if it needs an MTUD probe. +1496 match self.paths.keys().find(|&&next| next > path_id) { +1497 Some(next) => { +1498 path_id = *next; +1499 continue; +1500 } +1501 None => break None, +1502 } +1503 } +1504 } +1505 }; +1506 if let Some((active_cid, probe_size)) = probe_data { +1507 // We are definitely sending a DPLPMTUD probe. +1508 debug_assert_eq!(transmit.num_datagrams(), 0); +1509 transmit.start_new_datagram_with_size(probe_size as usize); +1510 +1511 let mut builder = PacketBuilder::new( +1512 now, +1513 space_id, +1514 path_id, +1515 active_cid, +1516 &mut transmit, +1517 true, +1518 self, +1519 )?; +1520 +1521 // We implement MTU probes as ping packets padded up to the probe size +1522 trace!(?probe_size, "writing MTUD probe"); +1523 builder.write_frame(frame::Ping, &mut self.stats.frame_tx); 1524 -1525 // Send MTU probe if necessary -1526 if transmit.is_empty() && self.state.is_established() { -1527 // MTU probing happens only in Data space. -1528 let space_id = SpaceId::Data; -1529 path_id = *self.paths.first_key_value().expect("one path must exist").0; -1530 let probe_data = loop { -1531 // We MTU probe all paths for which all of the following is true: -1532 // - We have an active destination CID for the path. -1533 // - The remote address *and* path are validated. -1534 // - The path is not abandoned. -1535 // - The MTU Discovery subsystem wants to probe the path. -1536 let active_cid = self.rem_cids.get(&path_id).map(CidQueue::active); -1537 let eligible = self.path_data(path_id).validated -1538 && !self.path_data(path_id).is_validating_path() -1539 && !self.abandoned_paths.contains(&path_id); -1540 let probe_size = eligible -1541 .then(|| { -1542 let next_pn = self.spaces[space_id].for_path(path_id).peek_tx_number(); -1543 self.path_data_mut(path_id).mtud.poll_transmit(now, next_pn) -1544 }) -1545 .flatten(); -1546 match (active_cid, probe_size) { -1547 (Some(active_cid), Some(probe_size)) => { -1548 // Let's send an MTUD probe! -1549 break Some((active_cid, probe_size)); -1550 } -1551 _ => { -1552 // Find the next path to check if it needs an MTUD probe. -1553 match self.paths.keys().find(|&&next| next > path_id) { -1554 Some(next) => { -1555 path_id = *next; -1556 continue; -1557 } -1558 None => break None, -1559 } -1560 } -1561 } -1562 }; -1563 if let Some((active_cid, probe_size)) = probe_data { -1564 // We are definitely sending a DPLPMTUD probe. -1565 debug_assert_eq!(transmit.num_datagrams(), 0); -1566 transmit.start_new_datagram_with_size(probe_size as usize); -1567 -1568 let mut qlog = QlogSentPacket::default(); -1569 let mut builder = PacketBuilder::new( -1570 now, -1571 space_id, -1572 path_id, -1573 active_cid, -1574 &mut transmit, -1575 true, -1576 self, -1577 &mut qlog, -1578 )?; -1579 -1580 // We implement MTU probes as ping packets padded up to the probe size -1581 trace!(?probe_size, "writing MTUD probe"); -1582 trace!("PING"); -1583 builder.frame_space_mut().write(frame::FrameType::Ping); -1584 qlog.frame(&Frame::Ping); -1585 self.stats.frame_tx.ping += 1; -1586 -1587 // If supported by the peer, we want no delays to the probe's ACK -1588 if self.peer_supports_ack_frequency() { -1589 trace!("IMMEDIATE_ACK"); -1590 builder -1591 .frame_space_mut() -1592 .write(frame::FrameType::ImmediateAck); -1593 self.stats.frame_tx.immediate_ack += 1; -1594 qlog.frame(&Frame::ImmediateAck); -1595 } -1596 -1597 let sent_frames = SentFrames { -1598 non_retransmits: true, -1599 ..Default::default() -1600 }; -1601 builder.finish_and_track( -1602 now, -1603 self, -1604 path_id, -1605 sent_frames, -1606 PadDatagram::ToSize(probe_size), -1607 qlog, -1608 ); -1609 -1610 self.path_stats -1611 .entry(path_id) -1612 .or_default() -1613 .sent_plpmtud_probes += 1; -1614 } -1615 } -1616 -1617 if transmit.is_empty() { -1618 return None; -1619 } -1620 -1621 let network_path = self.path_data(path_id).network_path; -1622 trace!( -1623 segment_size = transmit.segment_size(), -1624 last_datagram_len = transmit.len() % transmit.segment_size(), -1625 %network_path, -1626 "sending {} bytes in {} datagrams", -1627 transmit.len(), -1628 transmit.num_datagrams() -1629 ); -1630 self.path_data_mut(path_id) -1631 .inc_total_sent(transmit.len() as u64); -1632 -1633 self.stats -1634 .udp_tx -1635 .on_sent(transmit.num_datagrams() as u64, transmit.len()); -1636 -1637 Some(Transmit { -1638 destination: network_path.remote, -1639 size: transmit.len(), -1640 ecn: if self.path_data(path_id).sending_ecn { -1641 Some(EcnCodepoint::Ect0) -1642 } else { -1643 None -1644 }, -1645 segment_size: match transmit.num_datagrams() { -1646 1 => None, -1647 _ => Some(transmit.segment_size()), -1648 }, -1649 src_ip: network_path.local_ip, -1650 }) -1651 } -1652 -1653 /// Returns the [`SpaceId`] of the next packet space which has data to send -1654 /// -1655 /// This takes into account the space available to frames in the next datagram. -1656 // TODO(flub): This duplication is not nice. -1657 fn next_send_space( -1658 &mut self, -1659 current_space_id: SpaceId, -1660 path_id: PathId, -1661 buf: &TransmitBuf<'_>, -1662 close: bool, -1663 ) -> Option<SpaceId> { -1664 // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed -1665 // to be able to send an individual frame at least this large in the next 1-RTT -1666 // packet. This could be generalized to support every space, but it's only needed to -1667 // handle large fixed-size frames, which only exist in 1-RTT (application -1668 // datagrams). We don't account for coalesced packets potentially occupying space -1669 // because frames can always spill into the next datagram. -1670 let mut space_id = current_space_id; -1671 loop { -1672 let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close); -1673 if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) { -1674 return Some(space_id); -1675 } -1676 space_id = match space_id { -1677 SpaceId::Initial => SpaceId::Handshake, -1678 SpaceId::Handshake => SpaceId::Data, -1679 SpaceId::Data => break, -1680 } -1681 } -1682 None -1683 } -1684 -1685 /// Checks if creating a new datagram would be blocked by congestion control -1686 fn path_congestion_check( -1687 &mut self, -1688 space_id: SpaceId, -1689 path_id: PathId, -1690 transmit: &TransmitBuf<'_>, -1691 can_send: &SendableFrames, -1692 now: Instant, -1693 ) -> PathBlocked { -1694 // Anti-amplification is only based on `total_sent`, which gets updated after -1695 // the transmit is sent. Therefore we pass the amount of bytes for datagrams -1696 // that are already created, as well as 1 byte for starting another datagram. If -1697 // there is any anti-amplification budget left, we always allow a full MTU to be -1698 // sent (see https://github.com/quinn-rs/quinn/issues/1082). -1699 if self.side().is_server() -1700 && self -1701 .path_data(path_id) -1702 .anti_amplification_blocked(transmit.len() as u64 + 1) -1703 { -1704 trace!(?space_id, %path_id, "blocked by anti-amplification"); -1705 return PathBlocked::AntiAmplification; -1706 } +1525 // If supported by the peer, we want no delays to the probe's ACK +1526 if self.peer_supports_ack_frequency() { +1527 builder.write_frame(frame::ImmediateAck, &mut self.stats.frame_tx); +1528 } +1529 +1530 builder.finish_and_track(now, self, path_id, PadDatagram::ToSize(probe_size)); +1531 +1532 self.path_stats +1533 .entry(path_id) +1534 .or_default() +1535 .sent_plpmtud_probes += 1; +1536 } +1537 } +1538 +1539 if transmit.is_empty() { +1540 return None; +1541 } +1542 +1543 let network_path = self.path_data(path_id).network_path; +1544 trace!( +1545 segment_size = transmit.segment_size(), +1546 last_datagram_len = transmit.len() % transmit.segment_size(), +1547 %network_path, +1548 "sending {} bytes in {} datagrams", +1549 transmit.len(), +1550 transmit.num_datagrams() +1551 ); +1552 self.path_data_mut(path_id) +1553 .inc_total_sent(transmit.len() as u64); +1554 +1555 self.stats +1556 .udp_tx +1557 .on_sent(transmit.num_datagrams() as u64, transmit.len()); +1558 +1559 Some(Transmit { +1560 destination: network_path.remote, +1561 size: transmit.len(), +1562 ecn: if self.path_data(path_id).sending_ecn { +1563 Some(EcnCodepoint::Ect0) +1564 } else { +1565 None +1566 }, +1567 segment_size: match transmit.num_datagrams() { +1568 1 => None, +1569 _ => Some(transmit.segment_size()), +1570 }, +1571 src_ip: network_path.local_ip, +1572 }) +1573 } +1574 +1575 /// Returns the [`SpaceId`] of the next packet space which has data to send +1576 /// +1577 /// This takes into account the space available to frames in the next datagram. +1578 // TODO(flub): This duplication is not nice. +1579 fn next_send_space( +1580 &mut self, +1581 current_space_id: SpaceId, +1582 path_id: PathId, +1583 buf: &TransmitBuf<'_>, +1584 close: bool, +1585 ) -> Option<SpaceId> { +1586 // Number of bytes available for frames if this is a 1-RTT packet. We're guaranteed +1587 // to be able to send an individual frame at least this large in the next 1-RTT +1588 // packet. This could be generalized to support every space, but it's only needed to +1589 // handle large fixed-size frames, which only exist in 1-RTT (application +1590 // datagrams). We don't account for coalesced packets potentially occupying space +1591 // because frames can always spill into the next datagram. +1592 let mut space_id = current_space_id; +1593 loop { +1594 let can_send = self.space_can_send(space_id, path_id, buf.segment_size(), close); +1595 if !can_send.is_empty() || (close && self.spaces[space_id].crypto.is_some()) { +1596 return Some(space_id); +1597 } +1598 space_id = match space_id { +1599 SpaceId::Initial => SpaceId::Handshake, +1600 SpaceId::Handshake => SpaceId::Data, +1601 SpaceId::Data => break, +1602 } +1603 } +1604 None +1605 } +1606 +1607 /// Checks if creating a new datagram would be blocked by congestion control +1608 fn path_congestion_check( +1609 &mut self, +1610 space_id: SpaceId, +1611 path_id: PathId, +1612 transmit: &TransmitBuf<'_>, +1613 can_send: &SendableFrames, +1614 now: Instant, +1615 ) -> PathBlocked { +1616 // Anti-amplification is only based on `total_sent`, which gets updated after +1617 // the transmit is sent. Therefore we pass the amount of bytes for datagrams +1618 // that are already created, as well as 1 byte for starting another datagram. If +1619 // there is any anti-amplification budget left, we always allow a full MTU to be +1620 // sent (see https://github.com/quinn-rs/quinn/issues/1082). +1621 if self.side().is_server() +1622 && self +1623 .path_data(path_id) +1624 .anti_amplification_blocked(transmit.len() as u64 + 1) +1625 { +1626 trace!(?space_id, %path_id, "blocked by anti-amplification"); +1627 return PathBlocked::AntiAmplification; +1628 } +1629 +1630 // Congestion control check. +1631 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. +1632 let bytes_to_send = transmit.segment_size() as u64; +1633 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; +1634 +1635 if can_send.other && !need_loss_probe && !can_send.close { +1636 let path = self.path_data(path_id); +1637 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { +1638 trace!(?space_id, %path_id, "blocked by congestion control"); +1639 return PathBlocked::Congestion; +1640 } +1641 } +1642 +1643 // Pacing check. +1644 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { +1645 self.timers.set( +1646 Timer::PerPath(path_id, PathTimer::Pacing), +1647 delay, +1648 self.qlog.with_time(now), +1649 ); +1650 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though +1651 // they are not congestion controlled. +1652 trace!(?space_id, %path_id, "blocked by pacing"); +1653 return PathBlocked::Pacing; +1654 } +1655 +1656 PathBlocked::No +1657 } +1658 +1659 /// Send PATH_CHALLENGE for a previous path if necessary +1660 /// +1661 /// QUIC-TRANSPORT section 9.3.3 +1662 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> +1663 fn send_prev_path_challenge( +1664 &mut self, +1665 now: Instant, +1666 buf: &mut TransmitBuf<'_>, +1667 path_id: PathId, +1668 ) -> Option<Transmit> { +1669 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; +1670 // TODO (matheus23): We could use !prev_path.is_validating() here instead to +1671 // (possibly) also re-send challenges when they get lost. +1672 if !prev_path.send_new_challenge { +1673 return None; +1674 }; +1675 prev_path.send_new_challenge = false; +1676 let network_path = prev_path.network_path; +1677 let token = self.rng.random(); +1678 let info = paths::SentChallengeInfo { +1679 sent_instant: now, +1680 network_path, +1681 }; +1682 prev_path.challenges_sent.insert(token, info); +1683 debug_assert_eq!( +1684 self.highest_space, +1685 SpaceId::Data, +1686 "PATH_CHALLENGE queued without 1-RTT keys" +1687 ); +1688 buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize); +1689 +1690 // Use the previous CID to avoid linking the new path with the previous path. We +1691 // don't bother accounting for possible retirement of that prev_cid because this is +1692 // sent once, immediately after migration, when the CID is known to be valid. Even +1693 // if a post-migration packet caused the CID to be retired, it's fair to pretend +1694 // this is sent first. +1695 debug_assert_eq!(buf.datagram_start_offset(), 0); +1696 let mut builder = +1697 PacketBuilder::new(now, SpaceId::Data, path_id, *prev_cid, buf, false, self)?; +1698 let challenge = frame::PathChallenge(token); +1699 let stats = &mut self.stats.frame_tx; +1700 builder.write_frame_with_log_msg(challenge, stats, Some("validating previous path")); +1701 +1702 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame +1703 // to at least the smallest allowed maximum datagram size of 1200 bytes, +1704 // unless the anti-amplification limit for the path does not permit +1705 // sending a datagram of this size +1706 builder.pad_to(MIN_INITIAL_SIZE); 1707 -1708 // Congestion control check. -1709 // Tail loss probes must not be blocked by congestion, or a deadlock could arise. -1710 let bytes_to_send = transmit.segment_size() as u64; -1711 let need_loss_probe = self.spaces[space_id].for_path(path_id).loss_probes > 0; -1712 -1713 if can_send.other && !need_loss_probe && !can_send.close { -1714 let path = self.path_data(path_id); -1715 if path.in_flight.bytes + bytes_to_send >= path.congestion.window() { -1716 trace!(?space_id, %path_id, "blocked by congestion control"); -1717 return PathBlocked::Congestion; -1718 } -1719 } -1720 -1721 // Pacing check. -1722 if let Some(delay) = self.path_data_mut(path_id).pacing_delay(bytes_to_send, now) { -1723 self.timers.set( -1724 Timer::PerPath(path_id, PathTimer::Pacing), -1725 delay, -1726 self.qlog.with_time(now), -1727 ); -1728 // Loss probes and CONNECTION_CLOSE should be subject to pacing, even though -1729 // they are not congestion controlled. -1730 trace!(?space_id, %path_id, "blocked by pacing"); -1731 return PathBlocked::Pacing; -1732 } -1733 -1734 PathBlocked::No -1735 } -1736 -1737 /// Send PATH_CHALLENGE for a previous path if necessary -1738 /// -1739 /// QUIC-TRANSPORT section 9.3.3 -1740 /// <https://www.rfc-editor.org/rfc/rfc9000.html#name-off-path-packet-forwarding> -1741 fn send_prev_path_challenge( -1742 &mut self, -1743 now: Instant, -1744 buf: &mut TransmitBuf<'_>, -1745 path_id: PathId, -1746 ) -> Option<Transmit> { -1747 let (prev_cid, prev_path) = self.paths.get_mut(&path_id)?.prev.as_mut()?; -1748 // TODO (matheus23): We could use !prev_path.is_validating() here instead to -1749 // (possibly) also re-send challenges when they get lost. -1750 if !prev_path.send_new_challenge { -1751 return None; -1752 }; -1753 prev_path.send_new_challenge = false; -1754 let network_path = prev_path.network_path; -1755 let token = self.rng.random(); -1756 let info = paths::SentChallengeInfo { -1757 sent_instant: now, -1758 network_path, -1759 }; -1760 prev_path.challenges_sent.insert(token, info); -1761 debug_assert_eq!( -1762 self.highest_space, -1763 SpaceId::Data, -1764 "PATH_CHALLENGE queued without 1-RTT keys" -1765 ); -1766 buf.start_new_datagram_with_size(MIN_INITIAL_SIZE as usize); -1767 -1768 // Use the previous CID to avoid linking the new path with the previous path. We -1769 // don't bother accounting for possible retirement of that prev_cid because this is -1770 // sent once, immediately after migration, when the CID is known to be valid. Even -1771 // if a post-migration packet caused the CID to be retired, it's fair to pretend -1772 // this is sent first. -1773 debug_assert_eq!(buf.datagram_start_offset(), 0); -1774 let mut qlog = QlogSentPacket::default(); -1775 let mut builder = PacketBuilder::new( -1776 now, -1777 SpaceId::Data, -1778 path_id, -1779 *prev_cid, -1780 buf, -1781 false, -1782 self, -1783 &mut qlog, -1784 )?; -1785 let challenge = frame::PathChallenge(token); -1786 trace!(%challenge, "validating previous path"); -1787 qlog.frame(&Frame::PathChallenge(challenge)); -1788 builder.frame_space_mut().write(challenge); -1789 self.stats.frame_tx.path_challenge += 1; -1790 -1791 // An endpoint MUST expand datagrams that contain a PATH_CHALLENGE frame -1792 // to at least the smallest allowed maximum datagram size of 1200 bytes, -1793 // unless the anti-amplification limit for the path does not permit -1794 // sending a datagram of this size -1795 builder.pad_to(MIN_INITIAL_SIZE); -1796 -1797 builder.finish(self, now, qlog); -1798 self.stats.udp_tx.on_sent(1, buf.len()); -1799 -1800 Some(Transmit { -1801 destination: network_path.remote, -1802 size: buf.len(), -1803 ecn: None, -1804 segment_size: None, -1805 src_ip: network_path.local_ip, -1806 }) -1807 } -1808 -1809 /// Indicate what types of frames are ready to send for the given space -1810 /// -1811 /// *packet_size* is the number of bytes available to build the next packet. *close* -1812 /// *indicates whether a CONNECTION_CLOSE frame needs to be sent. -1813 fn space_can_send( -1814 &mut self, -1815 space_id: SpaceId, -1816 path_id: PathId, -1817 packet_size: usize, -1818 close: bool, -1819 ) -> SendableFrames { -1820 let pn = self.spaces[SpaceId::Data] -1821 .for_path(path_id) -1822 .peek_tx_number(); -1823 let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); -1824 if self.spaces[space_id].crypto.is_none() -1825 && (space_id != SpaceId::Data -1826 || self.zero_rtt_crypto.is_none() -1827 || self.side.is_server()) -1828 { -1829 // No keys available for this space -1830 return SendableFrames::empty(); -1831 } -1832 let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams); -1833 if space_id == SpaceId::Data { -1834 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); -1835 } -1836 -1837 can_send.close = close && self.spaces[space_id].crypto.is_some(); -1838 -1839 can_send -1840 } -1841 -1842 /// Process `ConnectionEvent`s generated by the associated `Endpoint` -1843 /// -1844 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals -1845 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be -1846 /// extracted through the relevant methods. -1847 pub fn handle_event(&mut self, event: ConnectionEvent) { -1848 use ConnectionEventInner::*; -1849 match event.0 { -1850 Datagram(DatagramConnectionEvent { -1851 now, -1852 network_path, -1853 path_id, -1854 ecn, -1855 first_decode, -1856 remaining, -1857 }) => { -1858 let span = trace_span!("pkt", %path_id); -1859 let _guard = span.enter(); -1860 -1861 if self.update_network_path_or_discard(network_path, path_id) { -1862 // A return value of true indicates we should discard this packet. -1863 return; -1864 } -1865 -1866 let was_anti_amplification_blocked = self -1867 .path(path_id) -1868 .map(|path| path.anti_amplification_blocked(1)) -1869 .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated -1870 // TODO(@divma): revisit this -1871 -1872 self.stats.udp_rx.datagrams += 1; -1873 self.stats.udp_rx.bytes += first_decode.len() as u64; -1874 let data_len = first_decode.len(); -1875 -1876 self.handle_decode(now, network_path, path_id, ecn, first_decode); -1877 // The current `path` might have changed inside `handle_decode` since the packet -1878 // could have triggered a migration. The packet might also belong to an unknown -1879 // path and have been rejected. Make sure the data received is accounted for the -1880 // most recent path by accessing `path` after `handle_decode`. -1881 if let Some(path) = self.path_mut(path_id) { -1882 path.inc_total_recvd(data_len as u64); -1883 } -1884 -1885 if let Some(data) = remaining { -1886 self.stats.udp_rx.bytes += data.len() as u64; -1887 self.handle_coalesced(now, network_path, path_id, ecn, data); -1888 } -1889 -1890 if let Some(path) = self.paths.get_mut(&path_id) { -1891 self.qlog -1892 .emit_recovery_metrics(path_id, &mut path.data, now); -1893 } -1894 -1895 if was_anti_amplification_blocked { -1896 // A prior attempt to set the loss detection timer may have failed due to -1897 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if -1898 // the server's first flight is lost. -1899 self.set_loss_detection_timer(now, path_id); -1900 } -1901 } -1902 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { -1903 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); -1904 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); -1905 let cid_state = self -1906 .local_cid_state -1907 .entry(path_id) -1908 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); -1909 cid_state.new_cids(&ids, now); -1910 -1911 ids.into_iter().rev().for_each(|frame| { -1912 self.spaces[SpaceId::Data].pending.new_cids.push(frame); -1913 }); -1914 // Always update Timer::PushNewCid -1915 self.reset_cid_retirement(now); -1916 } -1917 } -1918 } -1919 -1920 /// Updates the network path for `path_id`. -1921 /// -1922 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. -1923 /// Returns false if the path was updated and the packet doesn't need to be discarded. -1924 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { -1925 let remote_may_migrate = self.side.remote_may_migrate(&self.state); -1926 let local_ip_may_migrate = self.side.is_client(); -1927 // If this packet could initiate a migration and we're a client or a server that -1928 // forbids migration, drop the datagram. This could be relaxed to heuristically -1929 // permit NAT-rebinding-like migration. -1930 if let Some(known_path) = self.path_mut(path_id) { -1931 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { -1932 trace!( -1933 %path_id, -1934 %network_path, -1935 %known_path.network_path, -1936 "discarding packet from unrecognized peer" -1937 ); -1938 return true; -1939 } -1940 -1941 if known_path.network_path.local_ip.is_some() -1942 && network_path.local_ip.is_some() -1943 && known_path.network_path.local_ip != network_path.local_ip -1944 && !local_ip_may_migrate -1945 { -1946 trace!( -1947 %path_id, -1948 %network_path, -1949 %known_path.network_path, -1950 "discarding packet sent to incorrect interface" -1951 ); -1952 return true; -1953 } -1954 // If the datagram indicates that we've changed our local IP, we update it. -1955 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: -1956 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the -1957 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. -1958 if let Some(local_ip) = network_path.local_ip { -1959 if known_path -1960 .network_path -1961 .local_ip -1962 .is_some_and(|ip| ip != local_ip) -1963 { -1964 debug!( -1965 %path_id, -1966 %network_path, -1967 %known_path.network_path, -1968 "path's local address seemingly migrated" -1969 ); -1970 } -1971 // We update the address without path validation on the client side. -1972 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 -1973 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). -1974 // This sounds like it's *only* the server endpoints that do this. -1975 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. -1976 // For now, this preserves the behavior of this code pre 4-tuple tracking. -1977 known_path.network_path.local_ip = Some(local_ip); -1978 } -1979 } -1980 false -1981 } -1982 -1983 /// Process timer expirations -1984 /// -1985 /// Executes protocol logic, potentially preparing signals (including application `Event`s, -1986 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant -1987 /// methods. -1988 /// -1989 /// It is most efficient to call this immediately after the system clock reaches the latest -1990 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply -1991 /// no-op and therefore are safe. -1992 pub fn handle_timeout(&mut self, now: Instant) { -1993 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { -1994 // TODO(@divma): remove `at` when the unicorn is born -1995 trace!(?timer, at=?now, "timeout"); -1996 match timer { -1997 Timer::Conn(timer) => match timer { -1998 ConnTimer::Close => { -1999 self.state.move_to_drained(None); -2000 self.endpoint_events.push_back(EndpointEventInner::Drained); -2001 } -2002 ConnTimer::Idle => { -2003 self.kill(ConnectionError::TimedOut); -2004 } -2005 ConnTimer::KeepAlive => { -2006 trace!("sending keep-alive"); -2007 self.ping(); -2008 } -2009 ConnTimer::KeyDiscard => { -2010 self.zero_rtt_crypto = None; -2011 self.prev_crypto = None; -2012 } -2013 ConnTimer::PushNewCid => { -2014 while let Some((path_id, when)) = self.next_cid_retirement() { -2015 if when > now { -2016 break; -2017 } -2018 match self.local_cid_state.get_mut(&path_id) { -2019 None => error!(%path_id, "No local CID state for path"), -2020 Some(cid_state) => { -2021 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame -2022 let num_new_cid = cid_state.on_cid_timeout().into(); -2023 if !self.state.is_closed() { -2024 trace!( -2025 "push a new CID to peer RETIRE_PRIOR_TO field {}", -2026 cid_state.retire_prior_to() -2027 ); -2028 self.endpoint_events.push_back( -2029 EndpointEventInner::NeedIdentifiers( -2030 path_id, -2031 now, -2032 num_new_cid, -2033 ), -2034 ); -2035 } -2036 } -2037 } -2038 } -2039 } -2040 }, -2041 // TODO: add path_id as span somehow -2042 Timer::PerPath(path_id, timer) => { -2043 let span = trace_span!("per-path timer fired", %path_id, ?timer); -2044 let _guard = span.enter(); -2045 match timer { -2046 PathTimer::PathIdle => { -2047 self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into()) -2048 .ok(); -2049 } +1708 builder.finish(self, now); +1709 self.stats.udp_tx.on_sent(1, buf.len()); +1710 +1711 Some(Transmit { +1712 destination: network_path.remote, +1713 size: buf.len(), +1714 ecn: None, +1715 segment_size: None, +1716 src_ip: network_path.local_ip, +1717 }) +1718 } +1719 +1720 /// Indicate what types of frames are ready to send for the given space +1721 /// +1722 /// *packet_size* is the number of bytes available to build the next packet. *close* +1723 /// *indicates whether a CONNECTION_CLOSE frame needs to be sent. +1724 fn space_can_send( +1725 &mut self, +1726 space_id: SpaceId, +1727 path_id: PathId, +1728 packet_size: usize, +1729 close: bool, +1730 ) -> SendableFrames { +1731 let pn = self.spaces[SpaceId::Data] +1732 .for_path(path_id) +1733 .peek_tx_number(); +1734 let frame_space_1rtt = packet_size.saturating_sub(self.predict_1rtt_overhead(pn, path_id)); +1735 if self.spaces[space_id].crypto.is_none() +1736 && (space_id != SpaceId::Data +1737 || self.zero_rtt_crypto.is_none() +1738 || self.side.is_server()) +1739 { +1740 // No keys available for this space +1741 return SendableFrames::empty(); +1742 } +1743 let mut can_send = self.spaces[space_id].can_send(path_id, &self.streams); +1744 if space_id == SpaceId::Data { +1745 can_send |= self.can_send_1rtt(path_id, frame_space_1rtt); +1746 } +1747 +1748 can_send.close = close && self.spaces[space_id].crypto.is_some(); +1749 +1750 can_send +1751 } +1752 +1753 /// Process `ConnectionEvent`s generated by the associated `Endpoint` +1754 /// +1755 /// Will execute protocol logic upon receipt of a connection event, in turn preparing signals +1756 /// (including application `Event`s, `EndpointEvent`s and outgoing datagrams) that should be +1757 /// extracted through the relevant methods. +1758 pub fn handle_event(&mut self, event: ConnectionEvent) { +1759 use ConnectionEventInner::*; +1760 match event.0 { +1761 Datagram(DatagramConnectionEvent { +1762 now, +1763 network_path, +1764 path_id, +1765 ecn, +1766 first_decode, +1767 remaining, +1768 }) => { +1769 let span = trace_span!("pkt", %path_id); +1770 let _guard = span.enter(); +1771 +1772 if self.update_network_path_or_discard(network_path, path_id) { +1773 // A return value of true indicates we should discard this packet. +1774 return; +1775 } +1776 +1777 let was_anti_amplification_blocked = self +1778 .path(path_id) +1779 .map(|path| path.anti_amplification_blocked(1)) +1780 .unwrap_or(true); // if we don't know about this path it's eagerly considered as unvalidated +1781 // TODO(@divma): revisit this +1782 +1783 self.stats.udp_rx.datagrams += 1; +1784 self.stats.udp_rx.bytes += first_decode.len() as u64; +1785 let data_len = first_decode.len(); +1786 +1787 self.handle_decode(now, network_path, path_id, ecn, first_decode); +1788 // The current `path` might have changed inside `handle_decode` since the packet +1789 // could have triggered a migration. The packet might also belong to an unknown +1790 // path and have been rejected. Make sure the data received is accounted for the +1791 // most recent path by accessing `path` after `handle_decode`. +1792 if let Some(path) = self.path_mut(path_id) { +1793 path.inc_total_recvd(data_len as u64); +1794 } +1795 +1796 if let Some(data) = remaining { +1797 self.stats.udp_rx.bytes += data.len() as u64; +1798 self.handle_coalesced(now, network_path, path_id, ecn, data); +1799 } +1800 +1801 if let Some(path) = self.paths.get_mut(&path_id) { +1802 self.qlog +1803 .emit_recovery_metrics(path_id, &mut path.data, now); +1804 } +1805 +1806 if was_anti_amplification_blocked { +1807 // A prior attempt to set the loss detection timer may have failed due to +1808 // anti-amplification, so ensure it's set now. Prevents a handshake deadlock if +1809 // the server's first flight is lost. +1810 self.set_loss_detection_timer(now, path_id); +1811 } +1812 } +1813 NewIdentifiers(ids, now, cid_len, cid_lifetime) => { +1814 let path_id = ids.first().map(|issued| issued.path_id).unwrap_or_default(); +1815 debug_assert!(ids.iter().all(|issued| issued.path_id == path_id)); +1816 let cid_state = self +1817 .local_cid_state +1818 .entry(path_id) +1819 .or_insert_with(|| CidState::new(cid_len, cid_lifetime, now, 0)); +1820 cid_state.new_cids(&ids, now); +1821 +1822 ids.into_iter().rev().for_each(|frame| { +1823 self.spaces[SpaceId::Data].pending.new_cids.push(frame); +1824 }); +1825 // Always update Timer::PushNewCid +1826 self.reset_cid_retirement(now); +1827 } +1828 } +1829 } +1830 +1831 /// Updates the network path for `path_id`. +1832 /// +1833 /// Returns true if a packet coming in for this `path_id` over given `network_path` should be discarded. +1834 /// Returns false if the path was updated and the packet doesn't need to be discarded. +1835 fn update_network_path_or_discard(&mut self, network_path: FourTuple, path_id: PathId) -> bool { +1836 let remote_may_migrate = self.side.remote_may_migrate(&self.state); +1837 let local_ip_may_migrate = self.side.is_client(); +1838 // If this packet could initiate a migration and we're a client or a server that +1839 // forbids migration, drop the datagram. This could be relaxed to heuristically +1840 // permit NAT-rebinding-like migration. +1841 if let Some(known_path) = self.path_mut(path_id) { +1842 if network_path.remote != known_path.network_path.remote && !remote_may_migrate { +1843 trace!( +1844 %path_id, +1845 %network_path, +1846 %known_path.network_path, +1847 "discarding packet from unrecognized peer" +1848 ); +1849 return true; +1850 } +1851 +1852 if known_path.network_path.local_ip.is_some() +1853 && network_path.local_ip.is_some() +1854 && known_path.network_path.local_ip != network_path.local_ip +1855 && !local_ip_may_migrate +1856 { +1857 trace!( +1858 %path_id, +1859 %network_path, +1860 %known_path.network_path, +1861 "discarding packet sent to incorrect interface" +1862 ); +1863 return true; +1864 } +1865 // If the datagram indicates that we've changed our local IP, we update it. +1866 // This is alluded to in Section 5.2 of the Multipath RFC draft 18: +1867 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#name-using-multiple-paths-on-the +1868 // > Client receives the packet, recognizes a path migration, updates the source address of path 2 to 192.0.2.1. +1869 if let Some(local_ip) = network_path.local_ip { +1870 if known_path +1871 .network_path +1872 .local_ip +1873 .is_some_and(|ip| ip != local_ip) +1874 { +1875 debug!( +1876 %path_id, +1877 %network_path, +1878 %known_path.network_path, +1879 "path's local address seemingly migrated" +1880 ); +1881 } +1882 // We update the address without path validation on the client side. +1883 // https://www.ietf.org/archive/id/draft-ietf-quic-multipath-18.html#section-5.1 +1884 // > Servers observing a 4-tuple change will perform path validation (see Section 9 of [QUIC-TRANSPORT]). +1885 // This sounds like it's *only* the server endpoints that do this. +1886 // TODO(matheus23): We should still consider doing a proper migration on the client side in the future. +1887 // For now, this preserves the behavior of this code pre 4-tuple tracking. +1888 known_path.network_path.local_ip = Some(local_ip); +1889 } +1890 } +1891 false +1892 } +1893 +1894 /// Process timer expirations +1895 /// +1896 /// Executes protocol logic, potentially preparing signals (including application `Event`s, +1897 /// `EndpointEvent`s and outgoing datagrams) that should be extracted through the relevant +1898 /// methods. +1899 /// +1900 /// It is most efficient to call this immediately after the system clock reaches the latest +1901 /// `Instant` that was output by `poll_timeout`; however spurious extra calls will simply +1902 /// no-op and therefore are safe. +1903 pub fn handle_timeout(&mut self, now: Instant) { +1904 while let Some((timer, _time)) = self.timers.expire_before(now, &self.qlog) { +1905 // TODO(@divma): remove `at` when the unicorn is born +1906 trace!(?timer, at=?now, "timeout"); +1907 match timer { +1908 Timer::Conn(timer) => match timer { +1909 ConnTimer::Close => { +1910 self.state.move_to_drained(None); +1911 self.endpoint_events.push_back(EndpointEventInner::Drained); +1912 } +1913 ConnTimer::Idle => { +1914 self.kill(ConnectionError::TimedOut); +1915 } +1916 ConnTimer::KeepAlive => { +1917 trace!("sending keep-alive"); +1918 self.ping(); +1919 } +1920 ConnTimer::KeyDiscard => { +1921 self.zero_rtt_crypto = None; +1922 self.prev_crypto = None; +1923 } +1924 ConnTimer::PushNewCid => { +1925 while let Some((path_id, when)) = self.next_cid_retirement() { +1926 if when > now { +1927 break; +1928 } +1929 match self.local_cid_state.get_mut(&path_id) { +1930 None => error!(%path_id, "No local CID state for path"), +1931 Some(cid_state) => { +1932 // Update `retire_prior_to` field in NEW_CONNECTION_ID frame +1933 let num_new_cid = cid_state.on_cid_timeout().into(); +1934 if !self.state.is_closed() { +1935 trace!( +1936 "push a new CID to peer RETIRE_PRIOR_TO field {}", +1937 cid_state.retire_prior_to() +1938 ); +1939 self.endpoint_events.push_back( +1940 EndpointEventInner::NeedIdentifiers( +1941 path_id, +1942 now, +1943 num_new_cid, +1944 ), +1945 ); +1946 } +1947 } +1948 } +1949 } +1950 } +1951 }, +1952 // TODO: add path_id as span somehow +1953 Timer::PerPath(path_id, timer) => { +1954 let span = trace_span!("per-path timer fired", %path_id, ?timer); +1955 let _guard = span.enter(); +1956 match timer { +1957 PathTimer::PathIdle => { +1958 self.close_path(now, path_id, TransportErrorCode::NO_ERROR.into()) +1959 .ok(); +1960 } +1961 +1962 PathTimer::PathKeepAlive => { +1963 trace!("sending keep-alive on path"); +1964 self.ping_path(path_id).ok(); +1965 } +1966 PathTimer::LossDetection => { +1967 self.on_loss_detection_timeout(now, path_id); +1968 self.qlog.emit_recovery_metrics( +1969 path_id, +1970 &mut self.paths.get_mut(&path_id).unwrap().data, +1971 now, +1972 ); +1973 } +1974 PathTimer::PathValidation => { +1975 let Some(path) = self.paths.get_mut(&path_id) else { +1976 continue; +1977 }; +1978 self.timers.stop( +1979 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +1980 self.qlog.with_time(now), +1981 ); +1982 debug!("path validation failed"); +1983 if let Some((_, prev)) = path.prev.take() { +1984 path.data = prev; +1985 } +1986 path.data.challenges_sent.clear(); +1987 path.data.send_new_challenge = false; +1988 } +1989 PathTimer::PathChallengeLost => { +1990 let Some(path) = self.paths.get_mut(&path_id) else { +1991 continue; +1992 }; +1993 trace!("path challenge deemed lost"); +1994 path.data.send_new_challenge = true; +1995 } +1996 PathTimer::PathOpen => { +1997 let Some(path) = self.paths.get_mut(&path_id) else { +1998 continue; +1999 }; +2000 path.data.challenges_sent.clear(); +2001 path.data.send_new_challenge = false; +2002 self.timers.stop( +2003 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +2004 self.qlog.with_time(now), +2005 ); +2006 debug!("new path validation failed"); +2007 if let Err(err) = self.close_path( +2008 now, +2009 path_id, +2010 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), +2011 ) { +2012 warn!(?err, "failed closing path"); +2013 } +2014 +2015 self.events.push_back(Event::Path(PathEvent::LocallyClosed { +2016 id: path_id, +2017 error: PathError::ValidationFailed, +2018 })); +2019 } +2020 PathTimer::Pacing => trace!("pacing timer expired"), +2021 PathTimer::MaxAckDelay => { +2022 trace!("max ack delay reached"); +2023 // This timer is only armed in the Data space +2024 self.spaces[SpaceId::Data] +2025 .for_path(path_id) +2026 .pending_acks +2027 .on_max_ack_delay_timeout() +2028 } +2029 PathTimer::DiscardPath => { +2030 // The path was abandoned and 3*PTO has expired since. Clean up all +2031 // remaining state and install stateless reset token. +2032 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); +2033 if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) { +2034 let (min_seq, max_seq) = loc_cid_state.active_seq(); +2035 for seq in min_seq..=max_seq { +2036 self.endpoint_events.push_back( +2037 EndpointEventInner::RetireConnectionId( +2038 now, path_id, seq, false, +2039 ), +2040 ); +2041 } +2042 } +2043 self.discard_path(path_id, now); +2044 } +2045 } +2046 } +2047 } +2048 } +2049 } 2050 -2051 PathTimer::PathKeepAlive => { -2052 trace!("sending keep-alive on path"); -2053 self.ping_path(path_id).ok(); -2054 } -2055 PathTimer::LossDetection => { -2056 self.on_loss_detection_timeout(now, path_id); -2057 self.qlog.emit_recovery_metrics( -2058 path_id, -2059 &mut self.paths.get_mut(&path_id).unwrap().data, -2060 now, -2061 ); -2062 } -2063 PathTimer::PathValidation => { -2064 let Some(path) = self.paths.get_mut(&path_id) else { -2065 continue; -2066 }; -2067 self.timers.stop( -2068 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2069 self.qlog.with_time(now), -2070 ); -2071 debug!("path validation failed"); -2072 if let Some((_, prev)) = path.prev.take() { -2073 path.data = prev; -2074 } -2075 path.data.challenges_sent.clear(); -2076 path.data.send_new_challenge = false; -2077 } -2078 PathTimer::PathChallengeLost => { -2079 let Some(path) = self.paths.get_mut(&path_id) else { -2080 continue; -2081 }; -2082 trace!("path challenge deemed lost"); -2083 path.data.send_new_challenge = true; -2084 } -2085 PathTimer::PathOpen => { -2086 let Some(path) = self.paths.get_mut(&path_id) else { -2087 continue; -2088 }; -2089 path.data.challenges_sent.clear(); -2090 path.data.send_new_challenge = false; -2091 self.timers.stop( -2092 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -2093 self.qlog.with_time(now), -2094 ); -2095 debug!("new path validation failed"); -2096 if let Err(err) = self.close_path( -2097 now, -2098 path_id, -2099 TransportErrorCode::PATH_UNSTABLE_OR_POOR.into(), -2100 ) { -2101 warn!(?err, "failed closing path"); -2102 } -2103 -2104 self.events.push_back(Event::Path(PathEvent::LocallyClosed { -2105 id: path_id, -2106 error: PathError::ValidationFailed, -2107 })); -2108 } -2109 PathTimer::Pacing => trace!("pacing timer expired"), -2110 PathTimer::MaxAckDelay => { -2111 trace!("max ack delay reached"); -2112 // This timer is only armed in the Data space -2113 self.spaces[SpaceId::Data] -2114 .for_path(path_id) -2115 .pending_acks -2116 .on_max_ack_delay_timeout() -2117 } -2118 PathTimer::DiscardPath => { -2119 // The path was abandoned and 3*PTO has expired since. Clean up all -2120 // remaining state and install stateless reset token. -2121 self.timers.stop_per_path(path_id, self.qlog.with_time(now)); -2122 if let Some(loc_cid_state) = self.local_cid_state.remove(&path_id) { -2123 let (min_seq, max_seq) = loc_cid_state.active_seq(); -2124 for seq in min_seq..=max_seq { -2125 self.endpoint_events.push_back( -2126 EndpointEventInner::RetireConnectionId( -2127 now, path_id, seq, false, -2128 ), -2129 ); -2130 } -2131 } -2132 self.discard_path(path_id, now); -2133 } -2134 } -2135 } -2136 } -2137 } -2138 } -2139 -2140 /// Close a connection immediately -2141 /// -2142 /// This does not ensure delivery of outstanding data. It is the application's responsibility to -2143 /// call this only when all important communications have been completed, e.g. by calling -2144 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding -2145 /// [`StreamEvent::Finished`] event. -2146 /// -2147 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been -2148 /// delivered. There may still be data from the peer that has not been received. -2149 /// -2150 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished -2151 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { -2152 self.close_inner( -2153 now, -2154 Close::Application(frame::ApplicationClose { error_code, reason }), -2155 ) -2156 } -2157 -2158 fn close_inner(&mut self, now: Instant, reason: Close) { -2159 let was_closed = self.state.is_closed(); -2160 if !was_closed { -2161 self.close_common(); -2162 self.set_close_timer(now); -2163 self.close = true; -2164 self.state.move_to_closed_local(reason); -2165 } -2166 } -2167 -2168 /// Control datagrams -2169 pub fn datagrams(&mut self) -> Datagrams<'_> { -2170 Datagrams { conn: self } -2171 } -2172 -2173 /// Returns connection statistics -2174 pub fn stats(&mut self) -> ConnectionStats { -2175 self.stats.clone() +2051 /// Close a connection immediately +2052 /// +2053 /// This does not ensure delivery of outstanding data. It is the application's responsibility to +2054 /// call this only when all important communications have been completed, e.g. by calling +2055 /// [`SendStream::finish`] on outstanding streams and waiting for the corresponding +2056 /// [`StreamEvent::Finished`] event. +2057 /// +2058 /// If [`Streams::send_streams`] returns 0, all outstanding stream data has been +2059 /// delivered. There may still be data from the peer that has not been received. +2060 /// +2061 /// [`StreamEvent::Finished`]: crate::StreamEvent::Finished +2062 pub fn close(&mut self, now: Instant, error_code: VarInt, reason: Bytes) { +2063 self.close_inner( +2064 now, +2065 Close::Application(frame::ApplicationClose { error_code, reason }), +2066 ) +2067 } +2068 +2069 fn close_inner(&mut self, now: Instant, reason: Close) { +2070 let was_closed = self.state.is_closed(); +2071 if !was_closed { +2072 self.close_common(); +2073 self.set_close_timer(now); +2074 self.close = true; +2075 self.state.move_to_closed_local(reason); +2076 } +2077 } +2078 +2079 /// Control datagrams +2080 pub fn datagrams(&mut self) -> Datagrams<'_> { +2081 Datagrams { conn: self } +2082 } +2083 +2084 /// Returns connection statistics +2085 pub fn stats(&mut self) -> ConnectionStats { +2086 self.stats.clone() +2087 } +2088 +2089 /// Returns path statistics +2090 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { +2091 let path = self.paths.get(&path_id)?; +2092 let stats = self.path_stats.entry(path_id).or_default(); +2093 stats.rtt = path.data.rtt.get(); +2094 stats.cwnd = path.data.congestion.window(); +2095 stats.current_mtu = path.data.mtud.current_mtu(); +2096 Some(*stats) +2097 } +2098 +2099 /// Ping the remote endpoint +2100 /// +2101 /// Causes an ACK-eliciting packet to be transmitted on the connection. +2102 pub fn ping(&mut self) { +2103 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would +2104 // be nice if we could only send a single packet for this. +2105 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { +2106 path_data.ping_pending = true; +2107 } +2108 } +2109 +2110 /// Ping the remote endpoint over a specific path +2111 /// +2112 /// Causes an ACK-eliciting packet to be transmitted on the path. +2113 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { +2114 let path_data = self.spaces[self.highest_space] +2115 .number_spaces +2116 .get_mut(&path) +2117 .ok_or(ClosedPath { _private: () })?; +2118 path_data.ping_pending = true; +2119 Ok(()) +2120 } +2121 +2122 /// Update traffic keys spontaneously +2123 /// +2124 /// This can be useful for testing key updates, as they otherwise only happen infrequently. +2125 pub fn force_key_update(&mut self) { +2126 if !self.state.is_established() { +2127 debug!("ignoring forced key update in illegal state"); +2128 return; +2129 } +2130 if self.prev_crypto.is_some() { +2131 // We already just updated, or are currently updating, the keys. Concurrent key updates +2132 // are illegal. +2133 debug!("ignoring redundant forced key update"); +2134 return; +2135 } +2136 self.update_keys(None, false); +2137 } +2138 +2139 /// Get a session reference +2140 pub fn crypto_session(&self) -> &dyn crypto::Session { +2141 &*self.crypto +2142 } +2143 +2144 /// Whether the connection is in the process of being established +2145 /// +2146 /// If this returns `false`, the connection may be either established or closed, signaled by the +2147 /// emission of a `Connected` or `ConnectionLost` message respectively. +2148 pub fn is_handshaking(&self) -> bool { +2149 self.state.is_handshake() +2150 } +2151 +2152 /// Whether the connection is closed +2153 /// +2154 /// Closed connections cannot transport any further data. A connection becomes closed when +2155 /// either peer application intentionally closes it, or when either transport layer detects an +2156 /// error such as a time-out or certificate validation failure. +2157 /// +2158 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. +2159 pub fn is_closed(&self) -> bool { +2160 self.state.is_closed() +2161 } +2162 +2163 /// Whether there is no longer any need to keep the connection around +2164 /// +2165 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight +2166 /// packets from the peer. All drained connections have been closed. +2167 pub fn is_drained(&self) -> bool { +2168 self.state.is_drained() +2169 } +2170 +2171 /// For clients, if the peer accepted the 0-RTT data packets +2172 /// +2173 /// The value is meaningless until after the handshake completes. +2174 pub fn accepted_0rtt(&self) -> bool { +2175 self.accepted_0rtt 2176 } 2177 -2178 /// Returns path statistics -2179 pub fn path_stats(&mut self, path_id: PathId) -> Option<PathStats> { -2180 let path = self.paths.get(&path_id)?; -2181 let stats = self.path_stats.entry(path_id).or_default(); -2182 stats.rtt = path.data.rtt.get(); -2183 stats.cwnd = path.data.congestion.window(); -2184 stats.current_mtu = path.data.mtud.current_mtu(); -2185 Some(*stats) +2178 /// Whether 0-RTT is/was possible during the handshake +2179 pub fn has_0rtt(&self) -> bool { +2180 self.zero_rtt_enabled +2181 } +2182 +2183 /// Whether there are any pending retransmits +2184 pub fn has_pending_retransmits(&self) -> bool { +2185 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) 2186 } 2187 -2188 /// Ping the remote endpoint -2189 /// -2190 /// Causes an ACK-eliciting packet to be transmitted on the connection. -2191 pub fn ping(&mut self) { -2192 // TODO(flub): This is very brute-force: it pings *all* the paths. Instead it would -2193 // be nice if we could only send a single packet for this. -2194 for path_data in self.spaces[self.highest_space].number_spaces.values_mut() { -2195 path_data.ping_pending = true; -2196 } -2197 } -2198 -2199 /// Ping the remote endpoint over a specific path -2200 /// -2201 /// Causes an ACK-eliciting packet to be transmitted on the path. -2202 pub fn ping_path(&mut self, path: PathId) -> Result<(), ClosedPath> { -2203 let path_data = self.spaces[self.highest_space] -2204 .number_spaces -2205 .get_mut(&path) -2206 .ok_or(ClosedPath { _private: () })?; -2207 path_data.ping_pending = true; -2208 Ok(()) -2209 } -2210 -2211 /// Update traffic keys spontaneously -2212 /// -2213 /// This can be useful for testing key updates, as they otherwise only happen infrequently. -2214 pub fn force_key_update(&mut self) { -2215 if !self.state.is_established() { -2216 debug!("ignoring forced key update in illegal state"); -2217 return; -2218 } -2219 if self.prev_crypto.is_some() { -2220 // We already just updated, or are currently updating, the keys. Concurrent key updates -2221 // are illegal. -2222 debug!("ignoring redundant forced key update"); -2223 return; -2224 } -2225 self.update_keys(None, false); -2226 } -2227 -2228 /// Get a session reference -2229 pub fn crypto_session(&self) -> &dyn crypto::Session { -2230 &*self.crypto -2231 } -2232 -2233 /// Whether the connection is in the process of being established -2234 /// -2235 /// If this returns `false`, the connection may be either established or closed, signaled by the -2236 /// emission of a `Connected` or `ConnectionLost` message respectively. -2237 pub fn is_handshaking(&self) -> bool { -2238 self.state.is_handshake() -2239 } -2240 -2241 /// Whether the connection is closed -2242 /// -2243 /// Closed connections cannot transport any further data. A connection becomes closed when -2244 /// either peer application intentionally closes it, or when either transport layer detects an -2245 /// error such as a time-out or certificate validation failure. -2246 /// -2247 /// A `ConnectionLost` event is emitted with details when the connection becomes closed. -2248 pub fn is_closed(&self) -> bool { -2249 self.state.is_closed() -2250 } -2251 -2252 /// Whether there is no longer any need to keep the connection around -2253 /// -2254 /// Closed connections become drained after a brief timeout to absorb any remaining in-flight -2255 /// packets from the peer. All drained connections have been closed. -2256 pub fn is_drained(&self) -> bool { -2257 self.state.is_drained() -2258 } -2259 -2260 /// For clients, if the peer accepted the 0-RTT data packets -2261 /// -2262 /// The value is meaningless until after the handshake completes. -2263 pub fn accepted_0rtt(&self) -> bool { -2264 self.accepted_0rtt -2265 } -2266 -2267 /// Whether 0-RTT is/was possible during the handshake -2268 pub fn has_0rtt(&self) -> bool { -2269 self.zero_rtt_enabled -2270 } -2271 -2272 /// Whether there are any pending retransmits -2273 pub fn has_pending_retransmits(&self) -> bool { -2274 !self.spaces[SpaceId::Data].pending.is_empty(&self.streams) -2275 } -2276 -2277 /// Look up whether we're the client or server of this Connection -2278 pub fn side(&self) -> Side { -2279 self.side.side() -2280 } -2281 -2282 /// Get the address observed by the remote over the given path -2283 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { -2284 self.path(path_id) -2285 .map(|path_data| { -2286 path_data -2287 .last_observed_addr_report -2288 .as_ref() -2289 .map(|observed| observed.socket_addr()) -2290 }) -2291 .ok_or(ClosedPath { _private: () }) -2292 } -2293 -2294 /// Current best estimate of this connection's latency (round-trip-time) -2295 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { -2296 self.path(path_id).map(|d| d.rtt.get()) -2297 } -2298 -2299 /// Current state of this connection's congestion controller, for debugging purposes -2300 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { -2301 self.path(path_id).map(|d| d.congestion.as_ref()) -2302 } -2303 -2304 /// Modify the number of remotely initiated streams that may be concurrently open -2305 /// -2306 /// No streams may be opened by the peer unless fewer than `count` are already open. Large -2307 /// `count`s increase both minimum and worst-case memory consumption. -2308 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { -2309 self.streams.set_max_concurrent(dir, count); -2310 // If the limit was reduced, then a flow control update previously deemed insignificant may -2311 // now be significant. -2312 let pending = &mut self.spaces[SpaceId::Data].pending; -2313 self.streams.queue_max_stream_id(pending); -2314 } -2315 -2316 /// Modify the number of open paths allowed when multipath is enabled -2317 /// -2318 /// When reducing the number of concurrent paths this will only affect delaying sending -2319 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To -2320 /// actively reduce paths they must be closed using [`Connection::close_path`], which -2321 /// can also be used to close not-yet-opened paths. -2322 /// -2323 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable -2324 /// multipath and will fail. -2325 pub fn set_max_concurrent_paths( -2326 &mut self, -2327 now: Instant, -2328 count: NonZeroU32, -2329 ) -> Result<(), MultipathNotNegotiated> { -2330 if !self.is_multipath_negotiated() { -2331 return Err(MultipathNotNegotiated { _private: () }); -2332 } -2333 self.max_concurrent_paths = count; -2334 -2335 let in_use_count = self -2336 .local_max_path_id -2337 .next() -2338 .saturating_sub(self.abandoned_paths.len() as u32) -2339 .as_u32(); -2340 let extra_needed = count.get().saturating_sub(in_use_count); -2341 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); -2342 -2343 self.set_max_path_id(now, new_max_path_id); -2344 -2345 Ok(()) -2346 } -2347 -2348 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths -2349 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { -2350 if max_path_id <= self.local_max_path_id { -2351 return; -2352 } -2353 -2354 self.local_max_path_id = max_path_id; -2355 self.spaces[SpaceId::Data].pending.max_path_id = true; -2356 -2357 self.issue_first_path_cids(now); -2358 } -2359 -2360 /// Current number of remotely initiated streams that may be concurrently open -2361 /// -2362 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), -2363 /// it will not change immediately, even if fewer streams are open. Instead, it will -2364 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. -2365 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { -2366 self.streams.max_concurrent(dir) -2367 } -2368 -2369 /// See [`TransportConfig::send_window()`] -2370 pub fn set_send_window(&mut self, send_window: u64) { -2371 self.streams.set_send_window(send_window); -2372 } -2373 -2374 /// See [`TransportConfig::receive_window()`] -2375 pub fn set_receive_window(&mut self, receive_window: VarInt) { -2376 if self.streams.set_receive_window(receive_window) { -2377 self.spaces[SpaceId::Data].pending.max_data = true; -2378 } -2379 } -2380 -2381 /// Whether the Multipath for QUIC extension is enabled. -2382 /// -2383 /// Multipath is only enabled after the handshake is completed and if it was enabled by both -2384 /// peers. -2385 pub fn is_multipath_negotiated(&self) -> bool { -2386 !self.is_handshaking() -2387 && self.config.max_concurrent_multipath_paths.is_some() -2388 && self.peer_params.initial_max_path_id.is_some() -2389 } -2390 -2391 fn on_ack_received( -2392 &mut self, -2393 now: Instant, -2394 space: SpaceId, -2395 ack: frame::Ack, -2396 ) -> Result<(), TransportError> { -2397 // All ACKs are referencing path 0 -2398 let path = PathId::ZERO; -2399 self.inner_on_ack_received(now, space, path, ack) -2400 } -2401 -2402 fn on_path_ack_received( -2403 &mut self, -2404 now: Instant, -2405 space: SpaceId, -2406 path_ack: frame::PathAck, -2407 ) -> Result<(), TransportError> { -2408 let (ack, path) = path_ack.into_ack(); -2409 self.inner_on_ack_received(now, space, path, ack) -2410 } -2411 -2412 /// Handles an ACK frame acknowledging packets sent on *path*. -2413 fn inner_on_ack_received( -2414 &mut self, -2415 now: Instant, -2416 space: SpaceId, -2417 path: PathId, -2418 ack: frame::Ack, -2419 ) -> Result<(), TransportError> { -2420 if self.abandoned_paths.contains(&path) { -2421 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 -2422 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. -2423 trace!("silently ignoring PATH_ACK on abandoned path"); -2424 return Ok(()); -2425 } -2426 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { -2427 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); -2428 } -2429 let new_largest = { -2430 let space = &mut self.spaces[space].for_path(path); -2431 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { -2432 space.largest_acked_packet = Some(ack.largest); -2433 if let Some(info) = space.sent_packets.get(ack.largest) { -2434 // This should always succeed, but a misbehaving peer might ACK a packet we -2435 // haven't sent. At worst, that will result in us spuriously reducing the -2436 // congestion window. -2437 space.largest_acked_packet_sent = info.time_sent; -2438 } -2439 true -2440 } else { -2441 false -2442 } -2443 }; -2444 -2445 if self.detect_spurious_loss(&ack, space, path) { -2446 self.path_data_mut(path) -2447 .congestion -2448 .on_spurious_congestion_event(); -2449 } -2450 -2451 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. -2452 let mut newly_acked = ArrayRangeSet::new(); -2453 for range in ack.iter() { -2454 self.spaces[space].for_path(path).check_ack(range.clone())?; -2455 for (pn, _) in self.spaces[space] -2456 .for_path(path) -2457 .sent_packets -2458 .iter_range(range) -2459 { -2460 newly_acked.insert_one(pn); -2461 } -2462 } -2463 -2464 if newly_acked.is_empty() { -2465 return Ok(()); -2466 } -2467 -2468 let mut ack_eliciting_acked = false; -2469 for packet in newly_acked.elts() { -2470 if let Some(info) = self.spaces[space].for_path(path).take(packet) { -2471 for (acked_path_id, acked_pn) in info.largest_acked.iter() { -2472 // Assume ACKs for all packets below the largest acknowledged in -2473 // `packet` have been received. This can cause the peer to spuriously -2474 // retransmit if some of our earlier ACKs were lost, but allows for -2475 // simpler state tracking. See discussion at -2476 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking -2477 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { -2478 pns.pending_acks.subtract_below(*acked_pn); -2479 } -2480 } -2481 ack_eliciting_acked |= info.ack_eliciting; -2482 -2483 // Notify MTU discovery that a packet was acked, because it might be an MTU probe -2484 let path_data = self.path_data_mut(path); -2485 let mtu_updated = path_data.mtud.on_acked(space, packet, info.size); -2486 if mtu_updated { -2487 path_data -2488 .congestion -2489 .on_mtu_update(path_data.mtud.current_mtu()); -2490 } -2491 -2492 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame -2493 self.ack_frequency.on_acked(path, packet); -2494 -2495 self.on_packet_acked(now, path, info); -2496 } -2497 } -2498 -2499 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; -2500 let app_limited = self.app_limited; -2501 let path_data = self.path_data_mut(path); -2502 let in_flight = path_data.in_flight.bytes; -2503 -2504 path_data -2505 .congestion -2506 .on_end_acks(now, in_flight, app_limited, largest_ackd); -2507 -2508 if new_largest && ack_eliciting_acked { -2509 let ack_delay = if space != SpaceId::Data { -2510 Duration::from_micros(0) -2511 } else { -2512 cmp::min( -2513 self.ack_frequency.peer_max_ack_delay, -2514 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), -2515 ) -2516 }; -2517 let rtt = now.saturating_duration_since( -2518 self.spaces[space].for_path(path).largest_acked_packet_sent, -2519 ); -2520 -2521 let next_pn = self.spaces[space].for_path(path).next_packet_number; -2522 let path_data = self.path_data_mut(path); -2523 // TODO(@divma): should be a method of path, should be contained in a single place -2524 path_data.rtt.update(ack_delay, rtt); -2525 if path_data.first_packet_after_rtt_sample.is_none() { -2526 path_data.first_packet_after_rtt_sample = Some((space, next_pn)); -2527 } -2528 } -2529 -2530 // Must be called before crypto/pto_count are clobbered -2531 self.detect_lost_packets(now, space, path, true); -2532 -2533 if self.peer_completed_address_validation(path) { -2534 self.path_data_mut(path).pto_count = 0; -2535 } -2536 -2537 // Explicit congestion notification -2538 // TODO(@divma): this code is a good example of logic that should be contained in a single -2539 // place but it's split between the path data and the packet number space data, we should -2540 // find a way to make this work without two lookups -2541 if self.path_data(path).sending_ecn { -2542 if let Some(ecn) = ack.ecn { -2543 // We only examine ECN counters from ACKs that we are certain we received in transmit -2544 // order, allowing us to compute an increase in ECN counts to compare against the number -2545 // of newly acked packets that remains well-defined in the presence of arbitrary packet -2546 // reordering. -2547 if new_largest { -2548 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; -2549 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); -2550 } -2551 } else { -2552 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. -2553 debug!("ECN not acknowledged by peer"); -2554 self.path_data_mut(path).sending_ecn = false; -2555 } -2556 } -2557 -2558 self.set_loss_detection_timer(now, path); -2559 Ok(()) -2560 } +2188 /// Look up whether we're the client or server of this Connection +2189 pub fn side(&self) -> Side { +2190 self.side.side() +2191 } +2192 +2193 /// Get the address observed by the remote over the given path +2194 pub fn path_observed_address(&self, path_id: PathId) -> Result<Option<SocketAddr>, ClosedPath> { +2195 self.path(path_id) +2196 .map(|path_data| { +2197 path_data +2198 .last_observed_addr_report +2199 .as_ref() +2200 .map(|observed| observed.socket_addr()) +2201 }) +2202 .ok_or(ClosedPath { _private: () }) +2203 } +2204 +2205 /// Current best estimate of this connection's latency (round-trip-time) +2206 pub fn rtt(&self, path_id: PathId) -> Option<Duration> { +2207 self.path(path_id).map(|d| d.rtt.get()) +2208 } +2209 +2210 /// Current state of this connection's congestion controller, for debugging purposes +2211 pub fn congestion_state(&self, path_id: PathId) -> Option<&dyn Controller> { +2212 self.path(path_id).map(|d| d.congestion.as_ref()) +2213 } +2214 +2215 /// Modify the number of remotely initiated streams that may be concurrently open +2216 /// +2217 /// No streams may be opened by the peer unless fewer than `count` are already open. Large +2218 /// `count`s increase both minimum and worst-case memory consumption. +2219 pub fn set_max_concurrent_streams(&mut self, dir: Dir, count: VarInt) { +2220 self.streams.set_max_concurrent(dir, count); +2221 // If the limit was reduced, then a flow control update previously deemed insignificant may +2222 // now be significant. +2223 let pending = &mut self.spaces[SpaceId::Data].pending; +2224 self.streams.queue_max_stream_id(pending); +2225 } +2226 +2227 /// Modify the number of open paths allowed when multipath is enabled +2228 /// +2229 /// When reducing the number of concurrent paths this will only affect delaying sending +2230 /// new MAX_PATH_ID frames until fewer than this number of paths are possible. To +2231 /// actively reduce paths they must be closed using [`Connection::close_path`], which +2232 /// can also be used to close not-yet-opened paths. +2233 /// +2234 /// If multipath is not negotiated (see the [`TransportConfig`]) this can not enable +2235 /// multipath and will fail. +2236 pub fn set_max_concurrent_paths( +2237 &mut self, +2238 now: Instant, +2239 count: NonZeroU32, +2240 ) -> Result<(), MultipathNotNegotiated> { +2241 if !self.is_multipath_negotiated() { +2242 return Err(MultipathNotNegotiated { _private: () }); +2243 } +2244 self.max_concurrent_paths = count; +2245 +2246 let in_use_count = self +2247 .local_max_path_id +2248 .next() +2249 .saturating_sub(self.abandoned_paths.len() as u32) +2250 .as_u32(); +2251 let extra_needed = count.get().saturating_sub(in_use_count); +2252 let new_max_path_id = self.local_max_path_id.saturating_add(extra_needed); +2253 +2254 self.set_max_path_id(now, new_max_path_id); +2255 +2256 Ok(()) +2257 } +2258 +2259 /// If needed, issues a new MAX_PATH_ID frame and new CIDs for any newly allowed paths +2260 fn set_max_path_id(&mut self, now: Instant, max_path_id: PathId) { +2261 if max_path_id <= self.local_max_path_id { +2262 return; +2263 } +2264 +2265 self.local_max_path_id = max_path_id; +2266 self.spaces[SpaceId::Data].pending.max_path_id = true; +2267 +2268 self.issue_first_path_cids(now); +2269 } +2270 +2271 /// Current number of remotely initiated streams that may be concurrently open +2272 /// +2273 /// If the target for this limit is reduced using [`set_max_concurrent_streams`](Self::set_max_concurrent_streams), +2274 /// it will not change immediately, even if fewer streams are open. Instead, it will +2275 /// decrement by one for each time a remotely initiated stream of matching directionality is closed. +2276 pub fn max_concurrent_streams(&self, dir: Dir) -> u64 { +2277 self.streams.max_concurrent(dir) +2278 } +2279 +2280 /// See [`TransportConfig::send_window()`] +2281 pub fn set_send_window(&mut self, send_window: u64) { +2282 self.streams.set_send_window(send_window); +2283 } +2284 +2285 /// See [`TransportConfig::receive_window()`] +2286 pub fn set_receive_window(&mut self, receive_window: VarInt) { +2287 if self.streams.set_receive_window(receive_window) { +2288 self.spaces[SpaceId::Data].pending.max_data = true; +2289 } +2290 } +2291 +2292 /// Whether the Multipath for QUIC extension is enabled. +2293 /// +2294 /// Multipath is only enabled after the handshake is completed and if it was enabled by both +2295 /// peers. +2296 pub fn is_multipath_negotiated(&self) -> bool { +2297 !self.is_handshaking() +2298 && self.config.max_concurrent_multipath_paths.is_some() +2299 && self.peer_params.initial_max_path_id.is_some() +2300 } +2301 +2302 fn on_ack_received( +2303 &mut self, +2304 now: Instant, +2305 space: SpaceId, +2306 ack: frame::Ack, +2307 ) -> Result<(), TransportError> { +2308 // All ACKs are referencing path 0 +2309 let path = PathId::ZERO; +2310 self.inner_on_ack_received(now, space, path, ack) +2311 } +2312 +2313 fn on_path_ack_received( +2314 &mut self, +2315 now: Instant, +2316 space: SpaceId, +2317 path_ack: frame::PathAck, +2318 ) -> Result<(), TransportError> { +2319 let (ack, path) = path_ack.into_ack(); +2320 self.inner_on_ack_received(now, space, path, ack) +2321 } +2322 +2323 /// Handles an ACK frame acknowledging packets sent on *path*. +2324 fn inner_on_ack_received( +2325 &mut self, +2326 now: Instant, +2327 space: SpaceId, +2328 path: PathId, +2329 ack: frame::Ack, +2330 ) -> Result<(), TransportError> { +2331 if self.abandoned_paths.contains(&path) { +2332 // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.4.3-3 +2333 // > PATH_ACK frames received with an abandoned path ID are silently ignored, as specified in Section 4. +2334 trace!("silently ignoring PATH_ACK on abandoned path"); +2335 return Ok(()); +2336 } +2337 if ack.largest >= self.spaces[space].for_path(path).next_packet_number { +2338 return Err(TransportError::PROTOCOL_VIOLATION("unsent packet acked")); +2339 } +2340 let new_largest = { +2341 let space = &mut self.spaces[space].for_path(path); +2342 if space.largest_acked_packet.is_none_or(|pn| ack.largest > pn) { +2343 space.largest_acked_packet = Some(ack.largest); +2344 if let Some(info) = space.sent_packets.get(ack.largest) { +2345 // This should always succeed, but a misbehaving peer might ACK a packet we +2346 // haven't sent. At worst, that will result in us spuriously reducing the +2347 // congestion window. +2348 space.largest_acked_packet_sent = info.time_sent; +2349 } +2350 true +2351 } else { +2352 false +2353 } +2354 }; +2355 +2356 if self.detect_spurious_loss(&ack, space, path) { +2357 self.path_data_mut(path) +2358 .congestion +2359 .on_spurious_congestion_event(); +2360 } +2361 +2362 // Avoid DoS from unreasonably huge ack ranges by filtering out just the new acks. +2363 let mut newly_acked = ArrayRangeSet::new(); +2364 for range in ack.iter() { +2365 self.spaces[space].for_path(path).check_ack(range.clone())?; +2366 for (pn, _) in self.spaces[space] +2367 .for_path(path) +2368 .sent_packets +2369 .iter_range(range) +2370 { +2371 newly_acked.insert_one(pn); +2372 } +2373 } +2374 +2375 if newly_acked.is_empty() { +2376 return Ok(()); +2377 } +2378 +2379 let mut ack_eliciting_acked = false; +2380 for packet in newly_acked.elts() { +2381 if let Some(info) = self.spaces[space].for_path(path).take(packet) { +2382 for (acked_path_id, acked_pn) in info.largest_acked.iter() { +2383 // Assume ACKs for all packets below the largest acknowledged in +2384 // `packet` have been received. This can cause the peer to spuriously +2385 // retransmit if some of our earlier ACKs were lost, but allows for +2386 // simpler state tracking. See discussion at +2387 // https://www.rfc-editor.org/rfc/rfc9000.html#name-limiting-ranges-by-tracking +2388 if let Some(pns) = self.spaces[space].path_space_mut(*acked_path_id) { +2389 pns.pending_acks.subtract_below(*acked_pn); +2390 } +2391 } +2392 ack_eliciting_acked |= info.ack_eliciting; +2393 +2394 // Notify MTU discovery that a packet was acked, because it might be an MTU probe +2395 let path_data = self.path_data_mut(path); +2396 let mtu_updated = path_data.mtud.on_acked(space, packet, info.size); +2397 if mtu_updated { +2398 path_data +2399 .congestion +2400 .on_mtu_update(path_data.mtud.current_mtu()); +2401 } +2402 +2403 // Notify ack frequency that a packet was acked, because it might contain an ACK_FREQUENCY frame +2404 self.ack_frequency.on_acked(path, packet); +2405 +2406 self.on_packet_acked(now, path, info); +2407 } +2408 } +2409 +2410 let largest_ackd = self.spaces[space].for_path(path).largest_acked_packet; +2411 let app_limited = self.app_limited; +2412 let path_data = self.path_data_mut(path); +2413 let in_flight = path_data.in_flight.bytes; +2414 +2415 path_data +2416 .congestion +2417 .on_end_acks(now, in_flight, app_limited, largest_ackd); +2418 +2419 if new_largest && ack_eliciting_acked { +2420 let ack_delay = if space != SpaceId::Data { +2421 Duration::from_micros(0) +2422 } else { +2423 cmp::min( +2424 self.ack_frequency.peer_max_ack_delay, +2425 Duration::from_micros(ack.delay << self.peer_params.ack_delay_exponent.0), +2426 ) +2427 }; +2428 let rtt = now.saturating_duration_since( +2429 self.spaces[space].for_path(path).largest_acked_packet_sent, +2430 ); +2431 +2432 let next_pn = self.spaces[space].for_path(path).next_packet_number; +2433 let path_data = self.path_data_mut(path); +2434 // TODO(@divma): should be a method of path, should be contained in a single place +2435 path_data.rtt.update(ack_delay, rtt); +2436 if path_data.first_packet_after_rtt_sample.is_none() { +2437 path_data.first_packet_after_rtt_sample = Some((space, next_pn)); +2438 } +2439 } +2440 +2441 // Must be called before crypto/pto_count are clobbered +2442 self.detect_lost_packets(now, space, path, true); +2443 +2444 if self.peer_completed_address_validation(path) { +2445 self.path_data_mut(path).pto_count = 0; +2446 } +2447 +2448 // Explicit congestion notification +2449 // TODO(@divma): this code is a good example of logic that should be contained in a single +2450 // place but it's split between the path data and the packet number space data, we should +2451 // find a way to make this work without two lookups +2452 if self.path_data(path).sending_ecn { +2453 if let Some(ecn) = ack.ecn { +2454 // We only examine ECN counters from ACKs that we are certain we received in transmit +2455 // order, allowing us to compute an increase in ECN counts to compare against the number +2456 // of newly acked packets that remains well-defined in the presence of arbitrary packet +2457 // reordering. +2458 if new_largest { +2459 let sent = self.spaces[space].for_path(path).largest_acked_packet_sent; +2460 self.process_ecn(now, space, path, newly_acked.len() as u64, ecn, sent); +2461 } +2462 } else { +2463 // We always start out sending ECN, so any ack that doesn't acknowledge it disables it. +2464 debug!("ECN not acknowledged by peer"); +2465 self.path_data_mut(path).sending_ecn = false; +2466 } +2467 } +2468 +2469 self.set_loss_detection_timer(now, path); +2470 Ok(()) +2471 } +2472 +2473 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { +2474 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +2475 +2476 if lost_packets.is_empty() { +2477 return false; +2478 } +2479 +2480 for range in ack.iter() { +2481 let spurious_losses: Vec<u64> = lost_packets +2482 .iter_range(range.clone()) +2483 .map(|(pn, _info)| pn) +2484 .collect(); +2485 +2486 for pn in spurious_losses { +2487 lost_packets.remove(pn); +2488 } +2489 } +2490 +2491 // If this ACK frame acknowledged all deemed lost packets, +2492 // then we have raised a spurious congestion event in the past. +2493 // We cannot conclude when there are remaining packets, +2494 // but future ACK frames might indicate a spurious loss detection. +2495 lost_packets.is_empty() +2496 } +2497 +2498 /// Drain lost packets that we reasonably think will never arrive +2499 /// +2500 /// The current criterion is copied from `msquic`: +2501 /// discard packets that were sent earlier than 2 probe timeouts ago. +2502 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { +2503 let two_pto = 2 * self.path_data(path).rtt.pto_base(); +2504 +2505 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; +2506 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); +2507 } +2508 +2509 /// Process a new ECN block from an in-order ACK +2510 fn process_ecn( +2511 &mut self, +2512 now: Instant, +2513 space: SpaceId, +2514 path: PathId, +2515 newly_acked: u64, +2516 ecn: frame::EcnCounts, +2517 largest_sent_time: Instant, +2518 ) { +2519 match self.spaces[space] +2520 .for_path(path) +2521 .detect_ecn(newly_acked, ecn) +2522 { +2523 Err(e) => { +2524 debug!("halting ECN due to verification failure: {}", e); +2525 +2526 self.path_data_mut(path).sending_ecn = false; +2527 // Wipe out the existing value because it might be garbage and could interfere with +2528 // future attempts to use ECN on new paths. +2529 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; +2530 } +2531 Ok(false) => {} +2532 Ok(true) => { +2533 self.path_stats.entry(path).or_default().congestion_events += 1; +2534 self.path_data_mut(path).congestion.on_congestion_event( +2535 now, +2536 largest_sent_time, +2537 false, +2538 true, +2539 0, +2540 ); +2541 } +2542 } +2543 } +2544 +2545 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from +2546 // high-latency handshakes +2547 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { +2548 self.paths +2549 .get_mut(&path_id) +2550 .expect("known path") +2551 .remove_in_flight(&info); +2552 let app_limited = self.app_limited; +2553 let path = self.path_data_mut(path_id); +2554 if info.ack_eliciting && !path.is_validating_path() { +2555 // Only pass ACKs to the congestion controller if we are not validating the current +2556 // path, so as to ignore any ACKs from older paths still coming in. +2557 let rtt = path.rtt; +2558 path.congestion +2559 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); +2560 } 2561 -2562 fn detect_spurious_loss(&mut self, ack: &frame::Ack, space: SpaceId, path: PathId) -> bool { -2563 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2564 -2565 if lost_packets.is_empty() { -2566 return false; +2562 // Update state for confirmed delivery of frames +2563 if let Some(retransmits) = info.retransmits.get() { +2564 for (id, _) in retransmits.reset_stream.iter() { +2565 self.streams.reset_acked(*id); +2566 } 2567 } 2568 -2569 for range in ack.iter() { -2570 let spurious_losses: Vec<u64> = lost_packets -2571 .iter_range(range.clone()) -2572 .map(|(pn, _info)| pn) -2573 .collect(); -2574 -2575 for pn in spurious_losses { -2576 lost_packets.remove(pn); -2577 } -2578 } -2579 -2580 // If this ACK frame acknowledged all deemed lost packets, -2581 // then we have raised a spurious congestion event in the past. -2582 // We cannot conclude when there are remaining packets, -2583 // but future ACK frames might indicate a spurious loss detection. -2584 lost_packets.is_empty() -2585 } +2569 for frame in info.stream_frames { +2570 self.streams.received_ack_of(frame); +2571 } +2572 } +2573 +2574 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) { +2575 let start = if self.zero_rtt_crypto.is_some() { +2576 now +2577 } else { +2578 self.prev_crypto +2579 .as_ref() +2580 .expect("no previous keys") +2581 .end_packet +2582 .as_ref() +2583 .expect("update not acknowledged yet") +2584 .1 +2585 }; 2586 -2587 /// Drain lost packets that we reasonably think will never arrive -2588 /// -2589 /// The current criterion is copied from `msquic`: -2590 /// discard packets that were sent earlier than 2 probe timeouts ago. -2591 fn drain_lost_packets(&mut self, now: Instant, space: SpaceId, path: PathId) { -2592 let two_pto = 2 * self.path_data(path).rtt.pto_base(); -2593 -2594 let lost_packets = &mut self.spaces[space].for_path(path).lost_packets; -2595 lost_packets.retain(|_pn, info| now.saturating_duration_since(info.time_sent) <= two_pto); -2596 } -2597 -2598 /// Process a new ECN block from an in-order ACK -2599 fn process_ecn( -2600 &mut self, -2601 now: Instant, -2602 space: SpaceId, -2603 path: PathId, -2604 newly_acked: u64, -2605 ecn: frame::EcnCounts, -2606 largest_sent_time: Instant, -2607 ) { -2608 match self.spaces[space] -2609 .for_path(path) -2610 .detect_ecn(newly_acked, ecn) -2611 { -2612 Err(e) => { -2613 debug!("halting ECN due to verification failure: {}", e); +2587 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths. +2588 self.timers.set( +2589 Timer::Conn(ConnTimer::KeyDiscard), +2590 start + self.pto_max_path(space, false) * 3, +2591 self.qlog.with_time(now), +2592 ); +2593 } +2594 +2595 /// Handle a [`PathTimer::LossDetection`] timeout. +2596 /// +2597 /// This timer expires for two reasons: +2598 /// - An ACK-eliciting packet we sent should be considered lost. +2599 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. +2600 /// +2601 /// The former needs us to schedule re-transmission of the lost data. +2602 /// +2603 /// The latter means we have not received an ACK for an ack-eliciting packet we sent +2604 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting +2605 /// packet, to try and elicit new acknowledgements. These new acknowledgements will +2606 /// indicate whether the previously sent packets were lost or not. +2607 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { +2608 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { +2609 // Time threshold loss Detection +2610 self.detect_lost_packets(now, pn_space, path_id, false); +2611 self.set_loss_detection_timer(now, path_id); +2612 return; +2613 } 2614 -2615 self.path_data_mut(path).sending_ecn = false; -2616 // Wipe out the existing value because it might be garbage and could interfere with -2617 // future attempts to use ECN on new paths. -2618 self.spaces[space].for_path(path).ecn_feedback = frame::EcnCounts::ZERO; -2619 } -2620 Ok(false) => {} -2621 Ok(true) => { -2622 self.path_stats.entry(path).or_default().congestion_events += 1; -2623 self.path_data_mut(path).congestion.on_congestion_event( -2624 now, -2625 largest_sent_time, -2626 false, -2627 true, -2628 0, -2629 ); -2630 } -2631 } -2632 } -2633 -2634 // Not timing-aware, so it's safe to call this for inferred acks, such as arise from -2635 // high-latency handshakes -2636 fn on_packet_acked(&mut self, now: Instant, path_id: PathId, info: SentPacket) { -2637 self.paths -2638 .get_mut(&path_id) -2639 .expect("known path") -2640 .remove_in_flight(&info); -2641 let app_limited = self.app_limited; -2642 let path = self.path_data_mut(path_id); -2643 if info.ack_eliciting && !path.is_validating_path() { -2644 // Only pass ACKs to the congestion controller if we are not validating the current -2645 // path, so as to ignore any ACKs from older paths still coming in. -2646 let rtt = path.rtt; -2647 path.congestion -2648 .on_ack(now, info.time_sent, info.size.into(), app_limited, &rtt); -2649 } -2650 -2651 // Update state for confirmed delivery of frames -2652 if let Some(retransmits) = info.retransmits.get() { -2653 for (id, _) in retransmits.reset_stream.iter() { -2654 self.streams.reset_acked(*id); -2655 } -2656 } -2657 -2658 for frame in info.stream_frames { -2659 self.streams.received_ack_of(frame); -2660 } -2661 } -2662 -2663 fn set_key_discard_timer(&mut self, now: Instant, space: SpaceId) { -2664 let start = if self.zero_rtt_crypto.is_some() { -2665 now -2666 } else { -2667 self.prev_crypto -2668 .as_ref() -2669 .expect("no previous keys") -2670 .end_packet -2671 .as_ref() -2672 .expect("update not acknowledged yet") -2673 .1 -2674 }; +2615 let (_, space) = match self.pto_time_and_space(now, path_id) { +2616 Some(x) => x, +2617 None => { +2618 error!(%path_id, "PTO expired while unset"); +2619 return; +2620 } +2621 }; +2622 trace!( +2623 in_flight = self.path_data(path_id).in_flight.bytes, +2624 count = self.path_data(path_id).pto_count, +2625 ?space, +2626 %path_id, +2627 "PTO fired" +2628 ); +2629 +2630 let count = match self.path_data(path_id).in_flight.ack_eliciting { +2631 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification +2632 // deadlock preventions +2633 0 => { +2634 debug_assert!(!self.peer_completed_address_validation(path_id)); +2635 1 +2636 } +2637 // Conventional loss probe +2638 _ => 2, +2639 }; +2640 let pns = self.spaces[space].for_path(path_id); +2641 pns.loss_probes = pns.loss_probes.saturating_add(count); +2642 let path_data = self.path_data_mut(path_id); +2643 path_data.pto_count = path_data.pto_count.saturating_add(1); +2644 self.set_loss_detection_timer(now, path_id); +2645 } +2646 +2647 /// Detect any lost packets +2648 /// +2649 /// There are two cases in which we detects lost packets: +2650 /// +2651 /// - We received an ACK packet. +2652 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet +2653 /// that was followed by an acknowledged packet. The loss timer for this +2654 /// un-acknowledged packet expired and we need to detect that packet as lost. +2655 /// +2656 /// Packets are lost if they are both (See RFC9002 §6.1): +2657 /// +2658 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. +2659 /// - Old enough by either: +2660 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last +2661 /// acknowledged packet. +2662 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. +2663 fn detect_lost_packets( +2664 &mut self, +2665 now: Instant, +2666 pn_space: SpaceId, +2667 path_id: PathId, +2668 due_to_ack: bool, +2669 ) { +2670 let mut lost_packets = Vec::<u64>::new(); +2671 let mut lost_mtu_probe = None; +2672 let mut in_persistent_congestion = false; +2673 let mut size_of_lost_packets = 0u64; +2674 self.spaces[pn_space].for_path(path_id).loss_time = None; 2675 -2676 // QUIC-MULTIPATH § 2.5 Key Phase Update Process: use largest PTO off all paths. -2677 self.timers.set( -2678 Timer::Conn(ConnTimer::KeyDiscard), -2679 start + self.pto_max_path(space, false) * 3, -2680 self.qlog.with_time(now), -2681 ); -2682 } -2683 -2684 /// Handle a [`PathTimer::LossDetection`] timeout. -2685 /// -2686 /// This timer expires for two reasons: -2687 /// - An ACK-eliciting packet we sent should be considered lost. -2688 /// - The PTO may have expired and a tail-loss probe needs to be scheduled. -2689 /// -2690 /// The former needs us to schedule re-transmission of the lost data. -2691 /// -2692 /// The latter means we have not received an ACK for an ack-eliciting packet we sent -2693 /// within the PTO time-window. We need to schedule a tail-loss probe, an ack-eliciting -2694 /// packet, to try and elicit new acknowledgements. These new acknowledgements will -2695 /// indicate whether the previously sent packets were lost or not. -2696 fn on_loss_detection_timeout(&mut self, now: Instant, path_id: PathId) { -2697 if let Some((_, pn_space)) = self.loss_time_and_space(path_id) { -2698 // Time threshold loss Detection -2699 self.detect_lost_packets(now, pn_space, path_id, false); -2700 self.set_loss_detection_timer(now, path_id); -2701 return; -2702 } -2703 -2704 let (_, space) = match self.pto_time_and_space(now, path_id) { -2705 Some(x) => x, -2706 None => { -2707 error!(%path_id, "PTO expired while unset"); -2708 return; -2709 } -2710 }; -2711 trace!( -2712 in_flight = self.path_data(path_id).in_flight.bytes, -2713 count = self.path_data(path_id).pto_count, -2714 ?space, -2715 %path_id, -2716 "PTO fired" -2717 ); -2718 -2719 let count = match self.path_data(path_id).in_flight.ack_eliciting { -2720 // A PTO when we're not expecting any ACKs must be due to handshake anti-amplification -2721 // deadlock preventions -2722 0 => { -2723 debug_assert!(!self.peer_completed_address_validation(path_id)); -2724 1 -2725 } -2726 // Conventional loss probe -2727 _ => 2, -2728 }; -2729 let pns = self.spaces[space].for_path(path_id); -2730 pns.loss_probes = pns.loss_probes.saturating_add(count); -2731 let path_data = self.path_data_mut(path_id); -2732 path_data.pto_count = path_data.pto_count.saturating_add(1); -2733 self.set_loss_detection_timer(now, path_id); -2734 } -2735 -2736 /// Detect any lost packets -2737 /// -2738 /// There are two cases in which we detects lost packets: -2739 /// -2740 /// - We received an ACK packet. -2741 /// - The [`PathTimer::LossDetection`] timer expired. So there is an un-acknowledged packet -2742 /// that was followed by an acknowledged packet. The loss timer for this -2743 /// un-acknowledged packet expired and we need to detect that packet as lost. -2744 /// -2745 /// Packets are lost if they are both (See RFC9002 §6.1): -2746 /// -2747 /// - Unacknowledged, in flight and sent prior to an acknowledged packet. -2748 /// - Old enough by either: -2749 /// - Having a packet number [`TransportConfig::packet_threshold`] lower then the last -2750 /// acknowledged packet. -2751 /// - Being sent [`TransportConfig::time_threshold`] * RTT in the past. -2752 fn detect_lost_packets( -2753 &mut self, -2754 now: Instant, -2755 pn_space: SpaceId, -2756 path_id: PathId, -2757 due_to_ack: bool, -2758 ) { -2759 let mut lost_packets = Vec::<u64>::new(); -2760 let mut lost_mtu_probe = None; -2761 let mut in_persistent_congestion = false; -2762 let mut size_of_lost_packets = 0u64; -2763 self.spaces[pn_space].for_path(path_id).loss_time = None; -2764 -2765 // Find all the lost packets, populating all variables initialised above. -2766 -2767 let path = self.path_data(path_id); +2676 // Find all the lost packets, populating all variables initialised above. +2677 +2678 let path = self.path_data(path_id); +2679 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); +2680 let loss_delay = path +2681 .rtt +2682 .conservative() +2683 .mul_f32(self.config.time_threshold) +2684 .max(TIMER_GRANULARITY); +2685 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; +2686 +2687 let largest_acked_packet = self.spaces[pn_space] +2688 .for_path(path_id) +2689 .largest_acked_packet +2690 .expect("detect_lost_packets only to be called if path received at least one ACK"); +2691 let packet_threshold = self.config.packet_threshold as u64; +2692 +2693 // InPersistentCongestion: Determine if all packets in the time period before the newest +2694 // lost packet, including the edges, are marked lost. PTO computation must always +2695 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). +2696 let congestion_period = self +2697 .pto(SpaceId::Data, path_id) +2698 .saturating_mul(self.config.persistent_congestion_threshold); +2699 let mut persistent_congestion_start: Option<Instant> = None; +2700 let mut prev_packet = None; +2701 let space = self.spaces[pn_space].for_path(path_id); +2702 +2703 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { +2704 if prev_packet != Some(packet.wrapping_sub(1)) { +2705 // An intervening packet was acknowledged +2706 persistent_congestion_start = None; +2707 } +2708 +2709 // Packets sent before now - loss_delay are deemed lost. +2710 // However, we avoid subtraction as it can panic and there's no +2711 // saturating equivalent of this subtraction operation with a Duration. +2712 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; +2713 if packet_too_old || largest_acked_packet >= packet + packet_threshold { +2714 // The packet should be declared lost. +2715 if Some(packet) == in_flight_mtu_probe { +2716 // Lost MTU probes are not included in `lost_packets`, because they +2717 // should not trigger a congestion control response +2718 lost_mtu_probe = in_flight_mtu_probe; +2719 } else { +2720 lost_packets.push(packet); +2721 size_of_lost_packets += info.size as u64; +2722 if info.ack_eliciting && due_to_ack { +2723 match persistent_congestion_start { +2724 // Two ACK-eliciting packets lost more than +2725 // congestion_period apart, with no ACKed packets in between +2726 Some(start) if info.time_sent - start > congestion_period => { +2727 in_persistent_congestion = true; +2728 } +2729 // Persistent congestion must start after the first RTT sample +2730 None if first_packet_after_rtt_sample +2731 .is_some_and(|x| x < (pn_space, packet)) => +2732 { +2733 persistent_congestion_start = Some(info.time_sent); +2734 } +2735 _ => {} +2736 } +2737 } +2738 } +2739 } else { +2740 // The packet should not yet be declared lost. +2741 if space.loss_time.is_none() { +2742 // Since we iterate in order the lowest packet number's loss time will +2743 // always be the earliest. +2744 space.loss_time = Some(info.time_sent + loss_delay); +2745 } +2746 persistent_congestion_start = None; +2747 } +2748 +2749 prev_packet = Some(packet); +2750 } +2751 +2752 self.handle_lost_packets( +2753 pn_space, +2754 path_id, +2755 now, +2756 lost_packets, +2757 lost_mtu_probe, +2758 loss_delay, +2759 in_persistent_congestion, +2760 size_of_lost_packets, +2761 ); +2762 } +2763 +2764 /// Drops the path state, declaring any remaining in-flight packets as lost +2765 fn discard_path(&mut self, path_id: PathId, now: Instant) { +2766 trace!(%path_id, "dropping path state"); +2767 let path = self.path_data(path_id); 2768 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -2769 let loss_delay = path -2770 .rtt -2771 .conservative() -2772 .mul_f32(self.config.time_threshold) -2773 .max(TIMER_GRANULARITY); -2774 let first_packet_after_rtt_sample = path.first_packet_after_rtt_sample; -2775 -2776 let largest_acked_packet = self.spaces[pn_space] -2777 .for_path(path_id) -2778 .largest_acked_packet -2779 .expect("detect_lost_packets only to be called if path received at least one ACK"); -2780 let packet_threshold = self.config.packet_threshold as u64; +2769 +2770 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; +2771 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] +2772 .for_path(path_id) +2773 .sent_packets +2774 .iter() +2775 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) +2776 .map(|(pn, info)| { +2777 size_of_lost_packets += info.size as u64; +2778 pn +2779 }) +2780 .collect(); 2781 -2782 // InPersistentCongestion: Determine if all packets in the time period before the newest -2783 // lost packet, including the edges, are marked lost. PTO computation must always -2784 // include max ACK delay, i.e. operate as if in Data space (see RFC9001 §7.6.1). -2785 let congestion_period = self -2786 .pto(SpaceId::Data, path_id) -2787 .saturating_mul(self.config.persistent_congestion_threshold); -2788 let mut persistent_congestion_start: Option<Instant> = None; -2789 let mut prev_packet = None; -2790 let space = self.spaces[pn_space].for_path(path_id); -2791 -2792 for (packet, info) in space.sent_packets.iter_range(0..largest_acked_packet) { -2793 if prev_packet != Some(packet.wrapping_sub(1)) { -2794 // An intervening packet was acknowledged -2795 persistent_congestion_start = None; -2796 } -2797 -2798 // Packets sent before now - loss_delay are deemed lost. -2799 // However, we avoid subtraction as it can panic and there's no -2800 // saturating equivalent of this subtraction operation with a Duration. -2801 let packet_too_old = now.saturating_duration_since(info.time_sent) >= loss_delay; -2802 if packet_too_old || largest_acked_packet >= packet + packet_threshold { -2803 // The packet should be declared lost. -2804 if Some(packet) == in_flight_mtu_probe { -2805 // Lost MTU probes are not included in `lost_packets`, because they -2806 // should not trigger a congestion control response -2807 lost_mtu_probe = in_flight_mtu_probe; -2808 } else { -2809 lost_packets.push(packet); -2810 size_of_lost_packets += info.size as u64; -2811 if info.ack_eliciting && due_to_ack { -2812 match persistent_congestion_start { -2813 // Two ACK-eliciting packets lost more than -2814 // congestion_period apart, with no ACKed packets in between -2815 Some(start) if info.time_sent - start > congestion_period => { -2816 in_persistent_congestion = true; -2817 } -2818 // Persistent congestion must start after the first RTT sample -2819 None if first_packet_after_rtt_sample -2820 .is_some_and(|x| x < (pn_space, packet)) => -2821 { -2822 persistent_congestion_start = Some(info.time_sent); -2823 } -2824 _ => {} -2825 } -2826 } -2827 } -2828 } else { -2829 // The packet should not yet be declared lost. -2830 if space.loss_time.is_none() { -2831 // Since we iterate in order the lowest packet number's loss time will -2832 // always be the earliest. -2833 space.loss_time = Some(info.time_sent + loss_delay); -2834 } -2835 persistent_congestion_start = None; -2836 } -2837 -2838 prev_packet = Some(packet); -2839 } -2840 -2841 self.handle_lost_packets( -2842 pn_space, -2843 path_id, -2844 now, -2845 lost_packets, -2846 lost_mtu_probe, -2847 loss_delay, -2848 in_persistent_congestion, -2849 size_of_lost_packets, -2850 ); -2851 } -2852 -2853 /// Drops the path state, declaring any remaining in-flight packets as lost -2854 fn discard_path(&mut self, path_id: PathId, now: Instant) { -2855 trace!(%path_id, "dropping path state"); -2856 let path = self.path_data(path_id); -2857 let in_flight_mtu_probe = path.mtud.in_flight_mtu_probe(); -2858 -2859 let mut size_of_lost_packets = 0u64; // add to path_stats.lost_bytes; -2860 let lost_pns: Vec<_> = self.spaces[SpaceId::Data] -2861 .for_path(path_id) -2862 .sent_packets -2863 .iter() -2864 .filter(|(pn, _info)| Some(*pn) != in_flight_mtu_probe) -2865 .map(|(pn, info)| { -2866 size_of_lost_packets += info.size as u64; -2867 pn -2868 }) -2869 .collect(); -2870 -2871 if !lost_pns.is_empty() { -2872 trace!( -2873 %path_id, -2874 count = lost_pns.len(), -2875 lost_bytes = size_of_lost_packets, -2876 "packets lost on path abandon" -2877 ); -2878 self.handle_lost_packets( -2879 SpaceId::Data, -2880 path_id, -2881 now, -2882 lost_pns, -2883 in_flight_mtu_probe, -2884 Duration::ZERO, -2885 false, -2886 size_of_lost_packets, -2887 ); -2888 } -2889 self.paths.remove(&path_id); -2890 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); -2891 -2892 let path_stats = self.path_stats.remove(&path_id).unwrap_or_default(); -2893 self.events.push_back( -2894 PathEvent::Abandoned { -2895 id: path_id, -2896 path_stats, -2897 } -2898 .into(), -2899 ); -2900 } +2782 if !lost_pns.is_empty() { +2783 trace!( +2784 %path_id, +2785 count = lost_pns.len(), +2786 lost_bytes = size_of_lost_packets, +2787 "packets lost on path abandon" +2788 ); +2789 self.handle_lost_packets( +2790 SpaceId::Data, +2791 path_id, +2792 now, +2793 lost_pns, +2794 in_flight_mtu_probe, +2795 Duration::ZERO, +2796 false, +2797 size_of_lost_packets, +2798 ); +2799 } +2800 self.paths.remove(&path_id); +2801 self.spaces[SpaceId::Data].number_spaces.remove(&path_id); +2802 +2803 let path_stats = self.path_stats.remove(&path_id).unwrap_or_default(); +2804 self.events.push_back( +2805 PathEvent::Abandoned { +2806 id: path_id, +2807 path_stats, +2808 } +2809 .into(), +2810 ); +2811 } +2812 +2813 fn handle_lost_packets( +2814 &mut self, +2815 pn_space: SpaceId, +2816 path_id: PathId, +2817 now: Instant, +2818 lost_packets: Vec<u64>, +2819 lost_mtu_probe: Option<u64>, +2820 loss_delay: Duration, +2821 in_persistent_congestion: bool, +2822 size_of_lost_packets: u64, +2823 ) { +2824 debug_assert!( +2825 { +2826 let mut sorted = lost_packets.clone(); +2827 sorted.sort(); +2828 sorted == lost_packets +2829 }, +2830 "lost_packets must be sorted" +2831 ); +2832 +2833 self.drain_lost_packets(now, pn_space, path_id); +2834 +2835 // OnPacketsLost +2836 if let Some(largest_lost) = lost_packets.last().cloned() { +2837 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; +2838 let largest_lost_sent = self.spaces[pn_space] +2839 .for_path(path_id) +2840 .sent_packets +2841 .get(largest_lost) +2842 .unwrap() +2843 .time_sent; +2844 let path_stats = self.path_stats.entry(path_id).or_default(); +2845 path_stats.lost_packets += lost_packets.len() as u64; +2846 path_stats.lost_bytes += size_of_lost_packets; +2847 trace!( +2848 %path_id, +2849 count = lost_packets.len(), +2850 lost_bytes = size_of_lost_packets, +2851 "packets lost", +2852 ); +2853 +2854 for &packet in &lost_packets { +2855 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { +2856 continue; +2857 }; +2858 self.qlog +2859 .emit_packet_lost(packet, &info, loss_delay, pn_space, now); +2860 self.paths +2861 .get_mut(&path_id) +2862 .unwrap() +2863 .remove_in_flight(&info); +2864 +2865 for frame in info.stream_frames { +2866 self.streams.retransmit(frame); +2867 } +2868 self.spaces[pn_space].pending |= info.retransmits; +2869 self.path_data_mut(path_id) +2870 .mtud +2871 .on_non_probe_lost(packet, info.size); +2872 +2873 self.spaces[pn_space].for_path(path_id).lost_packets.insert( +2874 packet, +2875 LostPacket { +2876 time_sent: info.time_sent, +2877 }, +2878 ); +2879 } +2880 +2881 let path = self.path_data_mut(path_id); +2882 if path.mtud.black_hole_detected(now) { +2883 path.congestion.on_mtu_update(path.mtud.current_mtu()); +2884 if let Some(max_datagram_size) = self.datagrams().max_size() { +2885 if self.datagrams.drop_oversized(max_datagram_size) +2886 && self.datagrams.send_blocked +2887 { +2888 self.datagrams.send_blocked = false; +2889 self.events.push_back(Event::DatagramsUnblocked); +2890 } +2891 } +2892 self.path_stats +2893 .entry(path_id) +2894 .or_default() +2895 .black_holes_detected += 1; +2896 } +2897 +2898 // Don't apply congestion penalty for lost ack-only packets +2899 let lost_ack_eliciting = +2900 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; 2901 -2902 fn handle_lost_packets( -2903 &mut self, -2904 pn_space: SpaceId, -2905 path_id: PathId, -2906 now: Instant, -2907 lost_packets: Vec<u64>, -2908 lost_mtu_probe: Option<u64>, -2909 loss_delay: Duration, -2910 in_persistent_congestion: bool, -2911 size_of_lost_packets: u64, -2912 ) { -2913 debug_assert!( -2914 { -2915 let mut sorted = lost_packets.clone(); -2916 sorted.sort(); -2917 sorted == lost_packets -2918 }, -2919 "lost_packets must be sorted" -2920 ); -2921 -2922 self.drain_lost_packets(now, pn_space, path_id); -2923 -2924 // OnPacketsLost -2925 if let Some(largest_lost) = lost_packets.last().cloned() { -2926 let old_bytes_in_flight = self.path_data_mut(path_id).in_flight.bytes; -2927 let largest_lost_sent = self.spaces[pn_space] -2928 .for_path(path_id) -2929 .sent_packets -2930 .get(largest_lost) -2931 .unwrap() -2932 .time_sent; -2933 let path_stats = self.path_stats.entry(path_id).or_default(); -2934 path_stats.lost_packets += lost_packets.len() as u64; -2935 path_stats.lost_bytes += size_of_lost_packets; -2936 trace!( -2937 %path_id, -2938 count = lost_packets.len(), -2939 lost_bytes = size_of_lost_packets, -2940 "packets lost", -2941 ); -2942 -2943 for &packet in &lost_packets { -2944 let Some(info) = self.spaces[pn_space].for_path(path_id).take(packet) else { -2945 continue; -2946 }; -2947 self.qlog -2948 .emit_packet_lost(packet, &info, loss_delay, pn_space, now); -2949 self.paths -2950 .get_mut(&path_id) -2951 .unwrap() -2952 .remove_in_flight(&info); -2953 -2954 for frame in info.stream_frames { -2955 self.streams.retransmit(frame); -2956 } -2957 self.spaces[pn_space].pending |= info.retransmits; -2958 self.path_data_mut(path_id) -2959 .mtud -2960 .on_non_probe_lost(packet, info.size); -2961 -2962 self.spaces[pn_space].for_path(path_id).lost_packets.insert( -2963 packet, -2964 LostPacket { -2965 time_sent: info.time_sent, -2966 }, -2967 ); -2968 } -2969 -2970 let path = self.path_data_mut(path_id); -2971 if path.mtud.black_hole_detected(now) { -2972 path.congestion.on_mtu_update(path.mtud.current_mtu()); -2973 if let Some(max_datagram_size) = self.datagrams().max_size() { -2974 if self.datagrams.drop_oversized(max_datagram_size) -2975 && self.datagrams.send_blocked -2976 { -2977 self.datagrams.send_blocked = false; -2978 self.events.push_back(Event::DatagramsUnblocked); -2979 } -2980 } -2981 self.path_stats -2982 .entry(path_id) -2983 .or_default() -2984 .black_holes_detected += 1; +2902 if lost_ack_eliciting { +2903 self.path_stats +2904 .entry(path_id) +2905 .or_default() +2906 .congestion_events += 1; +2907 self.path_data_mut(path_id).congestion.on_congestion_event( +2908 now, +2909 largest_lost_sent, +2910 in_persistent_congestion, +2911 false, +2912 size_of_lost_packets, +2913 ); +2914 } +2915 } +2916 +2917 // Handle a lost MTU probe +2918 if let Some(packet) = lost_mtu_probe { +2919 let info = self.spaces[SpaceId::Data] +2920 .for_path(path_id) +2921 .take(packet) +2922 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and +2923 // therefore must not have been removed yet +2924 self.paths +2925 .get_mut(&path_id) +2926 .unwrap() +2927 .remove_in_flight(&info); +2928 self.path_data_mut(path_id).mtud.on_probe_lost(); +2929 self.path_stats +2930 .entry(path_id) +2931 .or_default() +2932 .lost_plpmtud_probes += 1; +2933 } +2934 } +2935 +2936 /// Returns the earliest time packets should be declared lost for all spaces on a path. +2937 /// +2938 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the +2939 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. +2940 /// The time returned is when this packet should be declared lost. +2941 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { +2942 SpaceId::iter() +2943 .filter_map(|id| { +2944 self.spaces[id] +2945 .number_spaces +2946 .get(&path_id) +2947 .and_then(|pns| pns.loss_time) +2948 .map(|time| (time, id)) +2949 }) +2950 .min_by_key(|&(time, _)| time) +2951 } +2952 +2953 /// Returns the earliest next PTO should fire for all spaces on a path. +2954 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { +2955 let path = self.path(path_id)?; +2956 let pto_count = path.pto_count; +2957 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); +2958 let mut duration = path.rtt.pto_base() * backoff; +2959 +2960 if path_id == PathId::ZERO +2961 && path.in_flight.ack_eliciting == 0 +2962 && !self.peer_completed_address_validation(PathId::ZERO) +2963 { +2964 // Address Validation during Connection Establishment: +2965 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a +2966 // deadlock if an Initial or Handshake packet from the server is lost and the +2967 // server can not send more due to its anti-amplification limit the client must +2968 // send another packet on PTO. +2969 let space = match self.highest_space { +2970 SpaceId::Handshake => SpaceId::Handshake, +2971 _ => SpaceId::Initial, +2972 }; +2973 +2974 return Some((now + duration, space)); +2975 } +2976 +2977 let mut result = None; +2978 for space in SpaceId::iter() { +2979 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { +2980 continue; +2981 }; +2982 +2983 if !pns.has_in_flight() { +2984 continue; 2985 } -2986 -2987 // Don't apply congestion penalty for lost ack-only packets -2988 let lost_ack_eliciting = -2989 old_bytes_in_flight != self.path_data_mut(path_id).in_flight.bytes; -2990 -2991 if lost_ack_eliciting { -2992 self.path_stats -2993 .entry(path_id) -2994 .or_default() -2995 .congestion_events += 1; -2996 self.path_data_mut(path_id).congestion.on_congestion_event( -2997 now, -2998 largest_lost_sent, -2999 in_persistent_congestion, -3000 false, -3001 size_of_lost_packets, -3002 ); -3003 } -3004 } -3005 -3006 // Handle a lost MTU probe -3007 if let Some(packet) = lost_mtu_probe { -3008 let info = self.spaces[SpaceId::Data] -3009 .for_path(path_id) -3010 .take(packet) -3011 .unwrap(); // safe: lost_mtu_probe is omitted from lost_packets, and -3012 // therefore must not have been removed yet -3013 self.paths -3014 .get_mut(&path_id) -3015 .unwrap() -3016 .remove_in_flight(&info); -3017 self.path_data_mut(path_id).mtud.on_probe_lost(); -3018 self.path_stats -3019 .entry(path_id) -3020 .or_default() -3021 .lost_plpmtud_probes += 1; -3022 } -3023 } -3024 -3025 /// Returns the earliest time packets should be declared lost for all spaces on a path. -3026 /// -3027 /// If a path has an acknowledged packet with any prior un-acknowledged packets, the -3028 /// earliest un-acknowledged packet can be declared lost after a timeout has elapsed. -3029 /// The time returned is when this packet should be declared lost. -3030 fn loss_time_and_space(&self, path_id: PathId) -> Option<(Instant, SpaceId)> { -3031 SpaceId::iter() -3032 .filter_map(|id| { -3033 self.spaces[id] -3034 .number_spaces -3035 .get(&path_id) -3036 .and_then(|pns| pns.loss_time) -3037 .map(|time| (time, id)) -3038 }) -3039 .min_by_key(|&(time, _)| time) -3040 } -3041 -3042 /// Returns the earliest next PTO should fire for all spaces on a path. -3043 fn pto_time_and_space(&mut self, now: Instant, path_id: PathId) -> Option<(Instant, SpaceId)> { -3044 let path = self.path(path_id)?; -3045 let pto_count = path.pto_count; -3046 let backoff = 2u32.pow(pto_count.min(MAX_BACKOFF_EXPONENT)); -3047 let mut duration = path.rtt.pto_base() * backoff; -3048 -3049 if path_id == PathId::ZERO -3050 && path.in_flight.ack_eliciting == 0 -3051 && !self.peer_completed_address_validation(PathId::ZERO) -3052 { -3053 // Address Validation during Connection Establishment: -3054 // https://www.rfc-editor.org/rfc/rfc9000.html#section-8.1. To prevent a -3055 // deadlock if an Initial or Handshake packet from the server is lost and the -3056 // server can not send more due to its anti-amplification limit the client must -3057 // send another packet on PTO. -3058 let space = match self.highest_space { -3059 SpaceId::Handshake => SpaceId::Handshake, -3060 _ => SpaceId::Initial, -3061 }; -3062 -3063 return Some((now + duration, space)); -3064 } -3065 -3066 let mut result = None; -3067 for space in SpaceId::iter() { -3068 let Some(pns) = self.spaces[space].number_spaces.get(&path_id) else { -3069 continue; -3070 }; -3071 -3072 if !pns.has_in_flight() { -3073 continue; -3074 } -3075 if space == SpaceId::Data { -3076 // Skip ApplicationData until handshake completes. -3077 if self.is_handshaking() { -3078 return result; -3079 } -3080 // Include max_ack_delay and backoff for ApplicationData. -3081 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; -3082 } -3083 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { -3084 continue; -3085 }; -3086 let pto = last_ack_eliciting + duration; -3087 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { -3088 if path.anti_amplification_blocked(1) { -3089 // Nothing would be able to be sent. -3090 continue; -3091 } -3092 if path.in_flight.ack_eliciting == 0 { -3093 // Nothing ack-eliciting, no PTO to arm/fire. -3094 continue; -3095 } -3096 result = Some((pto, space)); -3097 } -3098 } -3099 result -3100 } -3101 -3102 fn peer_completed_address_validation(&self, path: PathId) -> bool { -3103 // TODO(flub): This logic needs updating for multipath -3104 if self.side.is_server() || self.state.is_closed() { -3105 return true; -3106 } -3107 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT -3108 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. -3109 self.spaces[SpaceId::Handshake] -3110 .path_space(PathId::ZERO) -3111 .and_then(|pns| pns.largest_acked_packet) -3112 .is_some() -3113 || self.spaces[SpaceId::Data] -3114 .path_space(path) -3115 .and_then(|pns| pns.largest_acked_packet) -3116 .is_some() -3117 || (self.spaces[SpaceId::Data].crypto.is_some() -3118 && self.spaces[SpaceId::Handshake].crypto.is_none()) -3119 } -3120 -3121 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed -3122 /// -3123 /// The timer must fire if either: -3124 /// - An ack-eliciting packet we sent needs to be declared lost. -3125 /// - A tail-loss probe needs to be sent. -3126 /// -3127 /// See [`Connection::on_loss_detection_timeout`] for details. -3128 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { -3129 if self.state.is_closed() { -3130 // No loss detection takes place on closed connections, and `close_common` already -3131 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a -3132 // reordered packet being handled by state-insensitive code. -3133 return; -3134 } -3135 -3136 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { -3137 // Time threshold loss detection. -3138 self.timers.set( -3139 Timer::PerPath(path_id, PathTimer::LossDetection), -3140 loss_time, -3141 self.qlog.with_time(now), -3142 ); -3143 return; -3144 } -3145 -3146 // Determine which PN space to arm PTO for. -3147 // Calculate PTO duration -3148 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { -3149 self.timers.set( -3150 Timer::PerPath(path_id, PathTimer::LossDetection), -3151 timeout, -3152 self.qlog.with_time(now), -3153 ); -3154 } else { -3155 self.timers.stop( -3156 Timer::PerPath(path_id, PathTimer::LossDetection), -3157 self.qlog.with_time(now), -3158 ); -3159 } -3160 } -3161 -3162 /// The maximum probe timeout across all paths -3163 /// -3164 /// If `is_closing` is set to `true` it will filter out paths that have not yet been used. -3165 /// -3166 /// See [`Connection::pto`] -3167 fn pto_max_path(&self, space: SpaceId, is_closing: bool) -> Duration { -3168 match space { -3169 SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO), -3170 SpaceId::Data => self -3171 .paths -3172 .iter() -3173 .filter_map(|(path_id, state)| { -3174 if is_closing && state.data.total_sent == 0 && state.data.total_recvd == 0 { -3175 // If we are closing and haven't sent anything yet, do not include -3176 None -3177 } else { -3178 let pto = self.pto(space, *path_id); -3179 Some(pto) -3180 } -3181 }) -3182 .max() -3183 .expect("there should be one at least path"), +2986 if space == SpaceId::Data { +2987 // Skip ApplicationData until handshake completes. +2988 if self.is_handshaking() { +2989 return result; +2990 } +2991 // Include max_ack_delay and backoff for ApplicationData. +2992 duration += self.ack_frequency.max_ack_delay_for_pto() * backoff; +2993 } +2994 let Some(last_ack_eliciting) = pns.time_of_last_ack_eliciting_packet else { +2995 continue; +2996 }; +2997 let pto = last_ack_eliciting + duration; +2998 if result.is_none_or(|(earliest_pto, _)| pto < earliest_pto) { +2999 if path.anti_amplification_blocked(1) { +3000 // Nothing would be able to be sent. +3001 continue; +3002 } +3003 if path.in_flight.ack_eliciting == 0 { +3004 // Nothing ack-eliciting, no PTO to arm/fire. +3005 continue; +3006 } +3007 result = Some((pto, space)); +3008 } +3009 } +3010 result +3011 } +3012 +3013 fn peer_completed_address_validation(&self, path: PathId) -> bool { +3014 // TODO(flub): This logic needs updating for multipath +3015 if self.side.is_server() || self.state.is_closed() { +3016 return true; +3017 } +3018 // The server is guaranteed to have validated our address if any of our handshake or 1-RTT +3019 // packets are acknowledged or we've seen HANDSHAKE_DONE and discarded handshake keys. +3020 self.spaces[SpaceId::Handshake] +3021 .path_space(PathId::ZERO) +3022 .and_then(|pns| pns.largest_acked_packet) +3023 .is_some() +3024 || self.spaces[SpaceId::Data] +3025 .path_space(path) +3026 .and_then(|pns| pns.largest_acked_packet) +3027 .is_some() +3028 || (self.spaces[SpaceId::Data].crypto.is_some() +3029 && self.spaces[SpaceId::Handshake].crypto.is_none()) +3030 } +3031 +3032 /// Resets the the [`PathTimer::LossDetection`] timer to the next instant it may be needed +3033 /// +3034 /// The timer must fire if either: +3035 /// - An ack-eliciting packet we sent needs to be declared lost. +3036 /// - A tail-loss probe needs to be sent. +3037 /// +3038 /// See [`Connection::on_loss_detection_timeout`] for details. +3039 fn set_loss_detection_timer(&mut self, now: Instant, path_id: PathId) { +3040 if self.state.is_closed() { +3041 // No loss detection takes place on closed connections, and `close_common` already +3042 // stopped time timer. Ensure we don't restart it inadvertently, e.g. in response to a +3043 // reordered packet being handled by state-insensitive code. +3044 return; +3045 } +3046 +3047 if let Some((loss_time, _)) = self.loss_time_and_space(path_id) { +3048 // Time threshold loss detection. +3049 self.timers.set( +3050 Timer::PerPath(path_id, PathTimer::LossDetection), +3051 loss_time, +3052 self.qlog.with_time(now), +3053 ); +3054 return; +3055 } +3056 +3057 // Determine which PN space to arm PTO for. +3058 // Calculate PTO duration +3059 if let Some((timeout, _)) = self.pto_time_and_space(now, path_id) { +3060 self.timers.set( +3061 Timer::PerPath(path_id, PathTimer::LossDetection), +3062 timeout, +3063 self.qlog.with_time(now), +3064 ); +3065 } else { +3066 self.timers.stop( +3067 Timer::PerPath(path_id, PathTimer::LossDetection), +3068 self.qlog.with_time(now), +3069 ); +3070 } +3071 } +3072 +3073 /// The maximum probe timeout across all paths +3074 /// +3075 /// If `is_closing` is set to `true` it will filter out paths that have not yet been used. +3076 /// +3077 /// See [`Connection::pto`] +3078 fn pto_max_path(&self, space: SpaceId, is_closing: bool) -> Duration { +3079 match space { +3080 SpaceId::Initial | SpaceId::Handshake => self.pto(space, PathId::ZERO), +3081 SpaceId::Data => self +3082 .paths +3083 .iter() +3084 .filter_map(|(path_id, state)| { +3085 if is_closing && state.data.total_sent == 0 && state.data.total_recvd == 0 { +3086 // If we are closing and haven't sent anything yet, do not include +3087 None +3088 } else { +3089 let pto = self.pto(space, *path_id); +3090 Some(pto) +3091 } +3092 }) +3093 .max() +3094 .expect("there should be one at least path"), +3095 } +3096 } +3097 +3098 /// Probe Timeout +3099 /// +3100 /// The PTO is logically the time in which you'd expect to receive an acknowledgement +3101 /// for a packet. So approximately RTT + max_ack_delay. +3102 fn pto(&self, space: SpaceId, path_id: PathId) -> Duration { +3103 let max_ack_delay = match space { +3104 SpaceId::Initial | SpaceId::Handshake => Duration::ZERO, +3105 SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(), +3106 }; +3107 self.path_data(path_id).rtt.pto_base() + max_ack_delay +3108 } +3109 +3110 fn on_packet_authenticated( +3111 &mut self, +3112 now: Instant, +3113 space_id: SpaceId, +3114 path_id: PathId, +3115 ecn: Option<EcnCodepoint>, +3116 packet: Option<u64>, +3117 spin: bool, +3118 is_1rtt: bool, +3119 ) { +3120 self.total_authed_packets += 1; +3121 if let Some(last_allowed_receive) = self +3122 .paths +3123 .get(&path_id) +3124 .and_then(|path| path.data.last_allowed_receive) +3125 { +3126 if now > last_allowed_receive { +3127 warn!("received data on path which we abandoned more than 3 * PTO ago"); +3128 // The peer failed to respond with a PATH_ABANDON in time. +3129 if !self.state.is_closed() { +3130 // TODO(flub): What should the error code be? +3131 self.state.move_to_closed(TransportError::NO_ERROR( +3132 "peer failed to respond with PATH_ABANDON in time", +3133 )); +3134 self.close_common(); +3135 self.set_close_timer(now); +3136 self.close = true; +3137 } +3138 return; +3139 } +3140 } +3141 +3142 self.reset_keep_alive(path_id, now); +3143 self.reset_idle_timeout(now, space_id, path_id); +3144 self.permit_idle_reset = true; +3145 self.receiving_ecn |= ecn.is_some(); +3146 if let Some(x) = ecn { +3147 let space = &mut self.spaces[space_id]; +3148 space.for_path(path_id).ecn_counters += x; +3149 +3150 if x.is_ce() { +3151 space +3152 .for_path(path_id) +3153 .pending_acks +3154 .set_immediate_ack_required(); +3155 } +3156 } +3157 +3158 let packet = match packet { +3159 Some(x) => x, +3160 None => return, +3161 }; +3162 match &self.side { +3163 ConnectionSide::Client { .. } => { +3164 // If we received a handshake packet that authenticated, then we're talking to +3165 // the real server. From now on we should no longer allow the server to migrate +3166 // its address. +3167 if space_id == SpaceId::Handshake { +3168 if let Some(hs) = self.state.as_handshake_mut() { +3169 hs.allow_server_migration = false; +3170 } +3171 } +3172 } +3173 ConnectionSide::Server { .. } => { +3174 if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake +3175 { +3176 // A server stops sending and processing Initial packets when it receives its first Handshake packet. +3177 self.discard_space(now, SpaceId::Initial); +3178 } +3179 if self.zero_rtt_crypto.is_some() && is_1rtt { +3180 // Discard 0-RTT keys soon after receiving a 1-RTT packet +3181 self.set_key_discard_timer(now, space_id) +3182 } +3183 } 3184 } -3185 } -3186 -3187 /// Probe Timeout -3188 /// -3189 /// The PTO is logically the time in which you'd expect to receive an acknowledgement -3190 /// for a packet. So approximately RTT + max_ack_delay. -3191 fn pto(&self, space: SpaceId, path_id: PathId) -> Duration { -3192 let max_ack_delay = match space { -3193 SpaceId::Initial | SpaceId::Handshake => Duration::ZERO, -3194 SpaceId::Data => self.ack_frequency.max_ack_delay_for_pto(), -3195 }; -3196 self.path_data(path_id).rtt.pto_base() + max_ack_delay -3197 } -3198 -3199 fn on_packet_authenticated( -3200 &mut self, -3201 now: Instant, -3202 space_id: SpaceId, -3203 path_id: PathId, -3204 ecn: Option<EcnCodepoint>, -3205 packet: Option<u64>, -3206 spin: bool, -3207 is_1rtt: bool, -3208 ) { -3209 self.total_authed_packets += 1; -3210 if let Some(last_allowed_receive) = self -3211 .paths -3212 .get(&path_id) -3213 .and_then(|path| path.data.last_allowed_receive) -3214 { -3215 if now > last_allowed_receive { -3216 warn!("received data on path which we abandoned more than 3 * PTO ago"); -3217 // The peer failed to respond with a PATH_ABANDON in time. -3218 if !self.state.is_closed() { -3219 // TODO(flub): What should the error code be? -3220 self.state.move_to_closed(TransportError::NO_ERROR( -3221 "peer failed to respond with PATH_ABANDON in time", -3222 )); -3223 self.close_common(); -3224 self.set_close_timer(now); -3225 self.close = true; -3226 } -3227 return; +3185 let space = self.spaces[space_id].for_path(path_id); +3186 space.pending_acks.insert_one(packet, now); +3187 if packet >= space.rx_packet.unwrap_or_default() { +3188 space.rx_packet = Some(packet); +3189 // Update outgoing spin bit, inverting iff we're the client +3190 self.spin = self.side.is_client() ^ spin; +3191 } +3192 } +3193 +3194 /// Resets the idle timeout timers +3195 /// +3196 /// Without multipath there is only the connection-wide idle timeout. When multipath is +3197 /// enabled there is an additional per-path idle timeout. +3198 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) { +3199 // First reset the global idle timeout. +3200 if let Some(timeout) = self.idle_timeout { +3201 if self.state.is_closed() { +3202 self.timers +3203 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); +3204 } else { +3205 let dt = cmp::max(timeout, 3 * self.pto_max_path(space, false)); +3206 self.timers.set( +3207 Timer::Conn(ConnTimer::Idle), +3208 now + dt, +3209 self.qlog.with_time(now), +3210 ); +3211 } +3212 } +3213 +3214 // Now handle the per-path state +3215 if let Some(timeout) = self.path_data(path_id).idle_timeout { +3216 if self.state.is_closed() { +3217 self.timers.stop( +3218 Timer::PerPath(path_id, PathTimer::PathIdle), +3219 self.qlog.with_time(now), +3220 ); +3221 } else { +3222 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); +3223 self.timers.set( +3224 Timer::PerPath(path_id, PathTimer::PathIdle), +3225 now + dt, +3226 self.qlog.with_time(now), +3227 ); 3228 } 3229 } -3230 -3231 self.reset_keep_alive(path_id, now); -3232 self.reset_idle_timeout(now, space_id, path_id); -3233 self.permit_idle_reset = true; -3234 self.receiving_ecn |= ecn.is_some(); -3235 if let Some(x) = ecn { -3236 let space = &mut self.spaces[space_id]; -3237 space.for_path(path_id).ecn_counters += x; -3238 -3239 if x.is_ce() { -3240 space -3241 .for_path(path_id) -3242 .pending_acks -3243 .set_immediate_ack_required(); -3244 } -3245 } -3246 -3247 let packet = match packet { -3248 Some(x) => x, -3249 None => return, -3250 }; -3251 match &self.side { -3252 ConnectionSide::Client { .. } => { -3253 // If we received a handshake packet that authenticated, then we're talking to -3254 // the real server. From now on we should no longer allow the server to migrate -3255 // its address. -3256 if space_id == SpaceId::Handshake { -3257 if let Some(hs) = self.state.as_handshake_mut() { -3258 hs.allow_server_migration = false; -3259 } -3260 } -3261 } -3262 ConnectionSide::Server { .. } => { -3263 if self.spaces[SpaceId::Initial].crypto.is_some() && space_id == SpaceId::Handshake -3264 { -3265 // A server stops sending and processing Initial packets when it receives its first Handshake packet. -3266 self.discard_space(now, SpaceId::Initial); -3267 } -3268 if self.zero_rtt_crypto.is_some() && is_1rtt { -3269 // Discard 0-RTT keys soon after receiving a 1-RTT packet -3270 self.set_key_discard_timer(now, space_id) -3271 } -3272 } -3273 } -3274 let space = self.spaces[space_id].for_path(path_id); -3275 space.pending_acks.insert_one(packet, now); -3276 if packet >= space.rx_packet.unwrap_or_default() { -3277 space.rx_packet = Some(packet); -3278 // Update outgoing spin bit, inverting iff we're the client -3279 self.spin = self.side.is_client() ^ spin; -3280 } -3281 } -3282 -3283 /// Resets the idle timeout timers -3284 /// -3285 /// Without multipath there is only the connection-wide idle timeout. When multipath is -3286 /// enabled there is an additional per-path idle timeout. -3287 fn reset_idle_timeout(&mut self, now: Instant, space: SpaceId, path_id: PathId) { -3288 // First reset the global idle timeout. -3289 if let Some(timeout) = self.idle_timeout { -3290 if self.state.is_closed() { -3291 self.timers -3292 .stop(Timer::Conn(ConnTimer::Idle), self.qlog.with_time(now)); -3293 } else { -3294 let dt = cmp::max(timeout, 3 * self.pto_max_path(space, false)); -3295 self.timers.set( -3296 Timer::Conn(ConnTimer::Idle), -3297 now + dt, -3298 self.qlog.with_time(now), -3299 ); -3300 } -3301 } -3302 -3303 // Now handle the per-path state -3304 if let Some(timeout) = self.path_data(path_id).idle_timeout { -3305 if self.state.is_closed() { -3306 self.timers.stop( -3307 Timer::PerPath(path_id, PathTimer::PathIdle), -3308 self.qlog.with_time(now), -3309 ); -3310 } else { -3311 let dt = cmp::max(timeout, 3 * self.pto(space, path_id)); -3312 self.timers.set( -3313 Timer::PerPath(path_id, PathTimer::PathIdle), -3314 now + dt, -3315 self.qlog.with_time(now), -3316 ); -3317 } -3318 } -3319 } -3320 -3321 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers -3322 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { -3323 if !self.state.is_established() { -3324 return; -3325 } -3326 -3327 if let Some(interval) = self.config.keep_alive_interval { -3328 self.timers.set( -3329 Timer::Conn(ConnTimer::KeepAlive), -3330 now + interval, -3331 self.qlog.with_time(now), -3332 ); -3333 } +3230 } +3231 +3232 /// Resets both the [`ConnTimer::KeepAlive`] and [`PathTimer::PathKeepAlive`] timers +3233 fn reset_keep_alive(&mut self, path_id: PathId, now: Instant) { +3234 if !self.state.is_established() { +3235 return; +3236 } +3237 +3238 if let Some(interval) = self.config.keep_alive_interval { +3239 self.timers.set( +3240 Timer::Conn(ConnTimer::KeepAlive), +3241 now + interval, +3242 self.qlog.with_time(now), +3243 ); +3244 } +3245 +3246 if let Some(interval) = self.path_data(path_id).keep_alive { +3247 self.timers.set( +3248 Timer::PerPath(path_id, PathTimer::PathKeepAlive), +3249 now + interval, +3250 self.qlog.with_time(now), +3251 ); +3252 } +3253 } +3254 +3255 /// Sets the timer for when a previously issued CID should be retired next +3256 fn reset_cid_retirement(&mut self, now: Instant) { +3257 if let Some((_path, t)) = self.next_cid_retirement() { +3258 self.timers.set( +3259 Timer::Conn(ConnTimer::PushNewCid), +3260 t, +3261 self.qlog.with_time(now), +3262 ); +3263 } +3264 } +3265 +3266 /// The next time when a previously issued CID should be retired +3267 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { +3268 self.local_cid_state +3269 .iter() +3270 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) +3271 .min_by_key(|(_path_id, timeout)| *timeout) +3272 } +3273 +3274 /// Handle the already-decrypted first packet from the client +3275 /// +3276 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more +3277 /// efficient. +3278 pub(crate) fn handle_first_packet( +3279 &mut self, +3280 now: Instant, +3281 network_path: FourTuple, +3282 ecn: Option<EcnCodepoint>, +3283 packet_number: u64, +3284 packet: InitialPacket, +3285 remaining: Option<BytesMut>, +3286 ) -> Result<(), ConnectionError> { +3287 let span = trace_span!("first recv"); +3288 let _guard = span.enter(); +3289 debug_assert!(self.side.is_server()); +3290 let len = packet.header_data.len() + packet.payload.len(); +3291 let path_id = PathId::ZERO; +3292 self.path_data_mut(path_id).total_recvd = len as u64; +3293 +3294 if let Some(hs) = self.state.as_handshake_mut() { +3295 hs.expected_token = packet.header.token.clone(); +3296 } else { +3297 unreachable!("first packet must be delivered in Handshake state"); +3298 } +3299 +3300 // The first packet is always on PathId::ZERO +3301 self.on_packet_authenticated( +3302 now, +3303 SpaceId::Initial, +3304 path_id, +3305 ecn, +3306 Some(packet_number), +3307 false, +3308 false, +3309 ); +3310 +3311 let packet: Packet = packet.into(); +3312 +3313 let mut qlog = QlogRecvPacket::new(len); +3314 qlog.header(&packet.header, Some(packet_number), path_id); +3315 +3316 self.process_decrypted_packet( +3317 now, +3318 network_path, +3319 path_id, +3320 Some(packet_number), +3321 packet, +3322 &mut qlog, +3323 )?; +3324 self.qlog.emit_packet_received(qlog, now); +3325 if let Some(data) = remaining { +3326 self.handle_coalesced(now, network_path, path_id, ecn, data); +3327 } +3328 +3329 self.qlog.emit_recovery_metrics( +3330 path_id, +3331 &mut self.paths.get_mut(&path_id).unwrap().data, +3332 now, +3333 ); 3334 -3335 if let Some(interval) = self.path_data(path_id).keep_alive { -3336 self.timers.set( -3337 Timer::PerPath(path_id, PathTimer::PathKeepAlive), -3338 now + interval, -3339 self.qlog.with_time(now), -3340 ); -3341 } -3342 } -3343 -3344 /// Sets the timer for when a previously issued CID should be retired next -3345 fn reset_cid_retirement(&mut self, now: Instant) { -3346 if let Some((_path, t)) = self.next_cid_retirement() { -3347 self.timers.set( -3348 Timer::Conn(ConnTimer::PushNewCid), -3349 t, -3350 self.qlog.with_time(now), -3351 ); -3352 } -3353 } -3354 -3355 /// The next time when a previously issued CID should be retired -3356 fn next_cid_retirement(&self) -> Option<(PathId, Instant)> { -3357 self.local_cid_state -3358 .iter() -3359 .filter_map(|(path_id, cid_state)| cid_state.next_timeout().map(|t| (*path_id, t))) -3360 .min_by_key(|(_path_id, timeout)| *timeout) -3361 } -3362 -3363 /// Handle the already-decrypted first packet from the client -3364 /// -3365 /// Decrypting the first packet in the `Endpoint` allows stateless packet handling to be more -3366 /// efficient. -3367 pub(crate) fn handle_first_packet( -3368 &mut self, -3369 now: Instant, -3370 network_path: FourTuple, -3371 ecn: Option<EcnCodepoint>, -3372 packet_number: u64, -3373 packet: InitialPacket, -3374 remaining: Option<BytesMut>, -3375 ) -> Result<(), ConnectionError> { -3376 let span = trace_span!("first recv"); -3377 let _guard = span.enter(); -3378 debug_assert!(self.side.is_server()); -3379 let len = packet.header_data.len() + packet.payload.len(); -3380 let path_id = PathId::ZERO; -3381 self.path_data_mut(path_id).total_recvd = len as u64; -3382 -3383 if let Some(hs) = self.state.as_handshake_mut() { -3384 hs.expected_token = packet.header.token.clone(); +3335 Ok(()) +3336 } +3337 +3338 fn init_0rtt(&mut self, now: Instant) { +3339 let (header, packet) = match self.crypto.early_crypto() { +3340 Some(x) => x, +3341 None => return, +3342 }; +3343 if self.side.is_client() { +3344 match self.crypto.transport_parameters() { +3345 Ok(params) => { +3346 let params = params +3347 .expect("crypto layer didn't supply transport parameters with ticket"); +3348 // Certain values must not be cached +3349 let params = TransportParameters { +3350 initial_src_cid: None, +3351 original_dst_cid: None, +3352 preferred_address: None, +3353 retry_src_cid: None, +3354 stateless_reset_token: None, +3355 min_ack_delay: None, +3356 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, +3357 max_ack_delay: TransportParameters::default().max_ack_delay, +3358 initial_max_path_id: None, +3359 ..params +3360 }; +3361 self.set_peer_params(params); +3362 self.qlog.emit_peer_transport_params_restored(self, now); +3363 } +3364 Err(e) => { +3365 error!("session ticket has malformed transport parameters: {}", e); +3366 return; +3367 } +3368 } +3369 } +3370 trace!("0-RTT enabled"); +3371 self.zero_rtt_enabled = true; +3372 self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet }); +3373 } +3374 +3375 fn read_crypto( +3376 &mut self, +3377 space: SpaceId, +3378 crypto: &frame::Crypto, +3379 payload_len: usize, +3380 ) -> Result<(), TransportError> { +3381 let expected = if !self.state.is_handshake() { +3382 SpaceId::Data +3383 } else if self.highest_space == SpaceId::Initial { +3384 SpaceId::Initial 3385 } else { -3386 unreachable!("first packet must be delivered in Handshake state"); -3387 } -3388 -3389 // The first packet is always on PathId::ZERO -3390 self.on_packet_authenticated( -3391 now, -3392 SpaceId::Initial, -3393 path_id, -3394 ecn, -3395 Some(packet_number), -3396 false, -3397 false, -3398 ); -3399 -3400 let packet: Packet = packet.into(); -3401 -3402 let mut qlog = QlogRecvPacket::new(len); -3403 qlog.header(&packet.header, Some(packet_number), path_id); -3404 -3405 self.process_decrypted_packet( -3406 now, -3407 network_path, -3408 path_id, -3409 Some(packet_number), -3410 packet, -3411 &mut qlog, -3412 )?; -3413 self.qlog.emit_packet_received(qlog, now); -3414 if let Some(data) = remaining { -3415 self.handle_coalesced(now, network_path, path_id, ecn, data); -3416 } -3417 -3418 self.qlog.emit_recovery_metrics( -3419 path_id, -3420 &mut self.paths.get_mut(&path_id).unwrap().data, -3421 now, -3422 ); -3423 -3424 Ok(()) -3425 } -3426 -3427 fn init_0rtt(&mut self, now: Instant) { -3428 let (header, packet) = match self.crypto.early_crypto() { -3429 Some(x) => x, -3430 None => return, -3431 }; -3432 if self.side.is_client() { -3433 match self.crypto.transport_parameters() { -3434 Ok(params) => { -3435 let params = params -3436 .expect("crypto layer didn't supply transport parameters with ticket"); -3437 // Certain values must not be cached -3438 let params = TransportParameters { -3439 initial_src_cid: None, -3440 original_dst_cid: None, -3441 preferred_address: None, -3442 retry_src_cid: None, -3443 stateless_reset_token: None, -3444 min_ack_delay: None, -3445 ack_delay_exponent: TransportParameters::default().ack_delay_exponent, -3446 max_ack_delay: TransportParameters::default().max_ack_delay, -3447 initial_max_path_id: None, -3448 ..params -3449 }; -3450 self.set_peer_params(params); -3451 self.qlog.emit_peer_transport_params_restored(self, now); -3452 } -3453 Err(e) => { -3454 error!("session ticket has malformed transport parameters: {}", e); -3455 return; -3456 } -3457 } -3458 } -3459 trace!("0-RTT enabled"); -3460 self.zero_rtt_enabled = true; -3461 self.zero_rtt_crypto = Some(ZeroRttCrypto { header, packet }); +3386 // On the server, self.highest_space can be Data after receiving the client's first +3387 // flight, but we expect Handshake CRYPTO until the handshake is complete. +3388 SpaceId::Handshake +3389 }; +3390 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT +3391 // packets are illegal, and we don't process 1-RTT packets until the handshake is +3392 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. +3393 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); +3394 +3395 let end = crypto.offset + crypto.data.len() as u64; +3396 if space < expected && end > self.spaces[space].crypto_stream.bytes_read() { +3397 warn!( +3398 "received new {:?} CRYPTO data when expecting {:?}", +3399 space, expected +3400 ); +3401 return Err(TransportError::PROTOCOL_VIOLATION( +3402 "new data at unexpected encryption level", +3403 )); +3404 } +3405 +3406 let space = &mut self.spaces[space]; +3407 let max = end.saturating_sub(space.crypto_stream.bytes_read()); +3408 if max > self.config.crypto_buffer_size as u64 { +3409 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); +3410 } +3411 +3412 space +3413 .crypto_stream +3414 .insert(crypto.offset, crypto.data.clone(), payload_len); +3415 while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) { +3416 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); +3417 if self.crypto.read_handshake(&chunk.bytes)? { +3418 self.events.push_back(Event::HandshakeDataReady); +3419 } +3420 } +3421 +3422 Ok(()) +3423 } +3424 +3425 fn write_crypto(&mut self) { +3426 loop { +3427 let space = self.highest_space; +3428 let mut outgoing = Vec::new(); +3429 if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) { +3430 match space { +3431 SpaceId::Initial => { +3432 self.upgrade_crypto(SpaceId::Handshake, crypto); +3433 } +3434 SpaceId::Handshake => { +3435 self.upgrade_crypto(SpaceId::Data, crypto); +3436 } +3437 _ => unreachable!("got updated secrets during 1-RTT"), +3438 } +3439 } +3440 if outgoing.is_empty() { +3441 if space == self.highest_space { +3442 break; +3443 } else { +3444 // Keys updated, check for more data to send +3445 continue; +3446 } +3447 } +3448 let offset = self.spaces[space].crypto_offset; +3449 let outgoing = Bytes::from(outgoing); +3450 if let Some(hs) = self.state.as_handshake_mut() { +3451 if space == SpaceId::Initial && offset == 0 && self.side.is_client() { +3452 hs.client_hello = Some(outgoing.clone()); +3453 } +3454 } +3455 self.spaces[space].crypto_offset += outgoing.len() as u64; +3456 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); +3457 self.spaces[space].pending.crypto.push_back(frame::Crypto { +3458 offset, +3459 data: outgoing, +3460 }); +3461 } 3462 } 3463 -3464 fn read_crypto( -3465 &mut self, -3466 space: SpaceId, -3467 crypto: &frame::Crypto, -3468 payload_len: usize, -3469 ) -> Result<(), TransportError> { -3470 let expected = if !self.state.is_handshake() { -3471 SpaceId::Data -3472 } else if self.highest_space == SpaceId::Initial { -3473 SpaceId::Initial -3474 } else { -3475 // On the server, self.highest_space can be Data after receiving the client's first -3476 // flight, but we expect Handshake CRYPTO until the handshake is complete. -3477 SpaceId::Handshake -3478 }; -3479 // We can't decrypt Handshake packets when highest_space is Initial, CRYPTO frames in 0-RTT -3480 // packets are illegal, and we don't process 1-RTT packets until the handshake is -3481 // complete. Therefore, we will never see CRYPTO data from a later-than-expected space. -3482 debug_assert!(space <= expected, "received out-of-order CRYPTO data"); -3483 -3484 let end = crypto.offset + crypto.data.len() as u64; -3485 if space < expected && end > self.spaces[space].crypto_stream.bytes_read() { -3486 warn!( -3487 "received new {:?} CRYPTO data when expecting {:?}", -3488 space, expected -3489 ); -3490 return Err(TransportError::PROTOCOL_VIOLATION( -3491 "new data at unexpected encryption level", -3492 )); -3493 } -3494 -3495 let space = &mut self.spaces[space]; -3496 let max = end.saturating_sub(space.crypto_stream.bytes_read()); -3497 if max > self.config.crypto_buffer_size as u64 { -3498 return Err(TransportError::CRYPTO_BUFFER_EXCEEDED("")); -3499 } -3500 -3501 space -3502 .crypto_stream -3503 .insert(crypto.offset, crypto.data.clone(), payload_len); -3504 while let Some(chunk) = space.crypto_stream.read(usize::MAX, true) { -3505 trace!("consumed {} CRYPTO bytes", chunk.bytes.len()); -3506 if self.crypto.read_handshake(&chunk.bytes)? { -3507 self.events.push_back(Event::HandshakeDataReady); -3508 } -3509 } -3510 -3511 Ok(()) -3512 } -3513 -3514 fn write_crypto(&mut self) { -3515 loop { -3516 let space = self.highest_space; -3517 let mut outgoing = Vec::new(); -3518 if let Some(crypto) = self.crypto.write_handshake(&mut outgoing) { -3519 match space { -3520 SpaceId::Initial => { -3521 self.upgrade_crypto(SpaceId::Handshake, crypto); -3522 } -3523 SpaceId::Handshake => { -3524 self.upgrade_crypto(SpaceId::Data, crypto); -3525 } -3526 _ => unreachable!("got updated secrets during 1-RTT"), -3527 } -3528 } -3529 if outgoing.is_empty() { -3530 if space == self.highest_space { -3531 break; -3532 } else { -3533 // Keys updated, check for more data to send -3534 continue; -3535 } -3536 } -3537 let offset = self.spaces[space].crypto_offset; -3538 let outgoing = Bytes::from(outgoing); -3539 if let Some(hs) = self.state.as_handshake_mut() { -3540 if space == SpaceId::Initial && offset == 0 && self.side.is_client() { -3541 hs.client_hello = Some(outgoing.clone()); -3542 } -3543 } -3544 self.spaces[space].crypto_offset += outgoing.len() as u64; -3545 trace!("wrote {} {:?} CRYPTO bytes", outgoing.len(), space); -3546 self.spaces[space].pending.crypto.push_back(frame::Crypto { -3547 offset, -3548 data: outgoing, -3549 }); -3550 } -3551 } -3552 -3553 /// Switch to stronger cryptography during handshake -3554 fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) { -3555 debug_assert!( -3556 self.spaces[space].crypto.is_none(), -3557 "already reached packet space {space:?}" -3558 ); -3559 trace!("{:?} keys ready", space); -3560 if space == SpaceId::Data { -3561 // Precompute the first key update -3562 self.next_crypto = Some( -3563 self.crypto -3564 .next_1rtt_keys() -3565 .expect("handshake should be complete"), -3566 ); -3567 } -3568 -3569 self.spaces[space].crypto = Some(crypto); -3570 debug_assert!(space as usize > self.highest_space as usize); -3571 self.highest_space = space; -3572 if space == SpaceId::Data && self.side.is_client() { -3573 // Discard 0-RTT keys because 1-RTT keys are available. -3574 self.zero_rtt_crypto = None; -3575 } -3576 } -3577 -3578 fn discard_space(&mut self, now: Instant, space_id: SpaceId) { -3579 debug_assert!(space_id != SpaceId::Data); -3580 trace!("discarding {:?} keys", space_id); -3581 if space_id == SpaceId::Initial { -3582 // No longer needed -3583 if let ConnectionSide::Client { token, .. } = &mut self.side { -3584 *token = Bytes::new(); -3585 } -3586 } -3587 let space = &mut self.spaces[space_id]; -3588 space.crypto = None; -3589 let pns = space.for_path(PathId::ZERO); -3590 pns.time_of_last_ack_eliciting_packet = None; -3591 pns.loss_time = None; -3592 pns.loss_probes = 0; -3593 let sent_packets = mem::take(&mut pns.sent_packets); -3594 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); -3595 for (_, packet) in sent_packets.into_iter() { -3596 path.data.remove_in_flight(&packet); -3597 } -3598 -3599 self.set_loss_detection_timer(now, PathId::ZERO) -3600 } -3601 -3602 fn handle_coalesced( -3603 &mut self, -3604 now: Instant, -3605 network_path: FourTuple, -3606 path_id: PathId, -3607 ecn: Option<EcnCodepoint>, -3608 data: BytesMut, -3609 ) { -3610 self.path_data_mut(path_id) -3611 .inc_total_recvd(data.len() as u64); -3612 let mut remaining = Some(data); -3613 let cid_len = self -3614 .local_cid_state -3615 .values() -3616 .map(|cid_state| cid_state.cid_len()) -3617 .next() -3618 .expect("one cid_state must exist"); -3619 while let Some(data) = remaining { -3620 match PartialDecode::new( -3621 data, -3622 &FixedLengthConnectionIdParser::new(cid_len), -3623 &[self.version], -3624 self.endpoint_config.grease_quic_bit, -3625 ) { -3626 Ok((partial_decode, rest)) => { -3627 remaining = rest; -3628 self.handle_decode(now, network_path, path_id, ecn, partial_decode); -3629 } -3630 Err(e) => { -3631 trace!("malformed header: {}", e); -3632 return; -3633 } -3634 } -3635 } -3636 } -3637 -3638 fn handle_decode( -3639 &mut self, -3640 now: Instant, -3641 network_path: FourTuple, -3642 path_id: PathId, -3643 ecn: Option<EcnCodepoint>, -3644 partial_decode: PartialDecode, -3645 ) { -3646 let qlog = QlogRecvPacket::new(partial_decode.len()); -3647 if let Some(decoded) = packet_crypto::unprotect_header( -3648 partial_decode, -3649 &self.spaces, -3650 self.zero_rtt_crypto.as_ref(), -3651 self.peer_params.stateless_reset_token, -3652 ) { -3653 self.handle_packet( -3654 now, -3655 network_path, -3656 path_id, -3657 ecn, -3658 decoded.packet, -3659 decoded.stateless_reset, -3660 qlog, -3661 ); -3662 } -3663 } -3664 -3665 fn handle_packet( -3666 &mut self, -3667 now: Instant, -3668 network_path: FourTuple, -3669 path_id: PathId, -3670 ecn: Option<EcnCodepoint>, -3671 packet: Option<Packet>, -3672 stateless_reset: bool, -3673 mut qlog: QlogRecvPacket, -3674 ) { -3675 self.stats.udp_rx.ios += 1; -3676 if let Some(ref packet) = packet { -3677 trace!( -3678 "got {:?} packet ({} bytes) from {} using id {}", -3679 packet.header.space(), -3680 packet.payload.len() + packet.header_data.len(), -3681 network_path, -3682 packet.header.dst_cid(), -3683 ); -3684 } -3685 -3686 if self.is_handshaking() { -3687 if path_id != PathId::ZERO { -3688 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); -3689 return; -3690 } -3691 if network_path != self.path_data_mut(path_id).network_path { -3692 if let Some(hs) = self.state.as_handshake() { -3693 if hs.allow_server_migration { -3694 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); -3695 self.path_data_mut(path_id).network_path = network_path; -3696 self.qlog.emit_tuple_assigned(path_id, network_path, now); -3697 } else { -3698 debug!("discarding packet with unexpected remote during handshake"); -3699 return; -3700 } -3701 } else { -3702 debug!("discarding packet with unexpected remote during handshake"); -3703 return; -3704 } -3705 } -3706 } -3707 -3708 let was_closed = self.state.is_closed(); -3709 let was_drained = self.state.is_drained(); -3710 -3711 let decrypted = match packet { -3712 None => Err(None), -3713 Some(mut packet) => self -3714 .decrypt_packet(now, path_id, &mut packet) -3715 .map(move |number| (packet, number)), -3716 }; -3717 let result = match decrypted { -3718 _ if stateless_reset => { -3719 debug!("got stateless reset"); -3720 Err(ConnectionError::Reset) -3721 } -3722 Err(Some(e)) => { -3723 warn!("illegal packet: {}", e); -3724 Err(e.into()) -3725 } -3726 Err(None) => { -3727 debug!("failed to authenticate packet"); -3728 self.authentication_failures += 1; -3729 let integrity_limit = self.spaces[self.highest_space] -3730 .crypto -3731 .as_ref() -3732 .unwrap() -3733 .packet -3734 .local -3735 .integrity_limit(); -3736 if self.authentication_failures > integrity_limit { -3737 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) -3738 } else { -3739 return; -3740 } -3741 } -3742 Ok((packet, number)) => { -3743 qlog.header(&packet.header, number, path_id); -3744 let span = match number { -3745 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), -3746 None => trace_span!("recv", space = ?packet.header.space()), -3747 }; -3748 let _guard = span.enter(); -3749 -3750 let dedup = self.spaces[packet.header.space()] -3751 .path_space_mut(path_id) -3752 .map(|pns| &mut pns.dedup); -3753 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { -3754 debug!("discarding possible duplicate packet"); -3755 self.qlog.emit_packet_received(qlog, now); -3756 return; -3757 } else if self.state.is_handshake() && packet.header.is_short() { -3758 // TODO: SHOULD buffer these to improve reordering tolerance. -3759 trace!("dropping short packet during handshake"); -3760 self.qlog.emit_packet_received(qlog, now); -3761 return; -3762 } else { -3763 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header { -3764 if let Some(hs) = self.state.as_handshake() { -3765 if self.side.is_server() && token != &hs.expected_token { -3766 // Clients must send the same retry token in every Initial. Initial -3767 // packets can be spoofed, so we discard rather than killing the -3768 // connection. -3769 warn!("discarding Initial with invalid retry token"); -3770 self.qlog.emit_packet_received(qlog, now); -3771 return; -3772 } -3773 } -3774 } -3775 -3776 if !self.state.is_closed() { -3777 let spin = match packet.header { -3778 Header::Short { spin, .. } => spin, -3779 _ => false, -3780 }; -3781 -3782 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { -3783 // Only the client is allowed to open paths -3784 self.ensure_path(path_id, network_path, now, number); -3785 } -3786 if self.paths.contains_key(&path_id) { -3787 self.on_packet_authenticated( -3788 now, -3789 packet.header.space(), -3790 path_id, -3791 ecn, -3792 number, -3793 spin, -3794 packet.header.is_1rtt(), -3795 ); -3796 } -3797 } -3798 -3799 let res = self.process_decrypted_packet( -3800 now, -3801 network_path, -3802 path_id, -3803 number, -3804 packet, -3805 &mut qlog, -3806 ); -3807 -3808 self.qlog.emit_packet_received(qlog, now); -3809 res -3810 } -3811 } -3812 }; -3813 -3814 // State transitions for error cases -3815 if let Err(conn_err) = result { -3816 match conn_err { -3817 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), -3818 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), -3819 ConnectionError::Reset -3820 | ConnectionError::TransportError(TransportError { -3821 code: TransportErrorCode::AEAD_LIMIT_REACHED, -3822 .. -3823 }) => { -3824 self.state.move_to_drained(Some(conn_err)); -3825 } -3826 ConnectionError::TimedOut => { -3827 unreachable!("timeouts aren't generated by packet processing"); -3828 } -3829 ConnectionError::TransportError(err) => { -3830 debug!("closing connection due to transport error: {}", err); -3831 self.state.move_to_closed(err); -3832 } -3833 ConnectionError::VersionMismatch => { -3834 self.state.move_to_draining(Some(conn_err)); -3835 } -3836 ConnectionError::LocallyClosed => { -3837 unreachable!("LocallyClosed isn't generated by packet processing"); -3838 } -3839 ConnectionError::CidsExhausted => { -3840 unreachable!("CidsExhausted isn't generated by packet processing"); +3464 /// Switch to stronger cryptography during handshake +3465 fn upgrade_crypto(&mut self, space: SpaceId, crypto: Keys) { +3466 debug_assert!( +3467 self.spaces[space].crypto.is_none(), +3468 "already reached packet space {space:?}" +3469 ); +3470 trace!("{:?} keys ready", space); +3471 if space == SpaceId::Data { +3472 // Precompute the first key update +3473 self.next_crypto = Some( +3474 self.crypto +3475 .next_1rtt_keys() +3476 .expect("handshake should be complete"), +3477 ); +3478 } +3479 +3480 self.spaces[space].crypto = Some(crypto); +3481 debug_assert!(space as usize > self.highest_space as usize); +3482 self.highest_space = space; +3483 if space == SpaceId::Data && self.side.is_client() { +3484 // Discard 0-RTT keys because 1-RTT keys are available. +3485 self.zero_rtt_crypto = None; +3486 } +3487 } +3488 +3489 fn discard_space(&mut self, now: Instant, space_id: SpaceId) { +3490 debug_assert!(space_id != SpaceId::Data); +3491 trace!("discarding {:?} keys", space_id); +3492 if space_id == SpaceId::Initial { +3493 // No longer needed +3494 if let ConnectionSide::Client { token, .. } = &mut self.side { +3495 *token = Bytes::new(); +3496 } +3497 } +3498 let space = &mut self.spaces[space_id]; +3499 space.crypto = None; +3500 let pns = space.for_path(PathId::ZERO); +3501 pns.time_of_last_ack_eliciting_packet = None; +3502 pns.loss_time = None; +3503 pns.loss_probes = 0; +3504 let sent_packets = mem::take(&mut pns.sent_packets); +3505 let path = self.paths.get_mut(&PathId::ZERO).unwrap(); +3506 for (_, packet) in sent_packets.into_iter() { +3507 path.data.remove_in_flight(&packet); +3508 } +3509 +3510 self.set_loss_detection_timer(now, PathId::ZERO) +3511 } +3512 +3513 fn handle_coalesced( +3514 &mut self, +3515 now: Instant, +3516 network_path: FourTuple, +3517 path_id: PathId, +3518 ecn: Option<EcnCodepoint>, +3519 data: BytesMut, +3520 ) { +3521 self.path_data_mut(path_id) +3522 .inc_total_recvd(data.len() as u64); +3523 let mut remaining = Some(data); +3524 let cid_len = self +3525 .local_cid_state +3526 .values() +3527 .map(|cid_state| cid_state.cid_len()) +3528 .next() +3529 .expect("one cid_state must exist"); +3530 while let Some(data) = remaining { +3531 match PartialDecode::new( +3532 data, +3533 &FixedLengthConnectionIdParser::new(cid_len), +3534 &[self.version], +3535 self.endpoint_config.grease_quic_bit, +3536 ) { +3537 Ok((partial_decode, rest)) => { +3538 remaining = rest; +3539 self.handle_decode(now, network_path, path_id, ecn, partial_decode); +3540 } +3541 Err(e) => { +3542 trace!("malformed header: {}", e); +3543 return; +3544 } +3545 } +3546 } +3547 } +3548 +3549 fn handle_decode( +3550 &mut self, +3551 now: Instant, +3552 network_path: FourTuple, +3553 path_id: PathId, +3554 ecn: Option<EcnCodepoint>, +3555 partial_decode: PartialDecode, +3556 ) { +3557 let qlog = QlogRecvPacket::new(partial_decode.len()); +3558 if let Some(decoded) = packet_crypto::unprotect_header( +3559 partial_decode, +3560 &self.spaces, +3561 self.zero_rtt_crypto.as_ref(), +3562 self.peer_params.stateless_reset_token, +3563 ) { +3564 self.handle_packet( +3565 now, +3566 network_path, +3567 path_id, +3568 ecn, +3569 decoded.packet, +3570 decoded.stateless_reset, +3571 qlog, +3572 ); +3573 } +3574 } +3575 +3576 fn handle_packet( +3577 &mut self, +3578 now: Instant, +3579 network_path: FourTuple, +3580 path_id: PathId, +3581 ecn: Option<EcnCodepoint>, +3582 packet: Option<Packet>, +3583 stateless_reset: bool, +3584 mut qlog: QlogRecvPacket, +3585 ) { +3586 self.stats.udp_rx.ios += 1; +3587 if let Some(ref packet) = packet { +3588 trace!( +3589 "got {:?} packet ({} bytes) from {} using id {}", +3590 packet.header.space(), +3591 packet.payload.len() + packet.header_data.len(), +3592 network_path, +3593 packet.header.dst_cid(), +3594 ); +3595 } +3596 +3597 if self.is_handshaking() { +3598 if path_id != PathId::ZERO { +3599 debug!(%network_path, %path_id, "discarding multipath packet during handshake"); +3600 return; +3601 } +3602 if network_path != self.path_data_mut(path_id).network_path { +3603 if let Some(hs) = self.state.as_handshake() { +3604 if hs.allow_server_migration { +3605 trace!(%network_path, prev = %self.path_data(path_id).network_path, "server migrated to new remote"); +3606 self.path_data_mut(path_id).network_path = network_path; +3607 self.qlog.emit_tuple_assigned(path_id, network_path, now); +3608 } else { +3609 debug!("discarding packet with unexpected remote during handshake"); +3610 return; +3611 } +3612 } else { +3613 debug!("discarding packet with unexpected remote during handshake"); +3614 return; +3615 } +3616 } +3617 } +3618 +3619 let was_closed = self.state.is_closed(); +3620 let was_drained = self.state.is_drained(); +3621 +3622 let decrypted = match packet { +3623 None => Err(None), +3624 Some(mut packet) => self +3625 .decrypt_packet(now, path_id, &mut packet) +3626 .map(move |number| (packet, number)), +3627 }; +3628 let result = match decrypted { +3629 _ if stateless_reset => { +3630 debug!("got stateless reset"); +3631 Err(ConnectionError::Reset) +3632 } +3633 Err(Some(e)) => { +3634 warn!("illegal packet: {}", e); +3635 Err(e.into()) +3636 } +3637 Err(None) => { +3638 debug!("failed to authenticate packet"); +3639 self.authentication_failures += 1; +3640 let integrity_limit = self.spaces[self.highest_space] +3641 .crypto +3642 .as_ref() +3643 .unwrap() +3644 .packet +3645 .local +3646 .integrity_limit(); +3647 if self.authentication_failures > integrity_limit { +3648 Err(TransportError::AEAD_LIMIT_REACHED("integrity limit violated").into()) +3649 } else { +3650 return; +3651 } +3652 } +3653 Ok((packet, number)) => { +3654 qlog.header(&packet.header, number, path_id); +3655 let span = match number { +3656 Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn), +3657 None => trace_span!("recv", space = ?packet.header.space()), +3658 }; +3659 let _guard = span.enter(); +3660 +3661 let dedup = self.spaces[packet.header.space()] +3662 .path_space_mut(path_id) +3663 .map(|pns| &mut pns.dedup); +3664 if number.zip(dedup).is_some_and(|(n, d)| d.insert(n)) { +3665 debug!("discarding possible duplicate packet"); +3666 self.qlog.emit_packet_received(qlog, now); +3667 return; +3668 } else if self.state.is_handshake() && packet.header.is_short() { +3669 // TODO: SHOULD buffer these to improve reordering tolerance. +3670 trace!("dropping short packet during handshake"); +3671 self.qlog.emit_packet_received(qlog, now); +3672 return; +3673 } else { +3674 if let Header::Initial(InitialHeader { ref token, .. }) = packet.header { +3675 if let Some(hs) = self.state.as_handshake() { +3676 if self.side.is_server() && token != &hs.expected_token { +3677 // Clients must send the same retry token in every Initial. Initial +3678 // packets can be spoofed, so we discard rather than killing the +3679 // connection. +3680 warn!("discarding Initial with invalid retry token"); +3681 self.qlog.emit_packet_received(qlog, now); +3682 return; +3683 } +3684 } +3685 } +3686 +3687 if !self.state.is_closed() { +3688 let spin = match packet.header { +3689 Header::Short { spin, .. } => spin, +3690 _ => false, +3691 }; +3692 +3693 if self.side().is_server() && !self.abandoned_paths.contains(&path_id) { +3694 // Only the client is allowed to open paths +3695 self.ensure_path(path_id, network_path, now, number); +3696 } +3697 if self.paths.contains_key(&path_id) { +3698 self.on_packet_authenticated( +3699 now, +3700 packet.header.space(), +3701 path_id, +3702 ecn, +3703 number, +3704 spin, +3705 packet.header.is_1rtt(), +3706 ); +3707 } +3708 } +3709 +3710 let res = self.process_decrypted_packet( +3711 now, +3712 network_path, +3713 path_id, +3714 number, +3715 packet, +3716 &mut qlog, +3717 ); +3718 +3719 self.qlog.emit_packet_received(qlog, now); +3720 res +3721 } +3722 } +3723 }; +3724 +3725 // State transitions for error cases +3726 if let Err(conn_err) = result { +3727 match conn_err { +3728 ConnectionError::ApplicationClosed(reason) => self.state.move_to_closed(reason), +3729 ConnectionError::ConnectionClosed(reason) => self.state.move_to_closed(reason), +3730 ConnectionError::Reset +3731 | ConnectionError::TransportError(TransportError { +3732 code: TransportErrorCode::AEAD_LIMIT_REACHED, +3733 .. +3734 }) => { +3735 self.state.move_to_drained(Some(conn_err)); +3736 } +3737 ConnectionError::TimedOut => { +3738 unreachable!("timeouts aren't generated by packet processing"); +3739 } +3740 ConnectionError::TransportError(err) => { +3741 debug!("closing connection due to transport error: {}", err); +3742 self.state.move_to_closed(err); +3743 } +3744 ConnectionError::VersionMismatch => { +3745 self.state.move_to_draining(Some(conn_err)); +3746 } +3747 ConnectionError::LocallyClosed => { +3748 unreachable!("LocallyClosed isn't generated by packet processing"); +3749 } +3750 ConnectionError::CidsExhausted => { +3751 unreachable!("CidsExhausted isn't generated by packet processing"); +3752 } +3753 }; +3754 } +3755 +3756 if !was_closed && self.state.is_closed() { +3757 self.close_common(); +3758 if !self.state.is_drained() { +3759 self.set_close_timer(now); +3760 } +3761 } +3762 if !was_drained && self.state.is_drained() { +3763 self.endpoint_events.push_back(EndpointEventInner::Drained); +3764 // Close timer may have been started previously, e.g. if we sent a close and got a +3765 // stateless reset in response +3766 self.timers +3767 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); +3768 } +3769 +3770 // Transmit CONNECTION_CLOSE if necessary +3771 if matches!(self.state.as_type(), StateType::Closed) { +3772 // If there is no PathData for this PathId the packet was for a brand new +3773 // path. It was a valid packet however, so the remote is valid and we want to +3774 // send CONNECTION_CLOSE. +3775 let path_remote = self +3776 .paths +3777 .get(&path_id) +3778 .map(|p| p.data.network_path) +3779 .unwrap_or(network_path); +3780 self.close = network_path == path_remote; +3781 } +3782 } +3783 +3784 fn process_decrypted_packet( +3785 &mut self, +3786 now: Instant, +3787 network_path: FourTuple, +3788 path_id: PathId, +3789 number: Option<u64>, +3790 packet: Packet, +3791 qlog: &mut QlogRecvPacket, +3792 ) -> Result<(), ConnectionError> { +3793 if !self.paths.contains_key(&path_id) { +3794 // There is a chance this is a server side, first (for this path) packet, which would +3795 // be a protocol violation. It's more likely, however, that this is a packet of a +3796 // pruned path +3797 trace!(%path_id, ?number, "discarding packet for unknown path"); +3798 return Ok(()); +3799 } +3800 let state = match self.state.as_type() { +3801 StateType::Established => { +3802 match packet.header.space() { +3803 SpaceId::Data => self.process_payload( +3804 now, +3805 network_path, +3806 path_id, +3807 number.unwrap(), +3808 packet, +3809 qlog, +3810 )?, +3811 _ if packet.header.has_frames() => { +3812 self.process_early_payload(now, path_id, packet, qlog)? +3813 } +3814 _ => { +3815 trace!("discarding unexpected pre-handshake packet"); +3816 } +3817 } +3818 return Ok(()); +3819 } +3820 StateType::Closed => { +3821 for result in frame::Iter::new(packet.payload.freeze())? { +3822 let frame = match result { +3823 Ok(frame) => frame, +3824 Err(err) => { +3825 debug!("frame decoding error: {err:?}"); +3826 continue; +3827 } +3828 }; +3829 qlog.frame(&frame); +3830 +3831 if let Frame::Padding = frame { +3832 continue; +3833 }; +3834 +3835 self.stats.frame_rx.record(frame.ty()); +3836 +3837 if let Frame::Close(_error) = frame { +3838 self.state.move_to_draining(None); +3839 break; +3840 } 3841 } -3842 }; -3843 } -3844 -3845 if !was_closed && self.state.is_closed() { -3846 self.close_common(); -3847 if !self.state.is_drained() { -3848 self.set_close_timer(now); -3849 } -3850 } -3851 if !was_drained && self.state.is_drained() { -3852 self.endpoint_events.push_back(EndpointEventInner::Drained); -3853 // Close timer may have been started previously, e.g. if we sent a close and got a -3854 // stateless reset in response -3855 self.timers -3856 .stop(Timer::Conn(ConnTimer::Close), self.qlog.with_time(now)); -3857 } -3858 -3859 // Transmit CONNECTION_CLOSE if necessary -3860 if matches!(self.state.as_type(), StateType::Closed) { -3861 // If there is no PathData for this PathId the packet was for a brand new -3862 // path. It was a valid packet however, so the remote is valid and we want to -3863 // send CONNECTION_CLOSE. -3864 let path_remote = self -3865 .paths -3866 .get(&path_id) -3867 .map(|p| p.data.network_path) -3868 .unwrap_or(network_path); -3869 self.close = network_path == path_remote; -3870 } -3871 } -3872 -3873 fn process_decrypted_packet( -3874 &mut self, -3875 now: Instant, -3876 network_path: FourTuple, -3877 path_id: PathId, -3878 number: Option<u64>, -3879 packet: Packet, -3880 qlog: &mut QlogRecvPacket, -3881 ) -> Result<(), ConnectionError> { -3882 if !self.paths.contains_key(&path_id) { -3883 // There is a chance this is a server side, first (for this path) packet, which would -3884 // be a protocol violation. It's more likely, however, that this is a packet of a -3885 // pruned path -3886 trace!(%path_id, ?number, "discarding packet for unknown path"); -3887 return Ok(()); -3888 } -3889 let state = match self.state.as_type() { -3890 StateType::Established => { -3891 match packet.header.space() { -3892 SpaceId::Data => self.process_payload( -3893 now, -3894 network_path, -3895 path_id, -3896 number.unwrap(), -3897 packet, -3898 qlog, -3899 )?, -3900 _ if packet.header.has_frames() => { -3901 self.process_early_payload(now, path_id, packet, qlog)? -3902 } -3903 _ => { -3904 trace!("discarding unexpected pre-handshake packet"); -3905 } -3906 } -3907 return Ok(()); -3908 } -3909 StateType::Closed => { -3910 for result in frame::Iter::new(packet.payload.freeze())? { -3911 let frame = match result { -3912 Ok(frame) => frame, -3913 Err(err) => { -3914 debug!("frame decoding error: {err:?}"); -3915 continue; -3916 } -3917 }; -3918 qlog.frame(&frame); -3919 -3920 if let Frame::Padding = frame { -3921 continue; -3922 }; -3923 -3924 self.stats.frame_rx.record(&frame); -3925 -3926 if let Frame::Close(_error) = frame { -3927 self.state.move_to_draining(None); -3928 break; -3929 } -3930 } -3931 return Ok(()); -3932 } -3933 StateType::Draining | StateType::Drained => return Ok(()), -3934 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), -3935 }; -3936 -3937 match packet.header { -3938 Header::Retry { -3939 src_cid: rem_cid, .. -3940 } => { -3941 debug_assert_eq!(path_id, PathId::ZERO); -3942 if self.side.is_server() { -3943 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); -3944 } -3945 -3946 let is_valid_retry = self -3947 .rem_cids -3948 .get(&path_id) -3949 .map(|cids| cids.active()) -3950 .map(|orig_dst_cid| { -3951 self.crypto.is_valid_retry( -3952 orig_dst_cid, -3953 &packet.header_data, -3954 &packet.payload, -3955 ) -3956 }) -3957 .unwrap_or_default(); -3958 if self.total_authed_packets > 1 -3959 || packet.payload.len() <= 16 // token + 16 byte tag -3960 || !is_valid_retry -3961 { -3962 trace!("discarding invalid Retry"); -3963 // - After the client has received and processed an Initial or Retry -3964 // packet from the server, it MUST discard any subsequent Retry -3965 // packets that it receives. -3966 // - A client MUST discard a Retry packet with a zero-length Retry Token -3967 // field. -3968 // - Clients MUST discard Retry packets that have a Retry Integrity Tag -3969 // that cannot be validated -3970 return Ok(()); -3971 } -3972 -3973 trace!("retrying with CID {}", rem_cid); -3974 let client_hello = state.client_hello.take().unwrap(); -3975 self.retry_src_cid = Some(rem_cid); -3976 self.rem_cids -3977 .get_mut(&path_id) -3978 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") -3979 .update_initial_cid(rem_cid); -3980 self.rem_handshake_cid = rem_cid; -3981 -3982 let space = &mut self.spaces[SpaceId::Initial]; -3983 if let Some(info) = space.for_path(PathId::ZERO).take(0) { -3984 self.on_packet_acked(now, PathId::ZERO, info); -3985 }; -3986 -3987 self.discard_space(now, SpaceId::Initial); // Make sure we clean up after -3988 // any retransmitted Initials -3989 self.spaces[SpaceId::Initial] = { -3990 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); -3991 space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side())); -3992 space.crypto_offset = client_hello.len() as u64; -3993 space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial] -3994 .for_path(path_id) -3995 .next_packet_number; -3996 space.pending.crypto.push_back(frame::Crypto { -3997 offset: 0, -3998 data: client_hello, -3999 }); -4000 space -4001 }; -4002 -4003 // Retransmit all 0-RTT data -4004 let zero_rtt = mem::take( -4005 &mut self.spaces[SpaceId::Data] -4006 .for_path(PathId::ZERO) -4007 .sent_packets, -4008 ); -4009 for (_, info) in zero_rtt.into_iter() { -4010 self.paths -4011 .get_mut(&PathId::ZERO) -4012 .unwrap() -4013 .remove_in_flight(&info); -4014 self.spaces[SpaceId::Data].pending |= info.retransmits; -4015 } -4016 self.streams.retransmit_all_for_0rtt(); -4017 -4018 let token_len = packet.payload.len() - 16; -4019 let ConnectionSide::Client { ref mut token, .. } = self.side else { -4020 unreachable!("we already short-circuited if we're server"); -4021 }; -4022 *token = packet.payload.freeze().split_to(token_len); -4023 -4024 self.state = State::handshake(state::Handshake { -4025 expected_token: Bytes::new(), -4026 rem_cid_set: false, -4027 client_hello: None, -4028 allow_server_migration: true, -4029 }); -4030 Ok(()) -4031 } -4032 Header::Long { -4033 ty: LongType::Handshake, -4034 src_cid: rem_cid, -4035 dst_cid: loc_cid, -4036 .. -4037 } => { -4038 debug_assert_eq!(path_id, PathId::ZERO); -4039 if rem_cid != self.rem_handshake_cid { -4040 debug!( -4041 "discarding packet with mismatched remote CID: {} != {}", -4042 self.rem_handshake_cid, rem_cid -4043 ); -4044 return Ok(()); -4045 } -4046 self.on_path_validated(path_id); -4047 -4048 self.process_early_payload(now, path_id, packet, qlog)?; -4049 if self.state.is_closed() { +3842 return Ok(()); +3843 } +3844 StateType::Draining | StateType::Drained => return Ok(()), +3845 StateType::Handshake => self.state.as_handshake_mut().expect("checked"), +3846 }; +3847 +3848 match packet.header { +3849 Header::Retry { +3850 src_cid: rem_cid, .. +3851 } => { +3852 debug_assert_eq!(path_id, PathId::ZERO); +3853 if self.side.is_server() { +3854 return Err(TransportError::PROTOCOL_VIOLATION("client sent Retry").into()); +3855 } +3856 +3857 let is_valid_retry = self +3858 .rem_cids +3859 .get(&path_id) +3860 .map(|cids| cids.active()) +3861 .map(|orig_dst_cid| { +3862 self.crypto.is_valid_retry( +3863 orig_dst_cid, +3864 &packet.header_data, +3865 &packet.payload, +3866 ) +3867 }) +3868 .unwrap_or_default(); +3869 if self.total_authed_packets > 1 +3870 || packet.payload.len() <= 16 // token + 16 byte tag +3871 || !is_valid_retry +3872 { +3873 trace!("discarding invalid Retry"); +3874 // - After the client has received and processed an Initial or Retry +3875 // packet from the server, it MUST discard any subsequent Retry +3876 // packets that it receives. +3877 // - A client MUST discard a Retry packet with a zero-length Retry Token +3878 // field. +3879 // - Clients MUST discard Retry packets that have a Retry Integrity Tag +3880 // that cannot be validated +3881 return Ok(()); +3882 } +3883 +3884 trace!("retrying with CID {}", rem_cid); +3885 let client_hello = state.client_hello.take().unwrap(); +3886 self.retry_src_cid = Some(rem_cid); +3887 self.rem_cids +3888 .get_mut(&path_id) +3889 .expect("PathId::ZERO not yet abandoned, is_valid_retry would have been false") +3890 .update_initial_cid(rem_cid); +3891 self.rem_handshake_cid = rem_cid; +3892 +3893 let space = &mut self.spaces[SpaceId::Initial]; +3894 if let Some(info) = space.for_path(PathId::ZERO).take(0) { +3895 self.on_packet_acked(now, PathId::ZERO, info); +3896 }; +3897 +3898 self.discard_space(now, SpaceId::Initial); // Make sure we clean up after +3899 // any retransmitted Initials +3900 self.spaces[SpaceId::Initial] = { +3901 let mut space = PacketSpace::new(now, SpaceId::Initial, &mut self.rng); +3902 space.crypto = Some(self.crypto.initial_keys(rem_cid, self.side.side())); +3903 space.crypto_offset = client_hello.len() as u64; +3904 space.for_path(path_id).next_packet_number = self.spaces[SpaceId::Initial] +3905 .for_path(path_id) +3906 .next_packet_number; +3907 space.pending.crypto.push_back(frame::Crypto { +3908 offset: 0, +3909 data: client_hello, +3910 }); +3911 space +3912 }; +3913 +3914 // Retransmit all 0-RTT data +3915 let zero_rtt = mem::take( +3916 &mut self.spaces[SpaceId::Data] +3917 .for_path(PathId::ZERO) +3918 .sent_packets, +3919 ); +3920 for (_, info) in zero_rtt.into_iter() { +3921 self.paths +3922 .get_mut(&PathId::ZERO) +3923 .unwrap() +3924 .remove_in_flight(&info); +3925 self.spaces[SpaceId::Data].pending |= info.retransmits; +3926 } +3927 self.streams.retransmit_all_for_0rtt(); +3928 +3929 let token_len = packet.payload.len() - 16; +3930 let ConnectionSide::Client { ref mut token, .. } = self.side else { +3931 unreachable!("we already short-circuited if we're server"); +3932 }; +3933 *token = packet.payload.freeze().split_to(token_len); +3934 +3935 self.state = State::handshake(state::Handshake { +3936 expected_token: Bytes::new(), +3937 rem_cid_set: false, +3938 client_hello: None, +3939 allow_server_migration: true, +3940 }); +3941 Ok(()) +3942 } +3943 Header::Long { +3944 ty: LongType::Handshake, +3945 src_cid: rem_cid, +3946 dst_cid: loc_cid, +3947 .. +3948 } => { +3949 debug_assert_eq!(path_id, PathId::ZERO); +3950 if rem_cid != self.rem_handshake_cid { +3951 debug!( +3952 "discarding packet with mismatched remote CID: {} != {}", +3953 self.rem_handshake_cid, rem_cid +3954 ); +3955 return Ok(()); +3956 } +3957 self.on_path_validated(path_id); +3958 +3959 self.process_early_payload(now, path_id, packet, qlog)?; +3960 if self.state.is_closed() { +3961 return Ok(()); +3962 } +3963 +3964 if self.crypto.is_handshaking() { +3965 trace!("handshake ongoing"); +3966 return Ok(()); +3967 } +3968 +3969 if self.side.is_client() { +3970 // Client-only because server params were set from the client's Initial +3971 let params = self.crypto.transport_parameters()?.ok_or_else(|| { +3972 TransportError::new( +3973 TransportErrorCode::crypto(0x6d), +3974 "transport parameters missing".to_owned(), +3975 ) +3976 })?; +3977 +3978 if self.has_0rtt() { +3979 if !self.crypto.early_data_accepted().unwrap() { +3980 debug_assert!(self.side.is_client()); +3981 debug!("0-RTT rejected"); +3982 self.accepted_0rtt = false; +3983 self.streams.zero_rtt_rejected(); +3984 +3985 // Discard already-queued frames +3986 self.spaces[SpaceId::Data].pending = Retransmits::default(); +3987 +3988 // Discard 0-RTT packets +3989 let sent_packets = mem::take( +3990 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, +3991 ); +3992 for (_, packet) in sent_packets.into_iter() { +3993 self.paths +3994 .get_mut(&path_id) +3995 .unwrap() +3996 .remove_in_flight(&packet); +3997 } +3998 } else { +3999 self.accepted_0rtt = true; +4000 params.validate_resumption_from(&self.peer_params)?; +4001 } +4002 } +4003 if let Some(token) = params.stateless_reset_token { +4004 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? +4005 let remote = self.path_data(path_id).network_path.remote; +4006 self.endpoint_events +4007 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); +4008 } +4009 self.handle_peer_params(params, loc_cid, rem_cid, now)?; +4010 self.issue_first_cids(now); +4011 } else { +4012 // Server-only +4013 self.spaces[SpaceId::Data].pending.handshake_done = true; +4014 self.discard_space(now, SpaceId::Handshake); +4015 self.events.push_back(Event::HandshakeConfirmed); +4016 trace!("handshake confirmed"); +4017 } +4018 +4019 self.events.push_back(Event::Connected); +4020 self.state.move_to_established(); +4021 trace!("established"); +4022 +4023 // Multipath can only be enabled after the state has reached Established. +4024 // So this can not happen any earlier. +4025 self.issue_first_path_cids(now); +4026 Ok(()) +4027 } +4028 Header::Initial(InitialHeader { +4029 src_cid: rem_cid, +4030 dst_cid: loc_cid, +4031 .. +4032 }) => { +4033 debug_assert_eq!(path_id, PathId::ZERO); +4034 if !state.rem_cid_set { +4035 trace!("switching remote CID to {}", rem_cid); +4036 let mut state = state.clone(); +4037 self.rem_cids +4038 .get_mut(&path_id) +4039 .expect("PathId::ZERO not yet abandoned") +4040 .update_initial_cid(rem_cid); +4041 self.rem_handshake_cid = rem_cid; +4042 self.orig_rem_cid = rem_cid; +4043 state.rem_cid_set = true; +4044 self.state.move_to_handshake(state); +4045 } else if rem_cid != self.rem_handshake_cid { +4046 debug!( +4047 "discarding packet with mismatched remote CID: {} != {}", +4048 self.rem_handshake_cid, rem_cid +4049 ); 4050 return Ok(()); 4051 } 4052 -4053 if self.crypto.is_handshaking() { -4054 trace!("handshake ongoing"); -4055 return Ok(()); -4056 } -4057 -4058 if self.side.is_client() { -4059 // Client-only because server params were set from the client's Initial -4060 let params = self.crypto.transport_parameters()?.ok_or_else(|| { +4053 let starting_space = self.highest_space; +4054 self.process_early_payload(now, path_id, packet, qlog)?; +4055 +4056 if self.side.is_server() +4057 && starting_space == SpaceId::Initial +4058 && self.highest_space != SpaceId::Initial +4059 { +4060 let params = self.crypto.transport_parameters()?.ok_or_else(|| { 4061 TransportError::new( 4062 TransportErrorCode::crypto(0x6d), 4063 "transport parameters missing".to_owned(), 4064 ) 4065 })?; -4066 -4067 if self.has_0rtt() { -4068 if !self.crypto.early_data_accepted().unwrap() { -4069 debug_assert!(self.side.is_client()); -4070 debug!("0-RTT rejected"); -4071 self.accepted_0rtt = false; -4072 self.streams.zero_rtt_rejected(); -4073 -4074 // Discard already-queued frames -4075 self.spaces[SpaceId::Data].pending = Retransmits::default(); -4076 -4077 // Discard 0-RTT packets -4078 let sent_packets = mem::take( -4079 &mut self.spaces[SpaceId::Data].for_path(path_id).sent_packets, -4080 ); -4081 for (_, packet) in sent_packets.into_iter() { -4082 self.paths -4083 .get_mut(&path_id) -4084 .unwrap() -4085 .remove_in_flight(&packet); -4086 } -4087 } else { -4088 self.accepted_0rtt = true; -4089 params.validate_resumption_from(&self.peer_params)?; -4090 } -4091 } -4092 if let Some(token) = params.stateless_reset_token { -4093 // TODO(matheus23): Reset token for a remote, or for a 4-tuple? -4094 let remote = self.path_data(path_id).network_path.remote; -4095 self.endpoint_events -4096 .push_back(EndpointEventInner::ResetToken(path_id, remote, token)); -4097 } -4098 self.handle_peer_params(params, loc_cid, rem_cid, now)?; -4099 self.issue_first_cids(now); -4100 } else { -4101 // Server-only -4102 self.spaces[SpaceId::Data].pending.handshake_done = true; -4103 self.discard_space(now, SpaceId::Handshake); -4104 self.events.push_back(Event::HandshakeConfirmed); -4105 trace!("handshake confirmed"); -4106 } -4107 -4108 self.events.push_back(Event::Connected); -4109 self.state.move_to_established(); -4110 trace!("established"); -4111 -4112 // Multipath can only be enabled after the state has reached Established. -4113 // So this can not happen any earlier. -4114 self.issue_first_path_cids(now); -4115 Ok(()) -4116 } -4117 Header::Initial(InitialHeader { -4118 src_cid: rem_cid, -4119 dst_cid: loc_cid, -4120 .. -4121 }) => { -4122 debug_assert_eq!(path_id, PathId::ZERO); -4123 if !state.rem_cid_set { -4124 trace!("switching remote CID to {}", rem_cid); -4125 let mut state = state.clone(); -4126 self.rem_cids -4127 .get_mut(&path_id) -4128 .expect("PathId::ZERO not yet abandoned") -4129 .update_initial_cid(rem_cid); -4130 self.rem_handshake_cid = rem_cid; -4131 self.orig_rem_cid = rem_cid; -4132 state.rem_cid_set = true; -4133 self.state.move_to_handshake(state); -4134 } else if rem_cid != self.rem_handshake_cid { -4135 debug!( -4136 "discarding packet with mismatched remote CID: {} != {}", -4137 self.rem_handshake_cid, rem_cid -4138 ); -4139 return Ok(()); -4140 } -4141 -4142 let starting_space = self.highest_space; -4143 self.process_early_payload(now, path_id, packet, qlog)?; -4144 -4145 if self.side.is_server() -4146 && starting_space == SpaceId::Initial -4147 && self.highest_space != SpaceId::Initial -4148 { -4149 let params = self.crypto.transport_parameters()?.ok_or_else(|| { -4150 TransportError::new( -4151 TransportErrorCode::crypto(0x6d), -4152 "transport parameters missing".to_owned(), -4153 ) -4154 })?; -4155 self.handle_peer_params(params, loc_cid, rem_cid, now)?; -4156 self.issue_first_cids(now); -4157 self.init_0rtt(now); -4158 } -4159 Ok(()) -4160 } -4161 Header::Long { -4162 ty: LongType::ZeroRtt, -4163 .. -4164 } => { -4165 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; -4166 Ok(()) -4167 } -4168 Header::VersionNegotiate { .. } => { -4169 if self.total_authed_packets > 1 { -4170 return Ok(()); -4171 } -4172 let supported = packet -4173 .payload -4174 .chunks(4) -4175 .any(|x| match <[u8; 4]>::try_from(x) { -4176 Ok(version) => self.version == u32::from_be_bytes(version), -4177 Err(_) => false, -4178 }); -4179 if supported { -4180 return Ok(()); -4181 } -4182 debug!("remote doesn't support our version"); -4183 Err(ConnectionError::VersionMismatch) -4184 } -4185 Header::Short { .. } => unreachable!( -4186 "short packets received during handshake are discarded in handle_packet" -4187 ), -4188 } -4189 } -4190 -4191 /// Process an Initial or Handshake packet payload -4192 fn process_early_payload( -4193 &mut self, -4194 now: Instant, -4195 path_id: PathId, -4196 packet: Packet, -4197 #[allow(unused)] qlog: &mut QlogRecvPacket, -4198 ) -> Result<(), TransportError> { -4199 debug_assert_ne!(packet.header.space(), SpaceId::Data); -4200 debug_assert_eq!(path_id, PathId::ZERO); -4201 let payload_len = packet.payload.len(); -4202 let mut ack_eliciting = false; -4203 for result in frame::Iter::new(packet.payload.freeze())? { -4204 let frame = result?; -4205 qlog.frame(&frame); -4206 let span = match frame { -4207 Frame::Padding => continue, -4208 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), -4209 }; -4210 -4211 self.stats.frame_rx.record(&frame); -4212 -4213 let _guard = span.as_ref().map(|x| x.enter()); -4214 ack_eliciting |= frame.is_ack_eliciting(); +4066 self.handle_peer_params(params, loc_cid, rem_cid, now)?; +4067 self.issue_first_cids(now); +4068 self.init_0rtt(now); +4069 } +4070 Ok(()) +4071 } +4072 Header::Long { +4073 ty: LongType::ZeroRtt, +4074 .. +4075 } => { +4076 self.process_payload(now, network_path, path_id, number.unwrap(), packet, qlog)?; +4077 Ok(()) +4078 } +4079 Header::VersionNegotiate { .. } => { +4080 if self.total_authed_packets > 1 { +4081 return Ok(()); +4082 } +4083 let supported = packet +4084 .payload +4085 .chunks(4) +4086 .any(|x| match <[u8; 4]>::try_from(x) { +4087 Ok(version) => self.version == u32::from_be_bytes(version), +4088 Err(_) => false, +4089 }); +4090 if supported { +4091 return Ok(()); +4092 } +4093 debug!("remote doesn't support our version"); +4094 Err(ConnectionError::VersionMismatch) +4095 } +4096 Header::Short { .. } => unreachable!( +4097 "short packets received during handshake are discarded in handle_packet" +4098 ), +4099 } +4100 } +4101 +4102 /// Process an Initial or Handshake packet payload +4103 fn process_early_payload( +4104 &mut self, +4105 now: Instant, +4106 path_id: PathId, +4107 packet: Packet, +4108 #[allow(unused)] qlog: &mut QlogRecvPacket, +4109 ) -> Result<(), TransportError> { +4110 debug_assert_ne!(packet.header.space(), SpaceId::Data); +4111 debug_assert_eq!(path_id, PathId::ZERO); +4112 let payload_len = packet.payload.len(); +4113 let mut ack_eliciting = false; +4114 for result in frame::Iter::new(packet.payload.freeze())? { +4115 let frame = result?; +4116 qlog.frame(&frame); +4117 let span = match frame { +4118 Frame::Padding => continue, +4119 _ => Some(trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty)), +4120 }; +4121 +4122 self.stats.frame_rx.record(frame.ty()); +4123 +4124 let _guard = span.as_ref().map(|x| x.enter()); +4125 ack_eliciting |= frame.is_ack_eliciting(); +4126 +4127 // Process frames +4128 if frame.is_1rtt() && packet.header.space() != SpaceId::Data { +4129 return Err(TransportError::PROTOCOL_VIOLATION( +4130 "illegal frame type in handshake", +4131 )); +4132 } +4133 +4134 match frame { +4135 Frame::Padding | Frame::Ping => {} +4136 Frame::Crypto(frame) => { +4137 self.read_crypto(packet.header.space(), &frame, payload_len)?; +4138 } +4139 Frame::Ack(ack) => { +4140 self.on_ack_received(now, packet.header.space(), ack)?; +4141 } +4142 Frame::PathAck(ack) => { +4143 span.as_ref() +4144 .map(|span| span.record("path", tracing::field::debug(&ack.path_id))); +4145 self.on_path_ack_received(now, packet.header.space(), ack)?; +4146 } +4147 Frame::Close(reason) => { +4148 self.state.move_to_draining(Some(reason.into())); +4149 return Ok(()); +4150 } +4151 _ => { +4152 let mut err = +4153 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); +4154 err.frame = frame::MaybeFrame::Known(frame.ty()); +4155 return Err(err); +4156 } +4157 } +4158 } +4159 +4160 if ack_eliciting { +4161 // In the initial and handshake spaces, ACKs must be sent immediately +4162 self.spaces[packet.header.space()] +4163 .for_path(path_id) +4164 .pending_acks +4165 .set_immediate_ack_required(); +4166 } +4167 +4168 self.write_crypto(); +4169 Ok(()) +4170 } +4171 +4172 /// Processes the packet payload, always in the data space. +4173 fn process_payload( +4174 &mut self, +4175 now: Instant, +4176 network_path: FourTuple, +4177 path_id: PathId, +4178 number: u64, +4179 packet: Packet, +4180 #[allow(unused)] qlog: &mut QlogRecvPacket, +4181 ) -> Result<(), TransportError> { +4182 let payload = packet.payload.freeze(); +4183 let mut is_probing_packet = true; +4184 let mut close = None; +4185 let payload_len = payload.len(); +4186 let mut ack_eliciting = false; +4187 // if this packet triggers a path migration and includes a observed address frame, it's +4188 // stored here +4189 let mut migration_observed_addr = None; +4190 for result in frame::Iter::new(payload)? { +4191 let frame = result?; +4192 qlog.frame(&frame); +4193 let span = match frame { +4194 Frame::Padding => continue, +4195 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), +4196 }; +4197 +4198 self.stats.frame_rx.record(frame.ty()); +4199 // Crypto, Stream and Datagram frames are special cased in order no pollute +4200 // the log with payload data +4201 match &frame { +4202 Frame::Crypto(f) => { +4203 trace!(offset = f.offset, len = f.data.len(), "got crypto frame"); +4204 } +4205 Frame::Stream(f) => { +4206 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame"); +4207 } +4208 Frame::Datagram(f) => { +4209 trace!(len = f.data.len(), "got datagram frame"); +4210 } +4211 f => { +4212 trace!("got frame {f}"); +4213 } +4214 } 4215 -4216 // Process frames -4217 if frame.is_1rtt() && packet.header.space() != SpaceId::Data { -4218 return Err(TransportError::PROTOCOL_VIOLATION( -4219 "illegal frame type in handshake", -4220 )); -4221 } -4222 -4223 match frame { -4224 Frame::Padding | Frame::Ping => {} -4225 Frame::Crypto(frame) => { -4226 self.read_crypto(packet.header.space(), &frame, payload_len)?; -4227 } -4228 Frame::Ack(ack) => { -4229 self.on_ack_received(now, packet.header.space(), ack)?; -4230 } -4231 Frame::PathAck(ack) => { -4232 span.as_ref() -4233 .map(|span| span.record("path", tracing::field::debug(&ack.path_id))); -4234 self.on_path_ack_received(now, packet.header.space(), ack)?; -4235 } -4236 Frame::Close(reason) => { -4237 self.state.move_to_draining(Some(reason.into())); -4238 return Ok(()); -4239 } -4240 _ => { -4241 let mut err = -4242 TransportError::PROTOCOL_VIOLATION("illegal frame type in handshake"); -4243 err.frame = frame::MaybeFrame::Known(frame.ty()); -4244 return Err(err); -4245 } -4246 } -4247 } -4248 -4249 if ack_eliciting { -4250 // In the initial and handshake spaces, ACKs must be sent immediately -4251 self.spaces[packet.header.space()] -4252 .for_path(path_id) -4253 .pending_acks -4254 .set_immediate_ack_required(); -4255 } -4256 -4257 self.write_crypto(); -4258 Ok(()) -4259 } -4260 -4261 /// Processes the packet payload, always in the data space. -4262 fn process_payload( -4263 &mut self, -4264 now: Instant, -4265 network_path: FourTuple, -4266 path_id: PathId, -4267 number: u64, -4268 packet: Packet, -4269 #[allow(unused)] qlog: &mut QlogRecvPacket, -4270 ) -> Result<(), TransportError> { -4271 let payload = packet.payload.freeze(); -4272 let mut is_probing_packet = true; -4273 let mut close = None; -4274 let payload_len = payload.len(); -4275 let mut ack_eliciting = false; -4276 // if this packet triggers a path migration and includes a observed address frame, it's -4277 // stored here -4278 let mut migration_observed_addr = None; -4279 for result in frame::Iter::new(payload)? { -4280 let frame = result?; -4281 qlog.frame(&frame); -4282 let span = match frame { -4283 Frame::Padding => continue, -4284 _ => trace_span!("frame", ty = %frame.ty(), path = tracing::field::Empty), -4285 }; -4286 -4287 self.stats.frame_rx.record(&frame); -4288 // Crypto, Stream and Datagram frames are special cased in order no pollute -4289 // the log with payload data -4290 match &frame { -4291 Frame::Crypto(f) => { -4292 trace!(offset = f.offset, len = f.data.len(), "got crypto frame"); -4293 } -4294 Frame::Stream(f) => { -4295 trace!(id = %f.id, offset = f.offset, len = f.data.len(), fin = f.fin, "got stream frame"); -4296 } -4297 Frame::Datagram(f) => { -4298 trace!(len = f.data.len(), "got datagram frame"); -4299 } -4300 f => { -4301 trace!("got frame {f}"); -4302 } -4303 } -4304 -4305 let _guard = span.enter(); -4306 if packet.header.is_0rtt() { -4307 match frame { -4308 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { -4309 return Err(TransportError::PROTOCOL_VIOLATION( -4310 "illegal frame type in 0-RTT", -4311 )); -4312 } -4313 _ => { -4314 if frame.is_1rtt() { -4315 return Err(TransportError::PROTOCOL_VIOLATION( -4316 "illegal frame type in 0-RTT", -4317 )); -4318 } -4319 } -4320 } -4321 } -4322 ack_eliciting |= frame.is_ack_eliciting(); -4323 -4324 // Check whether this could be a probing packet -4325 match frame { -4326 Frame::Padding -4327 | Frame::PathChallenge(_) -4328 | Frame::PathResponse(_) -4329 | Frame::NewConnectionId(_) -4330 | Frame::ObservedAddr(_) => {} -4331 _ => { -4332 is_probing_packet = false; -4333 } -4334 } -4335 -4336 match frame { -4337 Frame::Crypto(frame) => { -4338 self.read_crypto(SpaceId::Data, &frame, payload_len)?; -4339 } -4340 Frame::Stream(frame) => { -4341 if self.streams.received(frame, payload_len)?.should_transmit() { -4342 self.spaces[SpaceId::Data].pending.max_data = true; -4343 } -4344 } -4345 Frame::Ack(ack) => { -4346 self.on_ack_received(now, SpaceId::Data, ack)?; -4347 } -4348 Frame::PathAck(ack) => { -4349 span.record("path", tracing::field::debug(&ack.path_id)); -4350 self.on_path_ack_received(now, SpaceId::Data, ack)?; -4351 } -4352 Frame::Padding | Frame::Ping => {} -4353 Frame::Close(reason) => { -4354 close = Some(reason); +4216 let _guard = span.enter(); +4217 if packet.header.is_0rtt() { +4218 match frame { +4219 Frame::Crypto(_) | Frame::Close(Close::Application(_)) => { +4220 return Err(TransportError::PROTOCOL_VIOLATION( +4221 "illegal frame type in 0-RTT", +4222 )); +4223 } +4224 _ => { +4225 if frame.is_1rtt() { +4226 return Err(TransportError::PROTOCOL_VIOLATION( +4227 "illegal frame type in 0-RTT", +4228 )); +4229 } +4230 } +4231 } +4232 } +4233 ack_eliciting |= frame.is_ack_eliciting(); +4234 +4235 // Check whether this could be a probing packet +4236 match frame { +4237 Frame::Padding +4238 | Frame::PathChallenge(_) +4239 | Frame::PathResponse(_) +4240 | Frame::NewConnectionId(_) +4241 | Frame::ObservedAddr(_) => {} +4242 _ => { +4243 is_probing_packet = false; +4244 } +4245 } +4246 +4247 match frame { +4248 Frame::Crypto(frame) => { +4249 self.read_crypto(SpaceId::Data, &frame, payload_len)?; +4250 } +4251 Frame::Stream(frame) => { +4252 if self.streams.received(frame, payload_len)?.should_transmit() { +4253 self.spaces[SpaceId::Data].pending.max_data = true; +4254 } +4255 } +4256 Frame::Ack(ack) => { +4257 self.on_ack_received(now, SpaceId::Data, ack)?; +4258 } +4259 Frame::PathAck(ack) => { +4260 span.record("path", tracing::field::debug(&ack.path_id)); +4261 self.on_path_ack_received(now, SpaceId::Data, ack)?; +4262 } +4263 Frame::Padding | Frame::Ping => {} +4264 Frame::Close(reason) => { +4265 close = Some(reason); +4266 } +4267 Frame::PathChallenge(challenge) => { +4268 let path = &mut self +4269 .path_mut(path_id) +4270 .expect("payload is processed only after the path becomes known"); +4271 path.path_responses.push(number, challenge.0, network_path); +4272 // At this point, update_network_path_or_discard was already called, so +4273 // we don't need to be lenient about `local_ip` possibly mis-matching. +4274 if network_path == path.network_path { +4275 // PATH_CHALLENGE on active path, possible off-path packet forwarding +4276 // attack. Send a non-probing packet to recover the active path. +4277 // TODO(flub): No longer true! We now path_challege also to validate +4278 // the path if the path is new, without an RFC9000-style +4279 // migration involved. This means we add in an extra +4280 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do +4281 // so, but it still is something untidy. We should instead +4282 // suppress this when we know the remote is still validating the +4283 // path. +4284 match self.peer_supports_ack_frequency() { +4285 true => self.immediate_ack(path_id), +4286 false => { +4287 self.ping_path(path_id).ok(); +4288 } +4289 } +4290 } +4291 } +4292 Frame::PathResponse(response) => { +4293 let path = self +4294 .paths +4295 .get_mut(&path_id) +4296 .expect("payload is processed only after the path becomes known"); +4297 +4298 use PathTimer::*; +4299 use paths::OnPathResponseReceived::*; +4300 match path +4301 .data +4302 .on_path_response_received(now, response.0, network_path) +4303 { +4304 OnPath { was_open } => { +4305 let qlog = self.qlog.with_time(now); +4306 +4307 self.timers +4308 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); +4309 self.timers +4310 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); +4311 +4312 let next_challenge = path +4313 .data +4314 .earliest_expiring_challenge() +4315 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4316 self.timers.set_or_stop( +4317 Timer::PerPath(path_id, PathChallengeLost), +4318 next_challenge, +4319 qlog, +4320 ); +4321 +4322 if !was_open { +4323 self.events +4324 .push_back(Event::Path(PathEvent::Opened { id: path_id })); +4325 if let Some(observed) = path.data.last_observed_addr_report.as_ref() +4326 { +4327 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4328 id: path_id, +4329 addr: observed.socket_addr(), +4330 })); +4331 } +4332 } +4333 if let Some((_, ref mut prev)) = path.prev { +4334 prev.challenges_sent.clear(); +4335 prev.send_new_challenge = false; +4336 } +4337 } +4338 OffPath => { +4339 debug!("Response to off-path PathChallenge!"); +4340 let next_challenge = path +4341 .data +4342 .earliest_expiring_challenge() +4343 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); +4344 self.timers.set_or_stop( +4345 Timer::PerPath(path_id, PathChallengeLost), +4346 next_challenge, +4347 self.qlog.with_time(now), +4348 ); +4349 } +4350 Invalid { expected } => { +4351 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") +4352 } +4353 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), +4354 } 4355 } -4356 Frame::PathChallenge(challenge) => { -4357 let path = &mut self -4358 .path_mut(path_id) -4359 .expect("payload is processed only after the path becomes known"); -4360 path.path_responses.push(number, challenge.0, network_path); -4361 // At this point, update_network_path_or_discard was already called, so -4362 // we don't need to be lenient about `local_ip` possibly mis-matching. -4363 if network_path == path.network_path { -4364 // PATH_CHALLENGE on active path, possible off-path packet forwarding -4365 // attack. Send a non-probing packet to recover the active path. -4366 // TODO(flub): No longer true! We now path_challege also to validate -4367 // the path if the path is new, without an RFC9000-style -4368 // migration involved. This means we add in an extra -4369 // IMMEDIATE_ACK on some challenges. It isn't really wrong to do -4370 // so, but it still is something untidy. We should instead -4371 // suppress this when we know the remote is still validating the -4372 // path. -4373 match self.peer_supports_ack_frequency() { -4374 true => self.immediate_ack(path_id), -4375 false => { -4376 self.ping_path(path_id).ok(); -4377 } -4378 } +4356 Frame::MaxData(frame::MaxData(bytes)) => { +4357 self.streams.received_max_data(bytes); +4358 } +4359 Frame::MaxStreamData(frame::MaxStreamData { id, offset }) => { +4360 self.streams.received_max_stream_data(id, offset)?; +4361 } +4362 Frame::MaxStreams(frame::MaxStreams { dir, count }) => { +4363 self.streams.received_max_streams(dir, count)?; +4364 } +4365 Frame::ResetStream(frame) => { +4366 if self.streams.received_reset(frame)?.should_transmit() { +4367 self.spaces[SpaceId::Data].pending.max_data = true; +4368 } +4369 } +4370 Frame::DataBlocked { offset } => { +4371 debug!(offset, "peer claims to be blocked at connection level"); +4372 } +4373 Frame::StreamDataBlocked { id, offset } => { +4374 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { +4375 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); +4376 return Err(TransportError::STREAM_STATE_ERROR( +4377 "STREAM_DATA_BLOCKED on send-only stream", +4378 )); 4379 } -4380 } -4381 Frame::PathResponse(response) => { -4382 let path = self -4383 .paths -4384 .get_mut(&path_id) -4385 .expect("payload is processed only after the path becomes known"); -4386 -4387 use PathTimer::*; -4388 use paths::OnPathResponseReceived::*; -4389 match path -4390 .data -4391 .on_path_response_received(now, response.0, network_path) -4392 { -4393 OnPath { was_open } => { -4394 let qlog = self.qlog.with_time(now); -4395 -4396 self.timers -4397 .stop(Timer::PerPath(path_id, PathValidation), qlog.clone()); -4398 self.timers -4399 .stop(Timer::PerPath(path_id, PathOpen), qlog.clone()); -4400 -4401 let next_challenge = path -4402 .data -4403 .earliest_expiring_challenge() -4404 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4405 self.timers.set_or_stop( -4406 Timer::PerPath(path_id, PathChallengeLost), -4407 next_challenge, -4408 qlog, -4409 ); -4410 -4411 if !was_open { -4412 self.events -4413 .push_back(Event::Path(PathEvent::Opened { id: path_id })); -4414 if let Some(observed) = path.data.last_observed_addr_report.as_ref() -4415 { -4416 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4417 id: path_id, -4418 addr: observed.socket_addr(), -4419 })); -4420 } -4421 } -4422 if let Some((_, ref mut prev)) = path.prev { -4423 prev.challenges_sent.clear(); -4424 prev.send_new_challenge = false; -4425 } -4426 } -4427 OffPath => { -4428 debug!("Response to off-path PathChallenge!"); -4429 let next_challenge = path -4430 .data -4431 .earliest_expiring_challenge() -4432 .map(|time| time + self.ack_frequency.max_ack_delay_for_pto()); -4433 self.timers.set_or_stop( -4434 Timer::PerPath(path_id, PathChallengeLost), -4435 next_challenge, -4436 self.qlog.with_time(now), -4437 ); -4438 } -4439 Invalid { expected } => { -4440 debug!(%response, %network_path, %expected, "ignoring invalid PATH_RESPONSE") -4441 } -4442 Unknown => debug!(%response, "ignoring invalid PATH_RESPONSE"), -4443 } -4444 } -4445 Frame::MaxData(bytes) => { -4446 self.streams.received_max_data(bytes); -4447 } -4448 Frame::MaxStreamData { id, offset } => { -4449 self.streams.received_max_stream_data(id, offset)?; -4450 } -4451 Frame::MaxStreams { dir, count } => { -4452 self.streams.received_max_streams(dir, count)?; -4453 } -4454 Frame::ResetStream(frame) => { -4455 if self.streams.received_reset(frame)?.should_transmit() { -4456 self.spaces[SpaceId::Data].pending.max_data = true; -4457 } -4458 } -4459 Frame::DataBlocked { offset } => { -4460 debug!(offset, "peer claims to be blocked at connection level"); -4461 } -4462 Frame::StreamDataBlocked { id, offset } => { -4463 if id.initiator() == self.side.side() && id.dir() == Dir::Uni { -4464 debug!("got STREAM_DATA_BLOCKED on send-only {}", id); -4465 return Err(TransportError::STREAM_STATE_ERROR( -4466 "STREAM_DATA_BLOCKED on send-only stream", -4467 )); -4468 } -4469 debug!( -4470 stream = %id, -4471 offset, "peer claims to be blocked at stream level" -4472 ); -4473 } -4474 Frame::StreamsBlocked { dir, limit } => { -4475 if limit > MAX_STREAM_COUNT { -4476 return Err(TransportError::FRAME_ENCODING_ERROR( -4477 "unrepresentable stream limit", -4478 )); -4479 } -4480 debug!( -4481 "peer claims to be blocked opening more than {} {} streams", -4482 limit, dir -4483 ); -4484 } -4485 Frame::StopSending(frame::StopSending { id, error_code }) => { -4486 if id.initiator() != self.side.side() { -4487 if id.dir() == Dir::Uni { -4488 debug!("got STOP_SENDING on recv-only {}", id); -4489 return Err(TransportError::STREAM_STATE_ERROR( -4490 "STOP_SENDING on recv-only stream", -4491 )); -4492 } -4493 } else if self.streams.is_local_unopened(id) { -4494 return Err(TransportError::STREAM_STATE_ERROR( -4495 "STOP_SENDING on unopened stream", -4496 )); -4497 } -4498 self.streams.received_stop_sending(id, error_code); -4499 } -4500 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { -4501 if let Some(ref path_id) = path_id { -4502 span.record("path", tracing::field::debug(&path_id)); -4503 } -4504 let path_id = path_id.unwrap_or_default(); -4505 match self.local_cid_state.get_mut(&path_id) { -4506 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), -4507 Some(cid_state) => { -4508 let allow_more_cids = cid_state -4509 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; -4510 -4511 // If the path has closed, we do not issue more CIDs for this path -4512 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 -4513 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. -4514 let has_path = !self.abandoned_paths.contains(&path_id); -4515 let allow_more_cids = allow_more_cids && has_path; -4516 -4517 self.endpoint_events -4518 .push_back(EndpointEventInner::RetireConnectionId( -4519 now, -4520 path_id, -4521 sequence, -4522 allow_more_cids, -4523 )); -4524 } -4525 } -4526 } -4527 Frame::NewConnectionId(frame) => { -4528 let path_id = if let Some(path_id) = frame.path_id { -4529 if !self.is_multipath_negotiated() { -4530 return Err(TransportError::PROTOCOL_VIOLATION( -4531 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", -4532 )); -4533 } -4534 if path_id > self.local_max_path_id { -4535 return Err(TransportError::PROTOCOL_VIOLATION( -4536 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", -4537 )); -4538 } -4539 path_id -4540 } else { -4541 PathId::ZERO +4380 debug!( +4381 stream = %id, +4382 offset, "peer claims to be blocked at stream level" +4383 ); +4384 } +4385 Frame::StreamsBlocked { dir, limit } => { +4386 if limit > MAX_STREAM_COUNT { +4387 return Err(TransportError::FRAME_ENCODING_ERROR( +4388 "unrepresentable stream limit", +4389 )); +4390 } +4391 debug!( +4392 "peer claims to be blocked opening more than {} {} streams", +4393 limit, dir +4394 ); +4395 } +4396 Frame::StopSending(frame::StopSending { id, error_code }) => { +4397 if id.initiator() != self.side.side() { +4398 if id.dir() == Dir::Uni { +4399 debug!("got STOP_SENDING on recv-only {}", id); +4400 return Err(TransportError::STREAM_STATE_ERROR( +4401 "STOP_SENDING on recv-only stream", +4402 )); +4403 } +4404 } else if self.streams.is_local_unopened(id) { +4405 return Err(TransportError::STREAM_STATE_ERROR( +4406 "STOP_SENDING on unopened stream", +4407 )); +4408 } +4409 self.streams.received_stop_sending(id, error_code); +4410 } +4411 Frame::RetireConnectionId(frame::RetireConnectionId { path_id, sequence }) => { +4412 if let Some(ref path_id) = path_id { +4413 span.record("path", tracing::field::debug(&path_id)); +4414 } +4415 let path_id = path_id.unwrap_or_default(); +4416 match self.local_cid_state.get_mut(&path_id) { +4417 None => error!(?path_id, "RETIRE_CONNECTION_ID for unknown path"), +4418 Some(cid_state) => { +4419 let allow_more_cids = cid_state +4420 .on_cid_retirement(sequence, self.peer_params.issue_cids_limit())?; +4421 +4422 // If the path has closed, we do not issue more CIDs for this path +4423 // For details see https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-3.2.2 +4424 // > an endpoint SHOULD provide new connection IDs for that path, if still open, using PATH_NEW_CONNECTION_ID frames. +4425 let has_path = !self.abandoned_paths.contains(&path_id); +4426 let allow_more_cids = allow_more_cids && has_path; +4427 +4428 self.endpoint_events +4429 .push_back(EndpointEventInner::RetireConnectionId( +4430 now, +4431 path_id, +4432 sequence, +4433 allow_more_cids, +4434 )); +4435 } +4436 } +4437 } +4438 Frame::NewConnectionId(frame) => { +4439 let path_id = if let Some(path_id) = frame.path_id { +4440 if !self.is_multipath_negotiated() { +4441 return Err(TransportError::PROTOCOL_VIOLATION( +4442 "received PATH_NEW_CONNECTION_ID frame when multipath was not negotiated", +4443 )); +4444 } +4445 if path_id > self.local_max_path_id { +4446 return Err(TransportError::PROTOCOL_VIOLATION( +4447 "PATH_NEW_CONNECTION_ID contains path_id exceeding current max", +4448 )); +4449 } +4450 path_id +4451 } else { +4452 PathId::ZERO +4453 }; +4454 +4455 if self.abandoned_paths.contains(&path_id) { +4456 trace!("ignoring issued CID for abandoned path"); +4457 continue; +4458 } +4459 if let Some(ref path_id) = frame.path_id { +4460 span.record("path", tracing::field::debug(&path_id)); +4461 } +4462 let rem_cids = self +4463 .rem_cids +4464 .entry(path_id) +4465 .or_insert_with(|| CidQueue::new(frame.id)); +4466 if rem_cids.active().is_empty() { +4467 return Err(TransportError::PROTOCOL_VIOLATION( +4468 "NEW_CONNECTION_ID when CIDs aren't in use", +4469 )); +4470 } +4471 if frame.retire_prior_to > frame.sequence { +4472 return Err(TransportError::PROTOCOL_VIOLATION( +4473 "NEW_CONNECTION_ID retiring unissued CIDs", +4474 )); +4475 } +4476 +4477 use crate::cid_queue::InsertError; +4478 match rem_cids.insert(frame) { +4479 Ok(None) if self.path(path_id).is_none() => { +4480 // if this gives us CIDs to open a new path and a nat traversal attempt +4481 // is underway we could try to probe a pending remote +4482 self.continue_nat_traversal_round(now); +4483 } +4484 Ok(None) => {} +4485 Ok(Some((retired, reset_token))) => { +4486 let pending_retired = +4487 &mut self.spaces[SpaceId::Data].pending.retire_cids; +4488 /// Ensure `pending_retired` cannot grow without bound. Limit is +4489 /// somewhat arbitrary but very permissive. +4490 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; +4491 // We don't bother counting in-flight frames because those are bounded +4492 // by congestion control. +4493 if (pending_retired.len() as u64) +4494 .saturating_add(retired.end.saturating_sub(retired.start)) +4495 > MAX_PENDING_RETIRED_CIDS +4496 { +4497 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( +4498 "queued too many retired CIDs", +4499 )); +4500 } +4501 pending_retired.extend(retired.map(|seq| (path_id, seq))); +4502 // TODO(matheus23): Reset token for a remote or a full 4-tuple? +4503 self.set_reset_token(path_id, network_path.remote, reset_token); +4504 } +4505 Err(InsertError::ExceedsLimit) => { +4506 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); +4507 } +4508 Err(InsertError::Retired) => { +4509 trace!("discarding already-retired"); +4510 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a +4511 // range of connection IDs larger than the active connection ID limit +4512 // was retired all at once via retire_prior_to. +4513 self.spaces[SpaceId::Data] +4514 .pending +4515 .retire_cids +4516 .push((path_id, frame.sequence)); +4517 continue; +4518 } +4519 }; +4520 +4521 if self.side.is_server() +4522 && path_id == PathId::ZERO +4523 && self +4524 .rem_cids +4525 .get(&PathId::ZERO) +4526 .map(|cids| cids.active_seq() == 0) +4527 .unwrap_or_default() +4528 { +4529 // We're a server still using the initial remote CID for the client, so +4530 // let's switch immediately to enable clientside stateless resets. +4531 self.update_rem_cid(PathId::ZERO); +4532 } +4533 } +4534 Frame::NewToken(NewToken { token }) => { +4535 let ConnectionSide::Client { +4536 token_store, +4537 server_name, +4538 .. +4539 } = &self.side +4540 else { +4541 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); 4542 }; -4543 -4544 if self.abandoned_paths.contains(&path_id) { -4545 trace!("ignoring issued CID for abandoned path"); -4546 continue; -4547 } -4548 if let Some(ref path_id) = frame.path_id { -4549 span.record("path", tracing::field::debug(&path_id)); -4550 } -4551 let rem_cids = self -4552 .rem_cids -4553 .entry(path_id) -4554 .or_insert_with(|| CidQueue::new(frame.id)); -4555 if rem_cids.active().is_empty() { -4556 return Err(TransportError::PROTOCOL_VIOLATION( -4557 "NEW_CONNECTION_ID when CIDs aren't in use", -4558 )); -4559 } -4560 if frame.retire_prior_to > frame.sequence { -4561 return Err(TransportError::PROTOCOL_VIOLATION( -4562 "NEW_CONNECTION_ID retiring unissued CIDs", -4563 )); +4543 if token.is_empty() { +4544 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); +4545 } +4546 trace!("got new token"); +4547 token_store.insert(server_name, token); +4548 } +4549 Frame::Datagram(datagram) => { +4550 if self +4551 .datagrams +4552 .received(datagram, &self.config.datagram_receive_buffer_size)? +4553 { +4554 self.events.push_back(Event::DatagramReceived); +4555 } +4556 } +4557 Frame::AckFrequency(ack_frequency) => { +4558 // This frame can only be sent in the Data space +4559 +4560 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { +4561 // The AckFrequency frame is stale (we have already received a more +4562 // recent one) +4563 continue; 4564 } 4565 -4566 use crate::cid_queue::InsertError; -4567 match rem_cids.insert(frame) { -4568 Ok(None) if self.path(path_id).is_none() => { -4569 // if this gives us CIDs to open a new path and a nat traversal attempt -4570 // is underway we could try to probe a pending remote -4571 self.continue_nat_traversal_round(now); -4572 } -4573 Ok(None) => {} -4574 Ok(Some((retired, reset_token))) => { -4575 let pending_retired = -4576 &mut self.spaces[SpaceId::Data].pending.retire_cids; -4577 /// Ensure `pending_retired` cannot grow without bound. Limit is -4578 /// somewhat arbitrary but very permissive. -4579 const MAX_PENDING_RETIRED_CIDS: u64 = CidQueue::LEN as u64 * 10; -4580 // We don't bother counting in-flight frames because those are bounded -4581 // by congestion control. -4582 if (pending_retired.len() as u64) -4583 .saturating_add(retired.end.saturating_sub(retired.start)) -4584 > MAX_PENDING_RETIRED_CIDS -4585 { -4586 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR( -4587 "queued too many retired CIDs", -4588 )); -4589 } -4590 pending_retired.extend(retired.map(|seq| (path_id, seq))); -4591 // TODO(matheus23): Reset token for a remote or a full 4-tuple? -4592 self.set_reset_token(path_id, network_path.remote, reset_token); -4593 } -4594 Err(InsertError::ExceedsLimit) => { -4595 return Err(TransportError::CONNECTION_ID_LIMIT_ERROR("")); -4596 } -4597 Err(InsertError::Retired) => { -4598 trace!("discarding already-retired"); -4599 // RETIRE_CONNECTION_ID might not have been previously sent if e.g. a -4600 // range of connection IDs larger than the active connection ID limit -4601 // was retired all at once via retire_prior_to. -4602 self.spaces[SpaceId::Data] -4603 .pending -4604 .retire_cids -4605 .push((path_id, frame.sequence)); -4606 continue; -4607 } -4608 }; -4609 -4610 if self.side.is_server() -4611 && path_id == PathId::ZERO -4612 && self -4613 .rem_cids -4614 .get(&PathId::ZERO) -4615 .map(|cids| cids.active_seq() == 0) -4616 .unwrap_or_default() -4617 { -4618 // We're a server still using the initial remote CID for the client, so -4619 // let's switch immediately to enable clientside stateless resets. -4620 self.update_rem_cid(PathId::ZERO); -4621 } -4622 } -4623 Frame::NewToken(NewToken { token }) => { -4624 let ConnectionSide::Client { -4625 token_store, -4626 server_name, -4627 .. -4628 } = &self.side -4629 else { -4630 return Err(TransportError::PROTOCOL_VIOLATION("client sent NEW_TOKEN")); -4631 }; -4632 if token.is_empty() { -4633 return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); -4634 } -4635 trace!("got new token"); -4636 token_store.insert(server_name, token); -4637 } -4638 Frame::Datagram(datagram) => { -4639 if self -4640 .datagrams -4641 .received(datagram, &self.config.datagram_receive_buffer_size)? -4642 { -4643 self.events.push_back(Event::DatagramReceived); -4644 } -4645 } -4646 Frame::AckFrequency(ack_frequency) => { -4647 // This frame can only be sent in the Data space -4648 -4649 if !self.ack_frequency.ack_frequency_received(&ack_frequency)? { -4650 // The AckFrequency frame is stale (we have already received a more -4651 // recent one) -4652 continue; -4653 } -4654 -4655 // Update the params for all of our paths -4656 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { -4657 space.pending_acks.set_ack_frequency_params(&ack_frequency); -4658 -4659 // Our `max_ack_delay` has been updated, so we may need to adjust -4660 // its associated timeout -4661 if let Some(timeout) = space -4662 .pending_acks -4663 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) -4664 { -4665 self.timers.set( -4666 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), -4667 timeout, -4668 self.qlog.with_time(now), -4669 ); -4670 } -4671 } -4672 } -4673 Frame::ImmediateAck => { -4674 // This frame can only be sent in the Data space -4675 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { -4676 pns.pending_acks.set_immediate_ack_required(); -4677 } -4678 } -4679 Frame::HandshakeDone => { -4680 if self.side.is_server() { -4681 return Err(TransportError::PROTOCOL_VIOLATION( -4682 "client sent HANDSHAKE_DONE", -4683 )); -4684 } -4685 if self.spaces[SpaceId::Handshake].crypto.is_some() { -4686 self.discard_space(now, SpaceId::Handshake); +4566 // Update the params for all of our paths +4567 for (path_id, space) in self.spaces[SpaceId::Data].number_spaces.iter_mut() { +4568 space.pending_acks.set_ack_frequency_params(&ack_frequency); +4569 +4570 // Our `max_ack_delay` has been updated, so we may need to adjust +4571 // its associated timeout +4572 if let Some(timeout) = space +4573 .pending_acks +4574 .max_ack_delay_timeout(self.ack_frequency.max_ack_delay) +4575 { +4576 self.timers.set( +4577 Timer::PerPath(*path_id, PathTimer::MaxAckDelay), +4578 timeout, +4579 self.qlog.with_time(now), +4580 ); +4581 } +4582 } +4583 } +4584 Frame::ImmediateAck => { +4585 // This frame can only be sent in the Data space +4586 for pns in self.spaces[SpaceId::Data].iter_paths_mut() { +4587 pns.pending_acks.set_immediate_ack_required(); +4588 } +4589 } +4590 Frame::HandshakeDone => { +4591 if self.side.is_server() { +4592 return Err(TransportError::PROTOCOL_VIOLATION( +4593 "client sent HANDSHAKE_DONE", +4594 )); +4595 } +4596 if self.spaces[SpaceId::Handshake].crypto.is_some() { +4597 self.discard_space(now, SpaceId::Handshake); +4598 } +4599 self.events.push_back(Event::HandshakeConfirmed); +4600 trace!("handshake confirmed"); +4601 } +4602 Frame::ObservedAddr(observed) => { +4603 // check if params allows the peer to send report and this node to receive it +4604 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); +4605 if !self +4606 .peer_params +4607 .address_discovery_role +4608 .should_report(&self.config.address_discovery_role) +4609 { +4610 return Err(TransportError::PROTOCOL_VIOLATION( +4611 "received OBSERVED_ADDRESS frame when not negotiated", +4612 )); +4613 } +4614 // must only be sent in data space +4615 if packet.header.space() != SpaceId::Data { +4616 return Err(TransportError::PROTOCOL_VIOLATION( +4617 "OBSERVED_ADDRESS frame outside data space", +4618 )); +4619 } +4620 +4621 let path = self.path_data_mut(path_id); +4622 if network_path == path.network_path { +4623 if let Some(updated) = path.update_observed_addr_report(observed) { +4624 if path.open { +4625 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4626 id: path_id, +4627 addr: updated, +4628 })); +4629 } +4630 // otherwise the event is reported when the path is deemed open +4631 } +4632 } else { +4633 // include in migration +4634 migration_observed_addr = Some(observed) +4635 } +4636 } +4637 Frame::PathAbandon(frame::PathAbandon { +4638 path_id, +4639 error_code, +4640 }) => { +4641 span.record("path", tracing::field::debug(&path_id)); +4642 // TODO(flub): don't really know which error code to use here. +4643 let already_abandoned = match self.close_path(now, path_id, error_code.into()) { +4644 Ok(()) => { +4645 trace!("peer abandoned path"); +4646 false +4647 } +4648 Err(ClosePathError::LastOpenPath) => { +4649 trace!("peer abandoned last path, closing connection"); +4650 // TODO(flub): which error code? +4651 return Err(TransportError::NO_ERROR("last path abandoned by peer")); +4652 } +4653 Err(ClosePathError::ClosedPath) => { +4654 trace!("peer abandoned already closed path"); +4655 true +4656 } +4657 }; +4658 // If we receive a retransmit of PATH_ABANDON then we may already have +4659 // abandoned this path locally. In that case the DiscardPath timer +4660 // may already have fired and we no longer have any state for this path. +4661 // Only set this timer if we still have path state. +4662 if self.path(path_id).is_some() && !already_abandoned { +4663 // TODO(flub): Checking is_some() here followed by a number of calls +4664 // that would panic if it was None is really ugly. If only we +4665 // could do something like PathData::pto(). One day we'll have +4666 // unified SpaceId and PathId and this will be possible. +4667 let delay = self.pto(SpaceId::Data, path_id) * 3; +4668 self.timers.set( +4669 Timer::PerPath(path_id, PathTimer::DiscardPath), +4670 now + delay, +4671 self.qlog.with_time(now), +4672 ); +4673 } +4674 } +4675 Frame::PathStatusAvailable(info) => { +4676 span.record("path", tracing::field::debug(&info.path_id)); +4677 if self.is_multipath_negotiated() { +4678 self.on_path_status( +4679 info.path_id, +4680 PathStatus::Available, +4681 info.status_seq_no, +4682 ); +4683 } else { +4684 return Err(TransportError::PROTOCOL_VIOLATION( +4685 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", +4686 )); 4687 } -4688 self.events.push_back(Event::HandshakeConfirmed); -4689 trace!("handshake confirmed"); -4690 } -4691 Frame::ObservedAddr(observed) => { -4692 // check if params allows the peer to send report and this node to receive it -4693 trace!(seq_no = %observed.seq_no, ip = %observed.ip, port = observed.port); -4694 if !self -4695 .peer_params -4696 .address_discovery_role -4697 .should_report(&self.config.address_discovery_role) -4698 { -4699 return Err(TransportError::PROTOCOL_VIOLATION( -4700 "received OBSERVED_ADDRESS frame when not negotiated", -4701 )); -4702 } -4703 // must only be sent in data space -4704 if packet.header.space() != SpaceId::Data { -4705 return Err(TransportError::PROTOCOL_VIOLATION( -4706 "OBSERVED_ADDRESS frame outside data space", -4707 )); -4708 } -4709 -4710 let path = self.path_data_mut(path_id); -4711 if network_path == path.network_path { -4712 if let Some(updated) = path.update_observed_addr_report(observed) { -4713 if path.open { -4714 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -4715 id: path_id, -4716 addr: updated, -4717 })); -4718 } -4719 // otherwise the event is reported when the path is deemed open -4720 } -4721 } else { -4722 // include in migration -4723 migration_observed_addr = Some(observed) -4724 } -4725 } -4726 Frame::PathAbandon(frame::PathAbandon { -4727 path_id, -4728 error_code, -4729 }) => { -4730 span.record("path", tracing::field::debug(&path_id)); -4731 // TODO(flub): don't really know which error code to use here. -4732 let already_abandoned = match self.close_path(now, path_id, error_code.into()) { -4733 Ok(()) => { -4734 trace!("peer abandoned path"); -4735 false -4736 } -4737 Err(ClosePathError::LastOpenPath) => { -4738 trace!("peer abandoned last path, closing connection"); -4739 // TODO(flub): which error code? -4740 return Err(TransportError::NO_ERROR("last path abandoned by peer")); -4741 } -4742 Err(ClosePathError::ClosedPath) => { -4743 trace!("peer abandoned already closed path"); -4744 true -4745 } -4746 }; -4747 // If we receive a retransmit of PATH_ABANDON then we may already have -4748 // abandoned this path locally. In that case the DiscardPath timer -4749 // may already have fired and we no longer have any state for this path. -4750 // Only set this timer if we still have path state. -4751 if self.path(path_id).is_some() && !already_abandoned { -4752 // TODO(flub): Checking is_some() here followed by a number of calls -4753 // that would panic if it was None is really ugly. If only we -4754 // could do something like PathData::pto(). One day we'll have -4755 // unified SpaceId and PathId and this will be possible. -4756 let delay = self.pto(SpaceId::Data, path_id) * 3; -4757 self.timers.set( -4758 Timer::PerPath(path_id, PathTimer::DiscardPath), -4759 now + delay, -4760 self.qlog.with_time(now), -4761 ); -4762 } -4763 } -4764 Frame::PathStatusAvailable(info) => { -4765 span.record("path", tracing::field::debug(&info.path_id)); -4766 if self.is_multipath_negotiated() { -4767 self.on_path_status( -4768 info.path_id, -4769 PathStatus::Available, -4770 info.status_seq_no, -4771 ); -4772 } else { -4773 return Err(TransportError::PROTOCOL_VIOLATION( -4774 "received PATH_STATUS_AVAILABLE frame when multipath was not negotiated", -4775 )); +4688 } +4689 Frame::PathStatusBackup(info) => { +4690 span.record("path", tracing::field::debug(&info.path_id)); +4691 if self.is_multipath_negotiated() { +4692 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); +4693 } else { +4694 return Err(TransportError::PROTOCOL_VIOLATION( +4695 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", +4696 )); +4697 } +4698 } +4699 Frame::MaxPathId(frame::MaxPathId(path_id)) => { +4700 span.record("path", tracing::field::debug(&path_id)); +4701 if !self.is_multipath_negotiated() { +4702 return Err(TransportError::PROTOCOL_VIOLATION( +4703 "received MAX_PATH_ID frame when multipath was not negotiated", +4704 )); +4705 } +4706 // frames that do not increase the path id are ignored +4707 if path_id > self.remote_max_path_id { +4708 self.remote_max_path_id = path_id; +4709 self.issue_first_path_cids(now); +4710 while let Some(true) = self.continue_nat_traversal_round(now) {} +4711 } +4712 } +4713 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { +4714 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +4715 // be treated as a connection error of type PROTOCOL_VIOLATION. +4716 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +4717 if self.is_multipath_negotiated() { +4718 if max_path_id > self.local_max_path_id { +4719 return Err(TransportError::PROTOCOL_VIOLATION( +4720 "PATHS_BLOCKED maximum path identifier was larger than local maximum", +4721 )); +4722 } +4723 debug!("received PATHS_BLOCKED({:?})", max_path_id); +4724 // TODO(@divma): ensure max concurrent paths +4725 } else { +4726 return Err(TransportError::PROTOCOL_VIOLATION( +4727 "received PATHS_BLOCKED frame when not multipath was not negotiated", +4728 )); +4729 } +4730 } +4731 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { +4732 // Nothing to do. This is recorded in the frame stats, but otherwise we +4733 // always issue all CIDs we're allowed to issue, so either this is an +4734 // impatient peer or a bug on our side. +4735 +4736 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST +4737 // be treated as a connection error of type PROTOCOL_VIOLATION. +4738 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> +4739 if self.is_multipath_negotiated() { +4740 if path_id > self.local_max_path_id { +4741 return Err(TransportError::PROTOCOL_VIOLATION( +4742 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", +4743 )); +4744 } +4745 if next_seq.0 +4746 > self +4747 .local_cid_state +4748 .get(&path_id) +4749 .map(|cid_state| cid_state.active_seq().1 + 1) +4750 .unwrap_or_default() +4751 { +4752 return Err(TransportError::PROTOCOL_VIOLATION( +4753 "PATH_CIDS_BLOCKED next sequence number larger than in local state", +4754 )); +4755 } +4756 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); +4757 } else { +4758 return Err(TransportError::PROTOCOL_VIOLATION( +4759 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", +4760 )); +4761 } +4762 } +4763 Frame::AddAddress(addr) => { +4764 let client_state = match self.iroh_hp.client_side_mut() { +4765 Ok(state) => state, +4766 Err(err) => { +4767 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4768 "Nat traversal(ADD_ADDRESS): {err}" +4769 ))); +4770 } +4771 }; +4772 +4773 if !client_state.check_remote_address(&addr) { +4774 // if the address is not valid we flag it, but update anyway +4775 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); 4776 } -4777 } -4778 Frame::PathStatusBackup(info) => { -4779 span.record("path", tracing::field::debug(&info.path_id)); -4780 if self.is_multipath_negotiated() { -4781 self.on_path_status(info.path_id, PathStatus::Backup, info.status_seq_no); -4782 } else { -4783 return Err(TransportError::PROTOCOL_VIOLATION( -4784 "received PATH_STATUS_BACKUP frame when multipath was not negotiated", -4785 )); -4786 } -4787 } -4788 Frame::MaxPathId(frame::MaxPathId(path_id)) => { -4789 span.record("path", tracing::field::debug(&path_id)); -4790 if !self.is_multipath_negotiated() { -4791 return Err(TransportError::PROTOCOL_VIOLATION( -4792 "received MAX_PATH_ID frame when multipath was not negotiated", -4793 )); -4794 } -4795 // frames that do not increase the path id are ignored -4796 if path_id > self.remote_max_path_id { -4797 self.remote_max_path_id = path_id; -4798 self.issue_first_path_cids(now); -4799 while let Some(true) = self.continue_nat_traversal_round(now) {} -4800 } -4801 } -4802 Frame::PathsBlocked(frame::PathsBlocked(max_path_id)) => { -4803 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -4804 // be treated as a connection error of type PROTOCOL_VIOLATION. -4805 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -4806 if self.is_multipath_negotiated() { -4807 if max_path_id > self.local_max_path_id { -4808 return Err(TransportError::PROTOCOL_VIOLATION( -4809 "PATHS_BLOCKED maximum path identifier was larger than local maximum", -4810 )); -4811 } -4812 debug!("received PATHS_BLOCKED({:?})", max_path_id); -4813 // TODO(@divma): ensure max concurrent paths -4814 } else { -4815 return Err(TransportError::PROTOCOL_VIOLATION( -4816 "received PATHS_BLOCKED frame when not multipath was not negotiated", -4817 )); -4818 } -4819 } -4820 Frame::PathCidsBlocked(frame::PathCidsBlocked { path_id, next_seq }) => { -4821 // Nothing to do. This is recorded in the frame stats, but otherwise we -4822 // always issue all CIDs we're allowed to issue, so either this is an -4823 // impatient peer or a bug on our side. -4824 -4825 // Receipt of a value of Maximum Path Identifier or Path Identifier that is higher than the local maximum value MUST -4826 // be treated as a connection error of type PROTOCOL_VIOLATION. -4827 // Ref <https://www.ietf.org/archive/id/draft-ietf-quic-multipath-14.html#name-paths_blocked-and-path_cids> -4828 if self.is_multipath_negotiated() { -4829 if path_id > self.local_max_path_id { -4830 return Err(TransportError::PROTOCOL_VIOLATION( -4831 "PATH_CIDS_BLOCKED path identifier was larger than local maximum", -4832 )); -4833 } -4834 if next_seq.0 -4835 > self -4836 .local_cid_state -4837 .get(&path_id) -4838 .map(|cid_state| cid_state.active_seq().1 + 1) -4839 .unwrap_or_default() -4840 { -4841 return Err(TransportError::PROTOCOL_VIOLATION( -4842 "PATH_CIDS_BLOCKED next sequence number larger than in local state", -4843 )); -4844 } -4845 debug!(%path_id, %next_seq, "received PATH_CIDS_BLOCKED"); -4846 } else { -4847 return Err(TransportError::PROTOCOL_VIOLATION( -4848 "received PATH_CIDS_BLOCKED frame when not multipath was not negotiated", -4849 )); -4850 } -4851 } -4852 Frame::AddAddress(addr) => { -4853 let client_state = match self.iroh_hp.client_side_mut() { -4854 Ok(state) => state, -4855 Err(err) => { -4856 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4857 "Nat traversal(ADD_ADDRESS): {err}" -4858 ))); -4859 } -4860 }; -4861 -4862 if !client_state.check_remote_address(&addr) { -4863 // if the address is not valid we flag it, but update anyway -4864 warn!(?addr, "server sent illegal ADD_ADDRESS frame"); -4865 } -4866 -4867 match client_state.add_remote_address(addr) { -4868 Ok(maybe_added) => { -4869 if let Some(added) = maybe_added { -4870 self.events.push_back(Event::NatTraversal( -4871 iroh_hp::Event::AddressAdded(added), -4872 )); -4873 } -4874 } -4875 Err(e) => { -4876 warn!(%e, "failed to add remote address") -4877 } -4878 } -4879 } -4880 Frame::RemoveAddress(addr) => { -4881 let client_state = match self.iroh_hp.client_side_mut() { -4882 Ok(state) => state, -4883 Err(err) => { -4884 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4885 "Nat traversal(REMOVE_ADDRESS): {err}" -4886 ))); -4887 } -4888 }; -4889 if let Some(removed_addr) = client_state.remove_remote_address(addr) { -4890 self.events -4891 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( -4892 removed_addr, -4893 ))); -4894 } -4895 } -4896 Frame::ReachOut(reach_out) => { -4897 let server_state = match self.iroh_hp.server_side_mut() { -4898 Ok(state) => state, -4899 Err(err) => { -4900 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4901 "Nat traversal(REACH_OUT): {err}" -4902 ))); -4903 } -4904 }; -4905 -4906 if let Err(err) = server_state.handle_reach_out(reach_out) { -4907 return Err(TransportError::PROTOCOL_VIOLATION(format!( -4908 "Nat traversal(REACH_OUT): {err}" -4909 ))); -4910 } -4911 } -4912 } -4913 } -4914 -4915 let space = self.spaces[SpaceId::Data].for_path(path_id); -4916 if space -4917 .pending_acks -4918 .packet_received(now, number, ack_eliciting, &space.dedup) -4919 { -4920 if self.abandoned_paths.contains(&path_id) { -4921 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from -4922 // abandoned paths. -4923 space.pending_acks.set_immediate_ack_required(); -4924 } else { -4925 self.timers.set( -4926 Timer::PerPath(path_id, PathTimer::MaxAckDelay), -4927 now + self.ack_frequency.max_ack_delay, -4928 self.qlog.with_time(now), -4929 ); -4930 } +4777 +4778 match client_state.add_remote_address(addr) { +4779 Ok(maybe_added) => { +4780 if let Some(added) = maybe_added { +4781 self.events.push_back(Event::NatTraversal( +4782 iroh_hp::Event::AddressAdded(added), +4783 )); +4784 } +4785 } +4786 Err(e) => { +4787 warn!(%e, "failed to add remote address") +4788 } +4789 } +4790 } +4791 Frame::RemoveAddress(addr) => { +4792 let client_state = match self.iroh_hp.client_side_mut() { +4793 Ok(state) => state, +4794 Err(err) => { +4795 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4796 "Nat traversal(REMOVE_ADDRESS): {err}" +4797 ))); +4798 } +4799 }; +4800 if let Some(removed_addr) = client_state.remove_remote_address(addr) { +4801 self.events +4802 .push_back(Event::NatTraversal(iroh_hp::Event::AddressRemoved( +4803 removed_addr, +4804 ))); +4805 } +4806 } +4807 Frame::ReachOut(reach_out) => { +4808 let server_state = match self.iroh_hp.server_side_mut() { +4809 Ok(state) => state, +4810 Err(err) => { +4811 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4812 "Nat traversal(REACH_OUT): {err}" +4813 ))); +4814 } +4815 }; +4816 +4817 if let Err(err) = server_state.handle_reach_out(reach_out) { +4818 return Err(TransportError::PROTOCOL_VIOLATION(format!( +4819 "Nat traversal(REACH_OUT): {err}" +4820 ))); +4821 } +4822 } +4823 } +4824 } +4825 +4826 let space = self.spaces[SpaceId::Data].for_path(path_id); +4827 if space +4828 .pending_acks +4829 .packet_received(now, number, ack_eliciting, &space.dedup) +4830 { +4831 if self.abandoned_paths.contains(&path_id) { +4832 // § 3.4.3 QUIC-MULTIPATH: promptly send ACKs for packets received from +4833 // abandoned paths. +4834 space.pending_acks.set_immediate_ack_required(); +4835 } else { +4836 self.timers.set( +4837 Timer::PerPath(path_id, PathTimer::MaxAckDelay), +4838 now + self.ack_frequency.max_ack_delay, +4839 self.qlog.with_time(now), +4840 ); +4841 } +4842 } +4843 +4844 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets +4845 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they +4846 // are only freed, and hence only issue credit, once the application has been notified +4847 // during a read on the stream. +4848 let pending = &mut self.spaces[SpaceId::Data].pending; +4849 self.streams.queue_max_stream_id(pending); +4850 +4851 if let Some(reason) = close { +4852 self.state.move_to_draining(Some(reason.into())); +4853 self.close = true; +4854 } +4855 +4856 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet +4857 && !is_probing_packet +4858 && network_path != self.path_data(path_id).network_path +4859 { +4860 let ConnectionSide::Server { ref server_config } = self.side else { +4861 panic!("packets from unknown remote should be dropped by clients"); +4862 }; +4863 debug_assert!( +4864 server_config.migration, +4865 "migration-initiating packets should have been dropped immediately" +4866 ); +4867 self.migrate(path_id, now, network_path, migration_observed_addr); +4868 // Break linkability, if possible +4869 self.update_rem_cid(path_id); +4870 self.spin = false; +4871 } +4872 +4873 Ok(()) +4874 } +4875 +4876 fn migrate( +4877 &mut self, +4878 path_id: PathId, +4879 now: Instant, +4880 network_path: FourTuple, +4881 observed_addr: Option<ObservedAddr>, +4882 ) { +4883 trace!(%network_path, %path_id, "migration initiated"); +4884 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); +4885 // TODO(@divma): conditions for path migration in multipath are very specific, check them +4886 // again to prevent path migrations that should actually create a new path +4887 +4888 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. +4889 // Note that the congestion window will not grow until validation terminates. Helps mitigate +4890 // amplification attacks performed by spoofing source addresses. +4891 let prev_pto = self.pto(SpaceId::Data, path_id); +4892 let known_path = self.paths.get_mut(&path_id).expect("known path"); +4893 let path = &mut known_path.data; +4894 let mut new_path = if network_path.remote.is_ipv4() +4895 && network_path.remote.ip() == path.network_path.remote.ip() +4896 { +4897 PathData::from_previous(network_path, path, self.path_generation_counter, now) +4898 } else { +4899 let peer_max_udp_payload_size = +4900 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) +4901 .unwrap_or(u16::MAX); +4902 PathData::new( +4903 network_path, +4904 self.allow_mtud, +4905 Some(peer_max_udp_payload_size), +4906 self.path_generation_counter, +4907 now, +4908 &self.config, +4909 ) +4910 }; +4911 new_path.last_observed_addr_report = path.last_observed_addr_report.clone(); +4912 if let Some(report) = observed_addr { +4913 if let Some(updated) = new_path.update_observed_addr_report(report) { +4914 tracing::info!("adding observed addr event from migration"); +4915 self.events.push_back(Event::Path(PathEvent::ObservedAddr { +4916 id: path_id, +4917 addr: updated, +4918 })); +4919 } +4920 } +4921 new_path.send_new_challenge = true; +4922 +4923 let mut prev = mem::replace(path, new_path); +4924 // Don't clobber the original path if the previous one hasn't been validated yet +4925 if !prev.is_validating_path() { +4926 prev.send_new_challenge = true; +4927 // We haven't updated the remote CID yet, this captures the remote CID we were using on +4928 // the previous path. +4929 +4930 known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev)); 4931 } 4932 -4933 // Issue stream ID credit due to ACKs of outgoing finish/resets and incoming finish/resets -4934 // on stopped streams. Incoming finishes/resets on open streams are not handled here as they -4935 // are only freed, and hence only issue credit, once the application has been notified -4936 // during a read on the stream. -4937 let pending = &mut self.spaces[SpaceId::Data].pending; -4938 self.streams.queue_max_stream_id(pending); -4939 -4940 if let Some(reason) = close { -4941 self.state.move_to_draining(Some(reason.into())); -4942 self.close = true; -4943 } -4944 -4945 if Some(number) == self.spaces[SpaceId::Data].for_path(path_id).rx_packet -4946 && !is_probing_packet -4947 && network_path != self.path_data(path_id).network_path -4948 { -4949 let ConnectionSide::Server { ref server_config } = self.side else { -4950 panic!("packets from unknown remote should be dropped by clients"); -4951 }; -4952 debug_assert!( -4953 server_config.migration, -4954 "migration-initiating packets should have been dropped immediately" -4955 ); -4956 self.migrate(path_id, now, network_path, migration_observed_addr); -4957 // Break linkability, if possible -4958 self.update_rem_cid(path_id); -4959 self.spin = false; -4960 } -4961 -4962 Ok(()) -4963 } -4964 -4965 fn migrate( -4966 &mut self, -4967 path_id: PathId, -4968 now: Instant, -4969 network_path: FourTuple, -4970 observed_addr: Option<ObservedAddr>, -4971 ) { -4972 trace!(%network_path, %path_id, "migration initiated"); -4973 self.path_generation_counter = self.path_generation_counter.wrapping_add(1); -4974 // TODO(@divma): conditions for path migration in multipath are very specific, check them -4975 // again to prevent path migrations that should actually create a new path -4976 -4977 // Reset rtt/congestion state for new path unless it looks like a NAT rebinding. -4978 // Note that the congestion window will not grow until validation terminates. Helps mitigate -4979 // amplification attacks performed by spoofing source addresses. -4980 let prev_pto = self.pto(SpaceId::Data, path_id); -4981 let known_path = self.paths.get_mut(&path_id).expect("known path"); -4982 let path = &mut known_path.data; -4983 let mut new_path = if network_path.remote.is_ipv4() -4984 && network_path.remote.ip() == path.network_path.remote.ip() -4985 { -4986 PathData::from_previous(network_path, path, self.path_generation_counter, now) -4987 } else { -4988 let peer_max_udp_payload_size = -4989 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()) -4990 .unwrap_or(u16::MAX); -4991 PathData::new( -4992 network_path, -4993 self.allow_mtud, -4994 Some(peer_max_udp_payload_size), -4995 self.path_generation_counter, -4996 now, -4997 &self.config, -4998 ) -4999 }; -5000 new_path.last_observed_addr_report = path.last_observed_addr_report.clone(); -5001 if let Some(report) = observed_addr { -5002 if let Some(updated) = new_path.update_observed_addr_report(report) { -5003 tracing::info!("adding observed addr event from migration"); -5004 self.events.push_back(Event::Path(PathEvent::ObservedAddr { -5005 id: path_id, -5006 addr: updated, -5007 })); -5008 } -5009 } -5010 new_path.send_new_challenge = true; -5011 -5012 let mut prev = mem::replace(path, new_path); -5013 // Don't clobber the original path if the previous one hasn't been validated yet -5014 if !prev.is_validating_path() { -5015 prev.send_new_challenge = true; -5016 // We haven't updated the remote CID yet, this captures the remote CID we were using on -5017 // the previous path. -5018 -5019 known_path.prev = Some((self.rem_cids.get(&path_id).unwrap().active(), prev)); -5020 } -5021 -5022 // We need to re-assign the correct remote to this path in qlog -5023 self.qlog.emit_tuple_assigned(path_id, network_path, now); -5024 -5025 self.timers.set( -5026 Timer::PerPath(path_id, PathTimer::PathValidation), -5027 now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto), -5028 self.qlog.with_time(now), -5029 ); +4933 // We need to re-assign the correct remote to this path in qlog +4934 self.qlog.emit_tuple_assigned(path_id, network_path, now); +4935 +4936 self.timers.set( +4937 Timer::PerPath(path_id, PathTimer::PathValidation), +4938 now + 3 * cmp::max(self.pto(SpaceId::Data, path_id), prev_pto), +4939 self.qlog.with_time(now), +4940 ); +4941 } +4942 +4943 /// Handle a change in the local address, i.e. an active migration +4944 pub fn local_address_changed(&mut self) { +4945 // TODO(flub): if multipath is enabled this needs to create a new path entirely. +4946 self.update_rem_cid(PathId::ZERO); +4947 self.ping(); +4948 } +4949 +4950 /// Switch to a previously unused remote connection ID, if possible +4951 fn update_rem_cid(&mut self, path_id: PathId) { +4952 let Some((reset_token, retired)) = +4953 self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next()) +4954 else { +4955 return; +4956 }; +4957 +4958 // Retire the current remote CID and any CIDs we had to skip. +4959 self.spaces[SpaceId::Data] +4960 .pending +4961 .retire_cids +4962 .extend(retired.map(|seq| (path_id, seq))); +4963 let remote = self.path_data(path_id).network_path.remote; +4964 self.set_reset_token(path_id, remote, reset_token); +4965 } +4966 +4967 /// Sends this reset token to the endpoint +4968 /// +4969 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer +4970 /// sends a reset token it knows to route it to this connection. See RFC 9000 section +4971 /// 10.3. Stateless Reset. +4972 /// +4973 /// Reset tokens are different for each path, the endpoint identifies paths by peer +4974 /// socket address however, not by path ID. +4975 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { +4976 self.endpoint_events +4977 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); +4978 +4979 // During the handshake the server sends a reset token in the transport +4980 // parameters. When we are the client and we receive the reset token during the +4981 // handshake we want this to affect our peer transport parameters. +4982 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten +4983 // shortly after this was called. And then the params don't have this anymore. +4984 if path_id == PathId::ZERO { +4985 self.peer_params.stateless_reset_token = Some(reset_token); +4986 } +4987 } +4988 +4989 /// Issue an initial set of connection IDs to the peer upon connection +4990 fn issue_first_cids(&mut self, now: Instant) { +4991 if self +4992 .local_cid_state +4993 .get(&PathId::ZERO) +4994 .expect("PathId::ZERO exists when the connection is created") +4995 .cid_len() +4996 == 0 +4997 { +4998 return; +4999 } +5000 +5001 // Subtract 1 to account for the CID we supplied while handshaking +5002 let mut n = self.peer_params.issue_cids_limit() - 1; +5003 if let ConnectionSide::Server { server_config } = &self.side { +5004 if server_config.has_preferred_address() { +5005 // We also sent a CID in the transport parameters +5006 n -= 1; +5007 } +5008 } +5009 self.endpoint_events +5010 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +5011 } +5012 +5013 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued +5014 /// +5015 /// Later CIDs are issued when CIDs expire or are retired by the peer. +5016 fn issue_first_path_cids(&mut self, now: Instant) { +5017 if let Some(max_path_id) = self.max_path_id() { +5018 let mut path_id = self.max_path_id_with_cids.next(); +5019 while path_id <= max_path_id { +5020 self.endpoint_events +5021 .push_back(EndpointEventInner::NeedIdentifiers( +5022 path_id, +5023 now, +5024 self.peer_params.issue_cids_limit(), +5025 )); +5026 path_id = path_id.next(); +5027 } +5028 self.max_path_id_with_cids = max_path_id; +5029 } 5030 } 5031 -5032 /// Handle a change in the local address, i.e. an active migration -5033 pub fn local_address_changed(&mut self) { -5034 // TODO(flub): if multipath is enabled this needs to create a new path entirely. -5035 self.update_rem_cid(PathId::ZERO); -5036 self.ping(); -5037 } -5038 -5039 /// Switch to a previously unused remote connection ID, if possible -5040 fn update_rem_cid(&mut self, path_id: PathId) { -5041 let Some((reset_token, retired)) = -5042 self.rem_cids.get_mut(&path_id).and_then(|cids| cids.next()) -5043 else { -5044 return; -5045 }; -5046 -5047 // Retire the current remote CID and any CIDs we had to skip. -5048 self.spaces[SpaceId::Data] -5049 .pending -5050 .retire_cids -5051 .extend(retired.map(|seq| (path_id, seq))); -5052 let remote = self.path_data(path_id).network_path.remote; -5053 self.set_reset_token(path_id, remote, reset_token); -5054 } -5055 -5056 /// Sends this reset token to the endpoint -5057 /// -5058 /// The endpoint needs to know the reset tokens issued by the peer, so that if the peer -5059 /// sends a reset token it knows to route it to this connection. See RFC 9000 section -5060 /// 10.3. Stateless Reset. -5061 /// -5062 /// Reset tokens are different for each path, the endpoint identifies paths by peer -5063 /// socket address however, not by path ID. -5064 fn set_reset_token(&mut self, path_id: PathId, remote: SocketAddr, reset_token: ResetToken) { -5065 self.endpoint_events -5066 .push_back(EndpointEventInner::ResetToken(path_id, remote, reset_token)); -5067 -5068 // During the handshake the server sends a reset token in the transport -5069 // parameters. When we are the client and we receive the reset token during the -5070 // handshake we want this to affect our peer transport parameters. -5071 // TODO(flub): Pretty sure this is pointless, the entire params is overwritten -5072 // shortly after this was called. And then the params don't have this anymore. -5073 if path_id == PathId::ZERO { -5074 self.peer_params.stateless_reset_token = Some(reset_token); -5075 } -5076 } -5077 -5078 /// Issue an initial set of connection IDs to the peer upon connection -5079 fn issue_first_cids(&mut self, now: Instant) { -5080 if self -5081 .local_cid_state -5082 .get(&PathId::ZERO) -5083 .expect("PathId::ZERO exists when the connection is created") -5084 .cid_len() -5085 == 0 -5086 { -5087 return; -5088 } -5089 -5090 // Subtract 1 to account for the CID we supplied while handshaking -5091 let mut n = self.peer_params.issue_cids_limit() - 1; -5092 if let ConnectionSide::Server { server_config } = &self.side { -5093 if server_config.has_preferred_address() { -5094 // We also sent a CID in the transport parameters -5095 n -= 1; -5096 } -5097 } -5098 self.endpoint_events -5099 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -5100 } -5101 -5102 /// Issues an initial set of CIDs for paths that have not yet had any CIDs issued -5103 /// -5104 /// Later CIDs are issued when CIDs expire or are retired by the peer. -5105 fn issue_first_path_cids(&mut self, now: Instant) { -5106 if let Some(max_path_id) = self.max_path_id() { -5107 let mut path_id = self.max_path_id_with_cids.next(); -5108 while path_id <= max_path_id { -5109 self.endpoint_events -5110 .push_back(EndpointEventInner::NeedIdentifiers( -5111 path_id, -5112 now, -5113 self.peer_params.issue_cids_limit(), -5114 )); -5115 path_id = path_id.next(); -5116 } -5117 self.max_path_id_with_cids = max_path_id; -5118 } -5119 } -5120 -5121 /// Populates a packet with frames -5122 /// -5123 /// This tries to fit as many frames as possible into the packet. -5124 /// -5125 /// *path_exclusive_only* means to only build frames which can only be sent on this -5126 /// *path. This is used in multipath for backup paths while there is still an active -5127 /// *path. -5128 fn populate_packet( -5129 &mut self, -5130 now: Instant, -5131 space_id: SpaceId, -5132 path_id: PathId, -5133 path_exclusive_only: bool, -5134 buf: &mut impl BufMut, -5135 pn: u64, -5136 #[allow(unused)] qlog: &mut QlogSentPacket, -5137 ) -> SentFrames { -5138 let mut sent = SentFrames::default(); -5139 let is_multipath_negotiated = self.is_multipath_negotiated(); -5140 let space = &mut self.spaces[space_id]; -5141 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -5142 let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none(); -5143 space -5144 .for_path(path_id) -5145 .pending_acks -5146 .maybe_ack_non_eliciting(); -5147 -5148 // HANDSHAKE_DONE -5149 if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) { -5150 trace!("HANDSHAKE_DONE"); -5151 buf.write(frame::FrameType::HandshakeDone); -5152 qlog.frame(&Frame::HandshakeDone); -5153 sent.retransmits.get_or_create().handshake_done = true; -5154 // This is just a u8 counter and the frame is typically just sent once -5155 self.stats.frame_tx.handshake_done = -5156 self.stats.frame_tx.handshake_done.saturating_add(1); -5157 } -5158 -5159 // REACH_OUT -5160 // TODO(@divma): path explusive considerations -5161 if let Some((round, addresses)) = space.pending.reach_out.as_mut() { -5162 while let Some(local_addr) = addresses.pop() { -5163 let reach_out = frame::ReachOut::new(*round, local_addr); -5164 if buf.remaining_mut() > reach_out.size() { -5165 trace!(%round, ?local_addr, "REACH_OUT"); -5166 reach_out.encode(buf); -5167 let sent_reachouts = sent -5168 .retransmits -5169 .get_or_create() -5170 .reach_out -5171 .get_or_insert_with(|| (*round, Default::default())); -5172 sent_reachouts.1.push(local_addr); -5173 self.stats.frame_tx.reach_out = self.stats.frame_tx.reach_out.saturating_add(1); -5174 qlog.frame(&Frame::ReachOut(reach_out)); -5175 } else { -5176 addresses.push(local_addr); -5177 break; -5178 } -5179 } -5180 if addresses.is_empty() { -5181 space.pending.reach_out = None; -5182 } -5183 } -5184 -5185 // OBSERVED_ADDR -5186 if !path_exclusive_only -5187 && space_id == SpaceId::Data -5188 && self -5189 .config -5190 .address_discovery_role -5191 .should_report(&self.peer_params.address_discovery_role) -5192 && (!path.observed_addr_sent || space.pending.observed_addr) -5193 { -5194 let frame = -5195 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); -5196 if buf.remaining_mut() > frame.size() { -5197 trace!(seq = %frame.seq_no, ip = %frame.ip, port = frame.port, "OBSERVED_ADDRESS"); -5198 frame.encode(buf); -5199 -5200 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); -5201 path.observed_addr_sent = true; -5202 -5203 self.stats.frame_tx.observed_addr += 1; -5204 sent.retransmits.get_or_create().observed_addr = true; -5205 space.pending.observed_addr = false; -5206 qlog.frame(&Frame::ObservedAddr(frame)); -5207 } -5208 } -5209 -5210 // PING -5211 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { -5212 trace!("PING"); -5213 buf.write(frame::FrameType::Ping); -5214 sent.non_retransmits = true; -5215 self.stats.frame_tx.ping += 1; -5216 qlog.frame(&Frame::Ping); -5217 } -5218 -5219 // IMMEDIATE_ACK -5220 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { -5221 debug_assert_eq!( -5222 space_id, -5223 SpaceId::Data, -5224 "immediate acks must be sent in the data space" -5225 ); -5226 trace!("IMMEDIATE_ACK"); -5227 buf.write(frame::FrameType::ImmediateAck); -5228 sent.non_retransmits = true; -5229 self.stats.frame_tx.immediate_ack += 1; -5230 qlog.frame(&Frame::ImmediateAck); -5231 } -5232 -5233 // ACK -5234 // TODO(flub): Should this send acks for this path anyway? -5235 -5236 if !path_exclusive_only { -5237 for path_id in space -5238 .number_spaces -5239 .iter_mut() -5240 .filter(|(_, pns)| pns.pending_acks.can_send()) -5241 .map(|(&path_id, _)| path_id) -5242 .collect::<Vec<_>>() -5243 { -5244 Self::populate_acks( -5245 now, -5246 self.receiving_ecn, -5247 &mut sent, -5248 path_id, -5249 space_id, -5250 space, -5251 is_multipath_negotiated, -5252 buf, -5253 &mut self.stats, -5254 qlog, -5255 ); -5256 } -5257 } -5258 -5259 // ACK_FREQUENCY -5260 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { -5261 let sequence_number = self.ack_frequency.next_sequence_number(); -5262 -5263 // Safe to unwrap because this is always provided when ACK frequency is enabled -5264 let config = self.config.ack_frequency_config.as_ref().unwrap(); -5265 -5266 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error -5267 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( -5268 path.rtt.get(), -5269 config, -5270 &self.peer_params, -5271 ); -5272 -5273 trace!(?max_ack_delay, "ACK_FREQUENCY"); -5274 -5275 let frame = frame::AckFrequency { -5276 sequence: sequence_number, -5277 ack_eliciting_threshold: config.ack_eliciting_threshold, -5278 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), -5279 reordering_threshold: config.reordering_threshold, -5280 }; -5281 frame.encode(buf); -5282 qlog.frame(&Frame::AckFrequency(frame)); -5283 -5284 sent.retransmits.get_or_create().ack_frequency = true; -5285 -5286 self.ack_frequency -5287 .ack_frequency_sent(path_id, pn, max_ack_delay); -5288 self.stats.frame_tx.ack_frequency += 1; -5289 } -5290 -5291 // PATH_CHALLENGE -5292 if buf.remaining_mut() > frame::PathChallenge::SIZE_BOUND -5293 && space_id == SpaceId::Data -5294 && path.send_new_challenge -5295 && !self.state.is_closed() -5296 // we don't want to send new challenges if we are already closing -5297 { -5298 path.send_new_challenge = false; -5299 -5300 // Generate a new challenge every time we send a new PATH_CHALLENGE -5301 let token = self.rng.random(); -5302 let info = paths::SentChallengeInfo { -5303 sent_instant: now, -5304 network_path: path.network_path, -5305 }; -5306 path.challenges_sent.insert(token, info); -5307 sent.non_retransmits = true; -5308 sent.requires_padding = true; -5309 let challenge = frame::PathChallenge(token); -5310 trace!(frame = %challenge); -5311 buf.write(challenge); -5312 qlog.frame(&Frame::PathChallenge(challenge)); -5313 self.stats.frame_tx.path_challenge += 1; -5314 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); -5315 self.timers.set( -5316 Timer::PerPath(path_id, PathTimer::PathChallengeLost), -5317 now + pto, -5318 self.qlog.with_time(now), -5319 ); -5320 -5321 if is_multipath_negotiated && !path.validated && path.send_new_challenge { -5322 // queue informing the path status along with the challenge -5323 space.pending.path_status.insert(path_id); -5324 } -5325 -5326 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless -5327 // of whether one has already been sent on this path. -5328 if space_id == SpaceId::Data -5329 && self -5330 .config -5331 .address_discovery_role -5332 .should_report(&self.peer_params.address_discovery_role) -5333 { -5334 let frame = frame::ObservedAddr::new( -5335 path.network_path.remote, -5336 self.next_observed_addr_seq_no, -5337 ); -5338 if buf.remaining_mut() > frame.size() { -5339 frame.encode(buf); -5340 qlog.frame(&Frame::ObservedAddr(frame)); +5032 /// Populates a packet with frames +5033 /// +5034 /// This tries to fit as many frames as possible into the packet. +5035 /// +5036 /// *path_exclusive_only* means to only build frames which can only be sent on this +5037 /// *path. This is used in multipath for backup paths while there is still an active +5038 /// *path. +5039 fn populate_packet<'a, 'b>( +5040 &mut self, +5041 now: Instant, +5042 space_id: SpaceId, +5043 path_id: PathId, +5044 path_exclusive_only: bool, +5045 builder: &mut PacketBuilder<'a, 'b>, +5046 ) { +5047 let pn = builder.exact_number; +5048 let is_multipath_negotiated = self.is_multipath_negotiated(); +5049 let stats = &mut self.stats.frame_tx; +5050 let space = &mut self.spaces[space_id]; +5051 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5052 let is_0rtt = space_id == SpaceId::Data && space.crypto.is_none(); +5053 space +5054 .for_path(path_id) +5055 .pending_acks +5056 .maybe_ack_non_eliciting(); +5057 +5058 // HANDSHAKE_DONE +5059 if !is_0rtt && mem::replace(&mut space.pending.handshake_done, false) { +5060 builder.write_frame(frame::HandshakeDone, stats); +5061 } +5062 +5063 // REACH_OUT +5064 // TODO(@divma): path explusive considerations +5065 if let Some((round, addresses)) = space.pending.reach_out.as_mut() { +5066 while let Some(local_addr) = addresses.pop() { +5067 let reach_out = frame::ReachOut::new(*round, local_addr); +5068 if builder.frame_space_remaining() > reach_out.size() { +5069 builder.write_frame(reach_out, stats); +5070 } else { +5071 addresses.push(local_addr); +5072 break; +5073 } +5074 } +5075 if addresses.is_empty() { +5076 space.pending.reach_out = None; +5077 } +5078 } +5079 +5080 // OBSERVED_ADDR +5081 if !path_exclusive_only +5082 && space_id == SpaceId::Data +5083 && self +5084 .config +5085 .address_discovery_role +5086 .should_report(&self.peer_params.address_discovery_role) +5087 && (!path.observed_addr_sent || space.pending.observed_addr) +5088 { +5089 let frame = +5090 frame::ObservedAddr::new(path.network_path.remote, self.next_observed_addr_seq_no); +5091 if builder.frame_space_remaining() > frame.size() { +5092 builder.write_frame(frame, stats); +5093 +5094 self.next_observed_addr_seq_no = self.next_observed_addr_seq_no.saturating_add(1u8); +5095 path.observed_addr_sent = true; +5096 +5097 space.pending.observed_addr = false; +5098 } +5099 } +5100 +5101 // PING +5102 if mem::replace(&mut space.for_path(path_id).ping_pending, false) { +5103 builder.write_frame(frame::Ping, stats); +5104 } +5105 +5106 // IMMEDIATE_ACK +5107 if mem::replace(&mut space.for_path(path_id).immediate_ack_pending, false) { +5108 debug_assert_eq!( +5109 space_id, +5110 SpaceId::Data, +5111 "immediate acks must be sent in the data space" +5112 ); +5113 builder.write_frame(frame::ImmediateAck, stats); +5114 } +5115 +5116 // ACK +5117 // TODO(flub): Should this send acks for this path anyway? +5118 +5119 if !path_exclusive_only { +5120 for path_id in space +5121 .number_spaces +5122 .iter_mut() +5123 .filter(|(_, pns)| pns.pending_acks.can_send()) +5124 .map(|(&path_id, _)| path_id) +5125 .collect::<Vec<_>>() +5126 { +5127 Self::populate_acks( +5128 now, +5129 self.receiving_ecn, +5130 path_id, +5131 space_id, +5132 space, +5133 is_multipath_negotiated, +5134 builder, +5135 stats, +5136 ); +5137 } +5138 } +5139 +5140 // ACK_FREQUENCY +5141 if !path_exclusive_only && mem::replace(&mut space.pending.ack_frequency, false) { +5142 let sequence_number = self.ack_frequency.next_sequence_number(); +5143 +5144 // Safe to unwrap because this is always provided when ACK frequency is enabled +5145 let config = self.config.ack_frequency_config.as_ref().unwrap(); +5146 +5147 // Ensure the delay is within bounds to avoid a PROTOCOL_VIOLATION error +5148 let max_ack_delay = self.ack_frequency.candidate_max_ack_delay( +5149 path.rtt.get(), +5150 config, +5151 &self.peer_params, +5152 ); +5153 +5154 let frame = frame::AckFrequency { +5155 sequence: sequence_number, +5156 ack_eliciting_threshold: config.ack_eliciting_threshold, +5157 request_max_ack_delay: max_ack_delay.as_micros().try_into().unwrap_or(VarInt::MAX), +5158 reordering_threshold: config.reordering_threshold, +5159 }; +5160 builder.write_frame(frame, stats); +5161 +5162 self.ack_frequency +5163 .ack_frequency_sent(path_id, pn, max_ack_delay); +5164 } +5165 +5166 // PATH_CHALLENGE +5167 if builder.frame_space_remaining() > frame::PathChallenge::SIZE_BOUND +5168 && space_id == SpaceId::Data +5169 && path.send_new_challenge +5170 && !self.state.is_closed() +5171 // we don't want to send new challenges if we are already closing +5172 { +5173 path.send_new_challenge = false; +5174 +5175 // Generate a new challenge every time we send a new PATH_CHALLENGE +5176 let token = self.rng.random(); +5177 let info = paths::SentChallengeInfo { +5178 sent_instant: now, +5179 network_path: path.network_path, +5180 }; +5181 path.challenges_sent.insert(token, info); +5182 let challenge = frame::PathChallenge(token); +5183 trace!(frame = %challenge); +5184 builder.write_frame(challenge, stats); +5185 builder.require_padding(); +5186 let pto = self.ack_frequency.max_ack_delay_for_pto() + path.rtt.pto_base(); +5187 self.timers.set( +5188 Timer::PerPath(path_id, PathTimer::PathChallengeLost), +5189 now + pto, +5190 self.qlog.with_time(now), +5191 ); +5192 +5193 if is_multipath_negotiated && !path.validated && path.send_new_challenge { +5194 // queue informing the path status along with the challenge +5195 space.pending.path_status.insert(path_id); +5196 } +5197 +5198 // Always include an OBSERVED_ADDR frame with a PATH_CHALLENGE, regardless +5199 // of whether one has already been sent on this path. +5200 if space_id == SpaceId::Data +5201 && self +5202 .config +5203 .address_discovery_role +5204 .should_report(&self.peer_params.address_discovery_role) +5205 { +5206 let frame = frame::ObservedAddr::new( +5207 path.network_path.remote, +5208 self.next_observed_addr_seq_no, +5209 ); +5210 if builder.frame_space_remaining() > frame.size() { +5211 builder.write_frame(frame, stats); +5212 +5213 self.next_observed_addr_seq_no = +5214 self.next_observed_addr_seq_no.saturating_add(1u8); +5215 path.observed_addr_sent = true; +5216 +5217 space.pending.observed_addr = false; +5218 } +5219 } +5220 } +5221 +5222 // PATH_RESPONSE +5223 if builder.frame_space_remaining() > frame::PathResponse::SIZE_BOUND +5224 && space_id == SpaceId::Data +5225 { +5226 if let Some(token) = path.path_responses.pop_on_path(path.network_path) { +5227 let response = frame::PathResponse(token); +5228 trace!(frame = %response); +5229 builder.write_frame(response, stats); +5230 builder.require_padding(); +5231 +5232 // NOTE: this is technically not required but might be useful to ride the +5233 // request/response nature of path challenges to refresh an observation +5234 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. +5235 if space_id == SpaceId::Data +5236 && self +5237 .config +5238 .address_discovery_role +5239 .should_report(&self.peer_params.address_discovery_role) +5240 { +5241 let frame = frame::ObservedAddr::new( +5242 path.network_path.remote, +5243 self.next_observed_addr_seq_no, +5244 ); +5245 if builder.frame_space_remaining() > frame.size() { +5246 builder.write_frame(frame, stats); +5247 +5248 self.next_observed_addr_seq_no = +5249 self.next_observed_addr_seq_no.saturating_add(1u8); +5250 path.observed_addr_sent = true; +5251 +5252 space.pending.observed_addr = false; +5253 } +5254 } +5255 } +5256 } +5257 +5258 // CRYPTO +5259 while !path_exclusive_only +5260 && builder.frame_space_remaining() > frame::Crypto::SIZE_BOUND +5261 && !is_0rtt +5262 { +5263 let mut frame = match space.pending.crypto.pop_front() { +5264 Some(x) => x, +5265 None => break, +5266 }; +5267 +5268 // Calculate the maximum amount of crypto data we can store in the buffer. +5269 // Since the offset is known, we can reserve the exact size required to encode it. +5270 // For length we reserve 2bytes which allows to encode up to 2^14, +5271 // which is more than what fits into normally sized QUIC frames. +5272 let max_crypto_data_size = builder.frame_space_remaining() +5273 - 1 // Frame Type +5274 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) +5275 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes +5276 +5277 let len = frame +5278 .data +5279 .len() +5280 .min(2usize.pow(14) - 1) +5281 .min(max_crypto_data_size); +5282 +5283 let data = frame.data.split_to(len); +5284 let offset = frame.offset; +5285 let truncated = frame::Crypto { offset, data }; +5286 builder.write_frame(truncated, stats); +5287 +5288 if !frame.data.is_empty() { +5289 frame.offset += len as u64; +5290 space.pending.crypto.push_front(frame); +5291 } +5292 } +5293 +5294 // TODO(flub): maybe this is much higher priority? +5295 // PATH_ABANDON +5296 while !path_exclusive_only +5297 && space_id == SpaceId::Data +5298 && frame::PathAbandon::SIZE_BOUND <= builder.frame_space_remaining() +5299 { +5300 let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else { +5301 break; +5302 }; +5303 let frame = frame::PathAbandon { +5304 path_id, +5305 error_code, +5306 }; +5307 builder.write_frame(frame, stats); +5308 } +5309 +5310 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP +5311 while !path_exclusive_only +5312 && space_id == SpaceId::Data +5313 && frame::PathStatusAvailable::SIZE_BOUND <= builder.frame_space_remaining() +5314 { +5315 let Some(path_id) = space.pending.path_status.pop_first() else { +5316 break; +5317 }; +5318 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { +5319 trace!(%path_id, "discarding queued path status for unknown path"); +5320 continue; +5321 }; +5322 +5323 let seq = path.status.seq(); +5324 match path.local_status() { +5325 PathStatus::Available => { +5326 let frame = frame::PathStatusAvailable { +5327 path_id, +5328 status_seq_no: seq, +5329 }; +5330 builder.write_frame(frame, stats); +5331 } +5332 PathStatus::Backup => { +5333 let frame = frame::PathStatusBackup { +5334 path_id, +5335 status_seq_no: seq, +5336 }; +5337 builder.write_frame(frame, stats); +5338 } +5339 } +5340 } 5341 -5342 self.next_observed_addr_seq_no = -5343 self.next_observed_addr_seq_no.saturating_add(1u8); -5344 path.observed_addr_sent = true; -5345 -5346 self.stats.frame_tx.observed_addr += 1; -5347 sent.retransmits.get_or_create().observed_addr = true; -5348 space.pending.observed_addr = false; -5349 } -5350 } -5351 } -5352 -5353 // PATH_RESPONSE -5354 if buf.remaining_mut() > frame::PathResponse::SIZE_BOUND && space_id == SpaceId::Data { -5355 if let Some(token) = path.path_responses.pop_on_path(path.network_path) { -5356 sent.non_retransmits = true; -5357 sent.requires_padding = true; -5358 let response = frame::PathResponse(token); -5359 trace!(frame = %response); -5360 buf.write(response); -5361 qlog.frame(&Frame::PathResponse(response)); -5362 self.stats.frame_tx.path_response += 1; -5363 -5364 // NOTE: this is technically not required but might be useful to ride the -5365 // request/response nature of path challenges to refresh an observation -5366 // Since PATH_RESPONSE is a probing frame, this is allowed by the spec. -5367 if space_id == SpaceId::Data -5368 && self -5369 .config -5370 .address_discovery_role -5371 .should_report(&self.peer_params.address_discovery_role) -5372 { -5373 let frame = frame::ObservedAddr::new( -5374 path.network_path.remote, -5375 self.next_observed_addr_seq_no, -5376 ); -5377 if buf.remaining_mut() > frame.size() { -5378 frame.encode(buf); -5379 qlog.frame(&Frame::ObservedAddr(frame)); -5380 -5381 self.next_observed_addr_seq_no = -5382 self.next_observed_addr_seq_no.saturating_add(1u8); -5383 path.observed_addr_sent = true; -5384 -5385 self.stats.frame_tx.observed_addr += 1; -5386 sent.retransmits.get_or_create().observed_addr = true; -5387 space.pending.observed_addr = false; -5388 } -5389 } -5390 } -5391 } -5392 -5393 // CRYPTO -5394 while !path_exclusive_only && buf.remaining_mut() > frame::Crypto::SIZE_BOUND && !is_0rtt { -5395 let mut frame = match space.pending.crypto.pop_front() { -5396 Some(x) => x, -5397 None => break, -5398 }; -5399 -5400 // Calculate the maximum amount of crypto data we can store in the buffer. -5401 // Since the offset is known, we can reserve the exact size required to encode it. -5402 // For length we reserve 2bytes which allows to encode up to 2^14, -5403 // which is more than what fits into normally sized QUIC frames. -5404 let max_crypto_data_size = buf.remaining_mut() -5405 - 1 // Frame Type -5406 - VarInt::size(unsafe { VarInt::from_u64_unchecked(frame.offset) }) -5407 - 2; // Maximum encoded length for frame size, given we send less than 2^14 bytes -5408 -5409 let len = frame -5410 .data -5411 .len() -5412 .min(2usize.pow(14) - 1) -5413 .min(max_crypto_data_size); -5414 -5415 let data = frame.data.split_to(len); -5416 let truncated = frame::Crypto { -5417 offset: frame.offset, -5418 data, -5419 }; -5420 trace!( -5421 "CRYPTO: off {} len {}", -5422 truncated.offset, -5423 truncated.data.len() -5424 ); -5425 truncated.encode(buf); -5426 self.stats.frame_tx.crypto += 1; -5427 -5428 // The clone is cheap but we still cfg it out if qlog is disabled. -5429 #[cfg(feature = "qlog")] -5430 qlog.frame(&Frame::Crypto(truncated.clone())); -5431 sent.retransmits.get_or_create().crypto.push_back(truncated); -5432 if !frame.data.is_empty() { -5433 frame.offset += len as u64; -5434 space.pending.crypto.push_front(frame); -5435 } -5436 } -5437 -5438 // TODO(flub): maybe this is much higher priority? -5439 // PATH_ABANDON -5440 while !path_exclusive_only -5441 && space_id == SpaceId::Data -5442 && frame::PathAbandon::SIZE_BOUND <= buf.remaining_mut() -5443 { -5444 let Some((path_id, error_code)) = space.pending.path_abandon.pop_first() else { -5445 break; -5446 }; -5447 let frame = frame::PathAbandon { -5448 path_id, -5449 error_code, -5450 }; -5451 frame.encode(buf); -5452 qlog.frame(&Frame::PathAbandon(frame)); -5453 self.stats.frame_tx.path_abandon += 1; -5454 trace!(%path_id, "PATH_ABANDON"); -5455 sent.retransmits -5456 .get_or_create() -5457 .path_abandon -5458 .entry(path_id) -5459 .or_insert(error_code); -5460 } +5342 // MAX_PATH_ID +5343 if space_id == SpaceId::Data +5344 && space.pending.max_path_id +5345 && frame::MaxPathId::SIZE_BOUND <= builder.frame_space_remaining() +5346 { +5347 let frame = frame::MaxPathId(self.local_max_path_id); +5348 builder.write_frame(frame, stats); +5349 space.pending.max_path_id = false; +5350 } +5351 +5352 // PATHS_BLOCKED +5353 if space_id == SpaceId::Data +5354 && space.pending.paths_blocked +5355 && frame::PathsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5356 { +5357 let frame = frame::PathsBlocked(self.remote_max_path_id); +5358 builder.write_frame(frame, stats); +5359 space.pending.paths_blocked = false; +5360 } +5361 +5362 // PATH_CIDS_BLOCKED +5363 while space_id == SpaceId::Data +5364 && frame::PathCidsBlocked::SIZE_BOUND <= builder.frame_space_remaining() +5365 { +5366 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { +5367 break; +5368 }; +5369 let next_seq = match self.rem_cids.get(&path_id) { +5370 Some(cid_queue) => VarInt(cid_queue.active_seq() + 1), +5371 None => VarInt(0), +5372 }; +5373 let frame = frame::PathCidsBlocked { path_id, next_seq }; +5374 builder.write_frame(frame, stats); +5375 } +5376 +5377 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS +5378 if space_id == SpaceId::Data { +5379 self.streams +5380 .write_control_frames(builder, &mut space.pending, stats); +5381 } +5382 +5383 // NEW_CONNECTION_ID +5384 let cid_len = self +5385 .local_cid_state +5386 .values() +5387 .map(|cid_state| cid_state.cid_len()) +5388 .max() +5389 .expect("some local CID state must exist"); +5390 let new_cid_size_bound = +5391 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); +5392 while !path_exclusive_only && builder.frame_space_remaining() > new_cid_size_bound { +5393 let issued = match space.pending.new_cids.pop() { +5394 Some(x) => x, +5395 None => break, +5396 }; +5397 let retire_prior_to = self +5398 .local_cid_state +5399 .get(&issued.path_id) +5400 .map(|cid_state| cid_state.retire_prior_to()) +5401 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); +5402 +5403 let cid_path_id = match is_multipath_negotiated { +5404 true => Some(issued.path_id), +5405 false => { +5406 debug_assert_eq!(issued.path_id, PathId::ZERO); +5407 None +5408 } +5409 }; +5410 let frame = frame::NewConnectionId { +5411 path_id: cid_path_id, +5412 sequence: issued.sequence, +5413 retire_prior_to, +5414 id: issued.id, +5415 reset_token: issued.reset_token, +5416 }; +5417 builder.write_frame(frame, stats); +5418 } +5419 +5420 // RETIRE_CONNECTION_ID +5421 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); +5422 while !path_exclusive_only && builder.frame_space_remaining() > retire_cid_bound { +5423 let (path_id, sequence) = match space.pending.retire_cids.pop() { +5424 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => (None, seq), +5425 Some((path_id, seq)) => (Some(path_id), seq), +5426 None => break, +5427 }; +5428 let frame = frame::RetireConnectionId { path_id, sequence }; +5429 builder.write_frame(frame, stats); +5430 } +5431 +5432 // DATAGRAM +5433 let mut sent_datagrams = false; +5434 while !path_exclusive_only +5435 && builder.frame_space_remaining() > Datagram::SIZE_BOUND +5436 && space_id == SpaceId::Data +5437 { +5438 match self.datagrams.write(builder, stats) { +5439 true => { +5440 sent_datagrams = true; +5441 } +5442 false => break, +5443 } +5444 } +5445 if self.datagrams.send_blocked && sent_datagrams { +5446 self.events.push_back(Event::DatagramsUnblocked); +5447 self.datagrams.send_blocked = false; +5448 } +5449 +5450 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; +5451 +5452 // NEW_TOKEN +5453 while let Some(network_path) = space.pending.new_tokens.pop() { +5454 if path_exclusive_only { +5455 break; +5456 } +5457 debug_assert_eq!(space_id, SpaceId::Data); +5458 let ConnectionSide::Server { server_config } = &self.side else { +5459 panic!("NEW_TOKEN frames should not be enqueued by clients"); +5460 }; 5461 -5462 // PATH_STATUS_AVAILABLE & PATH_STATUS_BACKUP -5463 while !path_exclusive_only -5464 && space_id == SpaceId::Data -5465 && frame::PathStatusAvailable::SIZE_BOUND <= buf.remaining_mut() -5466 { -5467 let Some(path_id) = space.pending.path_status.pop_first() else { -5468 break; -5469 }; -5470 let Some(path) = self.paths.get(&path_id).map(|path_state| &path_state.data) else { -5471 trace!(%path_id, "discarding queued path status for unknown path"); -5472 continue; -5473 }; -5474 -5475 let seq = path.status.seq(); -5476 sent.retransmits.get_or_create().path_status.insert(path_id); -5477 match path.local_status() { -5478 PathStatus::Available => { -5479 let frame = frame::PathStatusAvailable { -5480 path_id, -5481 status_seq_no: seq, -5482 }; -5483 frame.encode(buf); -5484 qlog.frame(&Frame::PathStatusAvailable(frame)); -5485 self.stats.frame_tx.path_status_available += 1; -5486 trace!(%path_id, %seq, "PATH_STATUS_AVAILABLE") -5487 } -5488 PathStatus::Backup => { -5489 let frame = frame::PathStatusBackup { -5490 path_id, -5491 status_seq_no: seq, -5492 }; -5493 frame.encode(buf); -5494 qlog.frame(&Frame::PathStatusBackup(frame)); -5495 self.stats.frame_tx.path_status_backup += 1; -5496 trace!(%path_id, %seq, "PATH_STATUS_BACKUP") -5497 } -5498 } -5499 } -5500 -5501 // MAX_PATH_ID -5502 if space_id == SpaceId::Data -5503 && space.pending.max_path_id -5504 && frame::MaxPathId::SIZE_BOUND <= buf.remaining_mut() -5505 { -5506 let frame = frame::MaxPathId(self.local_max_path_id); -5507 frame.encode(buf); -5508 qlog.frame(&Frame::MaxPathId(frame)); -5509 space.pending.max_path_id = false; -5510 sent.retransmits.get_or_create().max_path_id = true; -5511 trace!(val = %self.local_max_path_id, "MAX_PATH_ID"); -5512 self.stats.frame_tx.max_path_id += 1; -5513 } -5514 -5515 // PATHS_BLOCKED -5516 if space_id == SpaceId::Data -5517 && space.pending.paths_blocked -5518 && frame::PathsBlocked::SIZE_BOUND <= buf.remaining_mut() -5519 { -5520 let frame = frame::PathsBlocked(self.remote_max_path_id); -5521 frame.encode(buf); -5522 qlog.frame(&Frame::PathsBlocked(frame)); -5523 space.pending.paths_blocked = false; -5524 sent.retransmits.get_or_create().paths_blocked = true; -5525 trace!(max_path_id = %self.remote_max_path_id, "PATHS_BLOCKED"); -5526 self.stats.frame_tx.paths_blocked += 1; -5527 } -5528 -5529 // PATH_CIDS_BLOCKED -5530 while space_id == SpaceId::Data && frame::PathCidsBlocked::SIZE_BOUND <= buf.remaining_mut() -5531 { -5532 let Some(path_id) = space.pending.path_cids_blocked.pop_first() else { -5533 break; -5534 }; -5535 let next_seq = match self.rem_cids.get(&path_id) { -5536 Some(cid_queue) => cid_queue.active_seq() + 1, -5537 None => 0, -5538 }; -5539 let frame = frame::PathCidsBlocked { -5540 path_id, -5541 next_seq: VarInt(next_seq), -5542 }; -5543 frame.encode(buf); -5544 qlog.frame(&Frame::PathCidsBlocked(frame)); -5545 sent.retransmits -5546 .get_or_create() -5547 .path_cids_blocked -5548 .insert(path_id); -5549 trace!(%path_id, next_seq, "PATH_CIDS_BLOCKED"); -5550 self.stats.frame_tx.path_cids_blocked += 1; -5551 } -5552 -5553 // RESET_STREAM, STOP_SENDING, MAX_DATA, MAX_STREAM_DATA, MAX_STREAMS -5554 if space_id == SpaceId::Data { -5555 self.streams.write_control_frames( -5556 buf, -5557 &mut space.pending, -5558 &mut sent.retransmits, -5559 &mut self.stats.frame_tx, -5560 qlog, -5561 ); -5562 } -5563 -5564 // NEW_CONNECTION_ID -5565 let cid_len = self -5566 .local_cid_state -5567 .values() -5568 .map(|cid_state| cid_state.cid_len()) -5569 .max() -5570 .expect("some local CID state must exist"); -5571 let new_cid_size_bound = -5572 frame::NewConnectionId::size_bound(is_multipath_negotiated, cid_len); -5573 while !path_exclusive_only && buf.remaining_mut() > new_cid_size_bound { -5574 let issued = match space.pending.new_cids.pop() { -5575 Some(x) => x, -5576 None => break, -5577 }; -5578 let retire_prior_to = self -5579 .local_cid_state -5580 .get(&issued.path_id) -5581 .map(|cid_state| cid_state.retire_prior_to()) -5582 .unwrap_or_else(|| panic!("missing local CID state for path={}", issued.path_id)); -5583 -5584 let cid_path_id = match is_multipath_negotiated { -5585 true => { -5586 trace!( -5587 path_id = ?issued.path_id, -5588 sequence = issued.sequence, -5589 id = %issued.id, -5590 "PATH_NEW_CONNECTION_ID", -5591 ); -5592 self.stats.frame_tx.path_new_connection_id += 1; -5593 Some(issued.path_id) -5594 } -5595 false => { -5596 trace!( -5597 sequence = issued.sequence, -5598 id = %issued.id, -5599 "NEW_CONNECTION_ID" -5600 ); -5601 debug_assert_eq!(issued.path_id, PathId::ZERO); -5602 self.stats.frame_tx.new_connection_id += 1; -5603 None -5604 } -5605 }; -5606 let frame = frame::NewConnectionId { -5607 path_id: cid_path_id, -5608 sequence: issued.sequence, -5609 retire_prior_to, -5610 id: issued.id, -5611 reset_token: issued.reset_token, -5612 }; -5613 frame.encode(buf); -5614 sent.retransmits.get_or_create().new_cids.push(issued); -5615 qlog.frame(&Frame::NewConnectionId(frame)); -5616 } -5617 -5618 // RETIRE_CONNECTION_ID -5619 let retire_cid_bound = frame::RetireConnectionId::size_bound(is_multipath_negotiated); -5620 while !path_exclusive_only && buf.remaining_mut() > retire_cid_bound { -5621 let (path_id, sequence) = match space.pending.retire_cids.pop() { -5622 Some((PathId::ZERO, seq)) if !is_multipath_negotiated => { -5623 trace!(sequence = seq, "RETIRE_CONNECTION_ID"); -5624 self.stats.frame_tx.retire_connection_id += 1; -5625 (None, seq) -5626 } -5627 Some((path_id, seq)) => { -5628 trace!(%path_id, sequence = seq, "PATH_RETIRE_CONNECTION_ID"); -5629 self.stats.frame_tx.path_retire_connection_id += 1; -5630 (Some(path_id), seq) -5631 } -5632 None => break, -5633 }; -5634 let frame = frame::RetireConnectionId { path_id, sequence }; -5635 frame.encode(buf); -5636 qlog.frame(&Frame::RetireConnectionId(frame)); -5637 sent.retransmits -5638 .get_or_create() -5639 .retire_cids -5640 .push((path_id.unwrap_or_default(), sequence)); -5641 } -5642 -5643 // DATAGRAM -5644 let mut sent_datagrams = false; -5645 while !path_exclusive_only -5646 && buf.remaining_mut() > Datagram::SIZE_BOUND -5647 && space_id == SpaceId::Data -5648 { -5649 let prev_remaining = buf.remaining_mut(); -5650 match self.datagrams.write(buf) { -5651 true => { -5652 sent_datagrams = true; -5653 sent.non_retransmits = true; -5654 self.stats.frame_tx.datagram += 1; -5655 qlog.frame_datagram((prev_remaining - buf.remaining_mut()) as u64); -5656 } -5657 false => break, -5658 } -5659 } -5660 if self.datagrams.send_blocked && sent_datagrams { -5661 self.events.push_back(Event::DatagramsUnblocked); -5662 self.datagrams.send_blocked = false; -5663 } -5664 -5665 let path = &mut self.paths.get_mut(&path_id).expect("known path").data; -5666 -5667 // NEW_TOKEN -5668 while let Some(network_path) = space.pending.new_tokens.pop() { -5669 if path_exclusive_only { -5670 break; -5671 } -5672 debug_assert_eq!(space_id, SpaceId::Data); -5673 let ConnectionSide::Server { server_config } = &self.side else { -5674 panic!("NEW_TOKEN frames should not be enqueued by clients"); -5675 }; -5676 -5677 if !network_path.is_probably_same_path(&path.network_path) { -5678 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only -5679 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN -5680 // frames upon an path change. Instead, when the new path becomes validated, -5681 // NEW_TOKEN frames may be enqueued for the new path instead. -5682 continue; -5683 } -5684 -5685 let token = Token::new( -5686 TokenPayload::Validation { -5687 ip: network_path.remote.ip(), -5688 issued: server_config.time_source.now(), -5689 }, -5690 &mut self.rng, -5691 ); -5692 let new_token = NewToken { -5693 token: token.encode(&*server_config.token_key).into(), -5694 }; +5462 if !network_path.is_probably_same_path(&path.network_path) { +5463 // NEW_TOKEN frames contain tokens bound to a client's IP address, and are only +5464 // useful if used from the same IP address. Thus, we abandon enqueued NEW_TOKEN +5465 // frames upon an path change. Instead, when the new path becomes validated, +5466 // NEW_TOKEN frames may be enqueued for the new path instead. +5467 continue; +5468 } +5469 +5470 let token = Token::new( +5471 TokenPayload::Validation { +5472 ip: network_path.remote.ip(), +5473 issued: server_config.time_source.now(), +5474 }, +5475 &mut self.rng, +5476 ); +5477 let new_token = NewToken { +5478 token: token.encode(&*server_config.token_key).into(), +5479 }; +5480 +5481 if builder.frame_space_remaining() < new_token.size() { +5482 space.pending.new_tokens.push(network_path); +5483 break; +5484 } +5485 +5486 builder.write_frame(new_token, stats); +5487 builder.retransmits_mut().new_tokens.push(network_path); +5488 } +5489 +5490 // STREAM +5491 if !path_exclusive_only && space_id == SpaceId::Data { +5492 self.streams +5493 .write_stream_frames(builder, self.config.send_fairness, stats); +5494 } +5495 +5496 // ADD_ADDRESS +5497 // TODO(@divma): check if we need to do path exclusive filters +5498 while space_id == SpaceId::Data +5499 && frame::AddAddress::SIZE_BOUND <= builder.frame_space_remaining() +5500 { +5501 if let Some(added_address) = space.pending.add_address.pop_last() { +5502 builder.write_frame(added_address, stats); +5503 } else { +5504 break; +5505 } +5506 } +5507 +5508 // REMOVE_ADDRESS +5509 while space_id == SpaceId::Data +5510 && frame::RemoveAddress::SIZE_BOUND <= builder.frame_space_remaining() +5511 { +5512 if let Some(removed_address) = space.pending.remove_address.pop_last() { +5513 builder.write_frame(removed_address, stats); +5514 } else { +5515 break; +5516 } +5517 } +5518 } +5519 +5520 /// Write pending ACKs into a buffer +5521 fn populate_acks<'a, 'b>( +5522 now: Instant, +5523 receiving_ecn: bool, +5524 path_id: PathId, +5525 space_id: SpaceId, +5526 space: &mut PacketSpace, +5527 is_multipath_negotiated: bool, +5528 builder: &mut PacketBuilder<'a, 'b>, +5529 stats: &mut FrameStats, +5530 ) { +5531 // 0-RTT packets must never carry acks (which would have to be of handshake packets) +5532 debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT"); +5533 +5534 debug_assert!( +5535 is_multipath_negotiated || path_id == PathId::ZERO, +5536 "Only PathId::ZERO allowed without multipath (have {path_id:?})" +5537 ); +5538 if is_multipath_negotiated { +5539 debug_assert!( +5540 space_id == SpaceId::Data || path_id == PathId::ZERO, +5541 "path acks must be sent in 1RTT space (have {space_id:?})" +5542 ); +5543 } +5544 +5545 let pns = space.for_path(path_id); +5546 let ranges = pns.pending_acks.ranges(); +5547 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); +5548 let ecn = if receiving_ecn { +5549 Some(&pns.ecn_counters) +5550 } else { +5551 None +5552 }; +5553 +5554 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; +5555 // TODO: This should come from `TransportConfig` if that gets configurable. +5556 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; +5557 let delay = delay_micros >> ack_delay_exp.into_inner(); +5558 +5559 if is_multipath_negotiated && space_id == SpaceId::Data { +5560 if !ranges.is_empty() { +5561 let frame = frame::PathAck::encoder(path_id, delay, ranges, ecn); +5562 builder.write_frame(frame, stats); +5563 } +5564 } else { +5565 builder.write_frame(frame::Ack::encoder(delay, ranges, ecn), stats); +5566 } +5567 } +5568 +5569 fn close_common(&mut self) { +5570 trace!("connection closed"); +5571 self.timers.reset(); +5572 } +5573 +5574 fn set_close_timer(&mut self, now: Instant) { +5575 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of +5576 // the PTO for all paths. +5577 let pto_max = self.pto_max_path(self.highest_space, true); +5578 self.timers.set( +5579 Timer::Conn(ConnTimer::Close), +5580 now + 3 * pto_max, +5581 self.qlog.with_time(now), +5582 ); +5583 } +5584 +5585 /// Handle transport parameters received from the peer +5586 /// +5587 /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the +5588 /// *packet into which the transport parameters arrived. +5589 fn handle_peer_params( +5590 &mut self, +5591 params: TransportParameters, +5592 loc_cid: ConnectionId, +5593 rem_cid: ConnectionId, +5594 now: Instant, +5595 ) -> Result<(), TransportError> { +5596 if Some(self.orig_rem_cid) != params.initial_src_cid +5597 || (self.side.is_client() +5598 && (Some(self.initial_dst_cid) != params.original_dst_cid +5599 || self.retry_src_cid != params.retry_src_cid)) +5600 { +5601 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( +5602 "CID authentication failure", +5603 )); +5604 } +5605 if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) { +5606 return Err(TransportError::PROTOCOL_VIOLATION( +5607 "multipath must not use zero-length CIDs", +5608 )); +5609 } +5610 +5611 self.set_peer_params(params); +5612 self.qlog.emit_peer_transport_params_received(self, now); +5613 +5614 Ok(()) +5615 } +5616 +5617 fn set_peer_params(&mut self, params: TransportParameters) { +5618 self.streams.set_params(&params); +5619 self.idle_timeout = +5620 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); +5621 trace!("negotiated max idle timeout {:?}", self.idle_timeout); +5622 +5623 if let Some(ref info) = params.preferred_address { +5624 // During the handshake PathId::ZERO exists. +5625 self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { +5626 path_id: None, +5627 sequence: 1, +5628 id: info.connection_id, +5629 reset_token: info.stateless_reset_token, +5630 retire_prior_to: 0, +5631 }) +5632 .expect( +5633 "preferred address CID is the first received, and hence is guaranteed to be legal", +5634 ); +5635 let remote = self.path_data(PathId::ZERO).network_path.remote; +5636 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); +5637 } +5638 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); +5639 +5640 let mut multipath_enabled = None; +5641 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( +5642 self.config.get_initial_max_path_id(), +5643 params.initial_max_path_id, +5644 ) { +5645 // multipath is enabled, register the local and remote maximums +5646 self.local_max_path_id = local_max_path_id; +5647 self.remote_max_path_id = remote_max_path_id; +5648 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); +5649 debug!(%initial_max_path_id, "multipath negotiated"); +5650 multipath_enabled = Some(initial_max_path_id); +5651 } +5652 +5653 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = +5654 self.config +5655 .max_remote_nat_traversal_addresses +5656 .zip(params.max_remote_nat_traversal_addresses) +5657 { +5658 if let Some(max_initial_paths) = +5659 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) +5660 { +5661 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); +5662 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); +5663 self.iroh_hp = +5664 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); +5665 debug!( +5666 %max_remote_addresses, %max_local_addresses, +5667 "iroh hole punching negotiated" +5668 ); +5669 +5670 match self.side() { +5671 Side::Client => { +5672 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { +5673 // in this case the client might try to open `max_remote_addresses` new +5674 // paths, but the current multipath configuration will not allow it +5675 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") +5676 } else if max_local_addresses as u64 +5677 > params.active_connection_id_limit.into_inner() +5678 { +5679 // the server allows us to send at most `params.active_connection_id_limit` +5680 // but they might need at least `max_local_addresses` to effectively send +5681 // `PATH_CHALLENGE` frames to each advertised local address +5682 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") +5683 } +5684 } +5685 Side::Server => { +5686 if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT { +5687 warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues") +5688 } +5689 } +5690 } +5691 } else { +5692 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") +5693 } +5694 } 5695 -5696 if buf.remaining_mut() < new_token.size() { -5697 space.pending.new_tokens.push(network_path); -5698 break; -5699 } -5700 -5701 trace!("NEW_TOKEN"); -5702 new_token.encode(buf); -5703 qlog.frame(&Frame::NewToken(new_token)); -5704 sent.retransmits -5705 .get_or_create() -5706 .new_tokens -5707 .push(network_path); -5708 self.stats.frame_tx.new_token += 1; -5709 } -5710 -5711 // STREAM -5712 if !path_exclusive_only && space_id == SpaceId::Data { -5713 sent.stream_frames = -5714 self.streams -5715 .write_stream_frames(buf, self.config.send_fairness, qlog); -5716 self.stats.frame_tx.stream += sent.stream_frames.len() as u64; -5717 } -5718 -5719 // ADD_ADDRESS -5720 // TODO(@divma): check if we need to do path exclusive filters -5721 while space_id == SpaceId::Data && frame::AddAddress::SIZE_BOUND <= buf.remaining_mut() { -5722 if let Some(added_address) = space.pending.add_address.pop_last() { -5723 trace!( -5724 seq = %added_address.seq_no, -5725 ip = ?added_address.ip, -5726 port = added_address.port, -5727 "ADD_ADDRESS", -5728 ); -5729 added_address.encode(buf); -5730 sent.retransmits -5731 .get_or_create() -5732 .add_address -5733 .insert(added_address); -5734 self.stats.frame_tx.add_address = self.stats.frame_tx.add_address.saturating_add(1); -5735 qlog.frame(&Frame::AddAddress(added_address)); -5736 } else { -5737 break; -5738 } -5739 } -5740 -5741 // REMOVE_ADDRESS -5742 while space_id == SpaceId::Data && frame::RemoveAddress::SIZE_BOUND <= buf.remaining_mut() { -5743 if let Some(removed_address) = space.pending.remove_address.pop_last() { -5744 trace!(seq = %removed_address.seq_no, "REMOVE_ADDRESS"); -5745 removed_address.encode(buf); -5746 sent.retransmits -5747 .get_or_create() -5748 .remove_address -5749 .insert(removed_address); -5750 self.stats.frame_tx.remove_address = -5751 self.stats.frame_tx.remove_address.saturating_add(1); -5752 qlog.frame(&Frame::RemoveAddress(removed_address)); -5753 } else { -5754 break; -5755 } -5756 } -5757 -5758 sent -5759 } -5760 -5761 /// Write pending ACKs into a buffer -5762 fn populate_acks( -5763 now: Instant, -5764 receiving_ecn: bool, -5765 sent: &mut SentFrames, -5766 path_id: PathId, -5767 space_id: SpaceId, -5768 space: &mut PacketSpace, -5769 is_multipath_negotiated: bool, -5770 buf: &mut impl BufMut, -5771 stats: &mut ConnectionStats, -5772 #[allow(unused)] qlog: &mut QlogSentPacket, -5773 ) { -5774 // 0-RTT packets must never carry acks (which would have to be of handshake packets) -5775 debug_assert!(space.crypto.is_some(), "tried to send ACK in 0-RTT"); -5776 -5777 debug_assert!( -5778 is_multipath_negotiated || path_id == PathId::ZERO, -5779 "Only PathId::ZERO allowed without multipath (have {path_id:?})" -5780 ); -5781 if is_multipath_negotiated { -5782 debug_assert!( -5783 space_id == SpaceId::Data || path_id == PathId::ZERO, -5784 "path acks must be sent in 1RTT space (have {space_id:?})" -5785 ); -5786 } -5787 -5788 let pns = space.for_path(path_id); -5789 let ranges = pns.pending_acks.ranges(); -5790 debug_assert!(!ranges.is_empty(), "can not send empty ACK range"); -5791 let ecn = if receiving_ecn { -5792 Some(&pns.ecn_counters) -5793 } else { -5794 None -5795 }; -5796 if let Some(max) = ranges.max() { -5797 sent.largest_acked.insert(path_id, max); -5798 } -5799 -5800 let delay_micros = pns.pending_acks.ack_delay(now).as_micros() as u64; -5801 // TODO: This should come from `TransportConfig` if that gets configurable. -5802 let ack_delay_exp = TransportParameters::default().ack_delay_exponent; -5803 let delay = delay_micros >> ack_delay_exp.into_inner(); -5804 -5805 if is_multipath_negotiated && space_id == SpaceId::Data { -5806 if !ranges.is_empty() { -5807 trace!("PATH_ACK {path_id:?} {ranges:?}, Delay = {delay_micros}us"); -5808 frame::PathAck::encoder(path_id, delay as _, ranges, ecn).encode(buf); -5809 qlog.frame_path_ack(path_id, delay as _, ranges, ecn); -5810 stats.frame_tx.path_acks += 1; -5811 } -5812 } else { -5813 trace!("ACK {ranges:?}, Delay = {delay_micros}us"); -5814 frame::Ack::encoder(delay as _, ranges, ecn).encode(buf); -5815 stats.frame_tx.acks += 1; -5816 qlog.frame_ack(delay, ranges, ecn); -5817 } -5818 } -5819 -5820 fn close_common(&mut self) { -5821 trace!("connection closed"); -5822 self.timers.reset(); -5823 } -5824 -5825 fn set_close_timer(&mut self, now: Instant) { -5826 // QUIC-MULTIPATH § 2.6 Connection Closure: draining for 3*PTO with PTO the max of -5827 // the PTO for all paths. -5828 let pto_max = self.pto_max_path(self.highest_space, true); -5829 self.timers.set( -5830 Timer::Conn(ConnTimer::Close), -5831 now + 3 * pto_max, -5832 self.qlog.with_time(now), -5833 ); -5834 } -5835 -5836 /// Handle transport parameters received from the peer -5837 /// -5838 /// *rem_cid* and *loc_cid* are the source and destination CIDs respectively of the -5839 /// *packet into which the transport parameters arrived. -5840 fn handle_peer_params( -5841 &mut self, -5842 params: TransportParameters, -5843 loc_cid: ConnectionId, -5844 rem_cid: ConnectionId, -5845 now: Instant, -5846 ) -> Result<(), TransportError> { -5847 if Some(self.orig_rem_cid) != params.initial_src_cid -5848 || (self.side.is_client() -5849 && (Some(self.initial_dst_cid) != params.original_dst_cid -5850 || self.retry_src_cid != params.retry_src_cid)) -5851 { -5852 return Err(TransportError::TRANSPORT_PARAMETER_ERROR( -5853 "CID authentication failure", -5854 )); -5855 } -5856 if params.initial_max_path_id.is_some() && (loc_cid.is_empty() || rem_cid.is_empty()) { -5857 return Err(TransportError::PROTOCOL_VIOLATION( -5858 "multipath must not use zero-length CIDs", -5859 )); -5860 } -5861 -5862 self.set_peer_params(params); -5863 self.qlog.emit_peer_transport_params_received(self, now); -5864 -5865 Ok(()) +5696 self.peer_params = params; +5697 let peer_max_udp_payload_size = +5698 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); +5699 self.path_data_mut(PathId::ZERO) +5700 .mtud +5701 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); +5702 } +5703 +5704 /// Decrypts a packet, returning the packet number on success +5705 fn decrypt_packet( +5706 &mut self, +5707 now: Instant, +5708 path_id: PathId, +5709 packet: &mut Packet, +5710 ) -> Result<Option<u64>, Option<TransportError>> { +5711 let result = packet_crypto::decrypt_packet_body( +5712 packet, +5713 path_id, +5714 &self.spaces, +5715 self.zero_rtt_crypto.as_ref(), +5716 self.key_phase, +5717 self.prev_crypto.as_ref(), +5718 self.next_crypto.as_ref(), +5719 )?; +5720 +5721 let result = match result { +5722 Some(r) => r, +5723 None => return Ok(None), +5724 }; +5725 +5726 if result.outgoing_key_update_acked { +5727 if let Some(prev) = self.prev_crypto.as_mut() { +5728 prev.end_packet = Some((result.number, now)); +5729 self.set_key_discard_timer(now, packet.header.space()); +5730 } +5731 } +5732 +5733 if result.incoming_key_update { +5734 trace!("key update authenticated"); +5735 self.update_keys(Some((result.number, now)), true); +5736 self.set_key_discard_timer(now, packet.header.space()); +5737 } +5738 +5739 Ok(Some(result.number)) +5740 } +5741 +5742 fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) { +5743 trace!("executing key update"); +5744 // Generate keys for the key phase after the one we're switching to, store them in +5745 // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into +5746 // `prev_crypto`. +5747 let new = self +5748 .crypto +5749 .next_1rtt_keys() +5750 .expect("only called for `Data` packets"); +5751 self.key_phase_size = new +5752 .local +5753 .confidentiality_limit() +5754 .saturating_sub(KEY_UPDATE_MARGIN); +5755 let old = mem::replace( +5756 &mut self.spaces[SpaceId::Data] +5757 .crypto +5758 .as_mut() +5759 .unwrap() // safe because update_keys() can only be triggered by short packets +5760 .packet, +5761 mem::replace(self.next_crypto.as_mut().unwrap(), new), +5762 ); +5763 self.spaces[SpaceId::Data] +5764 .iter_paths_mut() +5765 .for_each(|s| s.sent_with_keys = 0); +5766 self.prev_crypto = Some(PrevCrypto { +5767 crypto: old, +5768 end_packet, +5769 update_unacked: remote, +5770 }); +5771 self.key_phase = !self.key_phase; +5772 } +5773 +5774 fn peer_supports_ack_frequency(&self) -> bool { +5775 self.peer_params.min_ack_delay.is_some() +5776 } +5777 +5778 /// Send an IMMEDIATE_ACK frame to the remote endpoint +5779 /// +5780 /// According to the spec, this will result in an error if the remote endpoint does not support +5781 /// the Acknowledgement Frequency extension +5782 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { +5783 debug_assert_eq!( +5784 self.highest_space, +5785 SpaceId::Data, +5786 "immediate ack must be written in the data space" +5787 ); +5788 self.spaces[self.highest_space] +5789 .for_path(path_id) +5790 .immediate_ack_pending = true; +5791 } +5792 +5793 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests +5794 #[cfg(test)] +5795 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { +5796 let (path_id, first_decode, remaining) = match &event.0 { +5797 ConnectionEventInner::Datagram(DatagramConnectionEvent { +5798 path_id, +5799 first_decode, +5800 remaining, +5801 .. +5802 }) => (path_id, first_decode, remaining), +5803 _ => return None, +5804 }; +5805 +5806 if remaining.is_some() { +5807 panic!("Packets should never be coalesced in tests"); +5808 } +5809 +5810 let decrypted_header = packet_crypto::unprotect_header( +5811 first_decode.clone(), +5812 &self.spaces, +5813 self.zero_rtt_crypto.as_ref(), +5814 self.peer_params.stateless_reset_token, +5815 )?; +5816 +5817 let mut packet = decrypted_header.packet?; +5818 packet_crypto::decrypt_packet_body( +5819 &mut packet, +5820 *path_id, +5821 &self.spaces, +5822 self.zero_rtt_crypto.as_ref(), +5823 self.key_phase, +5824 self.prev_crypto.as_ref(), +5825 self.next_crypto.as_ref(), +5826 ) +5827 .ok()?; +5828 +5829 Some(packet.payload.to_vec()) +5830 } +5831 +5832 /// The number of bytes of packets containing retransmittable frames that have not been +5833 /// acknowledged or declared lost. +5834 #[cfg(test)] +5835 pub(crate) fn bytes_in_flight(&self) -> u64 { +5836 // TODO(@divma): consider including for multipath? +5837 self.path_data(PathId::ZERO).in_flight.bytes +5838 } +5839 +5840 /// Number of bytes worth of non-ack-only packets that may be sent +5841 #[cfg(test)] +5842 pub(crate) fn congestion_window(&self) -> u64 { +5843 let path = self.path_data(PathId::ZERO); +5844 path.congestion +5845 .window() +5846 .saturating_sub(path.in_flight.bytes) +5847 } +5848 +5849 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running +5850 #[cfg(test)] +5851 pub(crate) fn is_idle(&self) -> bool { +5852 let current_timers = self.timers.values(); +5853 current_timers +5854 .into_iter() +5855 .filter(|(timer, _)| { +5856 !matches!( +5857 timer, +5858 Timer::Conn(ConnTimer::KeepAlive) +5859 | Timer::PerPath(_, PathTimer::PathKeepAlive) +5860 | Timer::Conn(ConnTimer::PushNewCid) +5861 | Timer::Conn(ConnTimer::KeyDiscard) +5862 ) +5863 }) +5864 .min_by_key(|(_, time)| *time) +5865 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) 5866 } 5867 -5868 fn set_peer_params(&mut self, params: TransportParameters) { -5869 self.streams.set_params(&params); -5870 self.idle_timeout = -5871 negotiate_max_idle_timeout(self.config.max_idle_timeout, Some(params.max_idle_timeout)); -5872 trace!("negotiated max idle timeout {:?}", self.idle_timeout); +5868 /// Whether explicit congestion notification is in use on outgoing packets. +5869 #[cfg(test)] +5870 pub(crate) fn using_ecn(&self) -> bool { +5871 self.path_data(PathId::ZERO).sending_ecn +5872 } 5873 -5874 if let Some(ref info) = params.preferred_address { -5875 // During the handshake PathId::ZERO exists. -5876 self.rem_cids.get_mut(&PathId::ZERO).expect("not yet abandoned").insert(frame::NewConnectionId { -5877 path_id: None, -5878 sequence: 1, -5879 id: info.connection_id, -5880 reset_token: info.stateless_reset_token, -5881 retire_prior_to: 0, -5882 }) -5883 .expect( -5884 "preferred address CID is the first received, and hence is guaranteed to be legal", -5885 ); -5886 let remote = self.path_data(PathId::ZERO).network_path.remote; -5887 self.set_reset_token(PathId::ZERO, remote, info.stateless_reset_token); -5888 } -5889 self.ack_frequency.peer_max_ack_delay = get_max_ack_delay(&params); -5890 -5891 let mut multipath_enabled = None; -5892 if let (Some(local_max_path_id), Some(remote_max_path_id)) = ( -5893 self.config.get_initial_max_path_id(), -5894 params.initial_max_path_id, -5895 ) { -5896 // multipath is enabled, register the local and remote maximums -5897 self.local_max_path_id = local_max_path_id; -5898 self.remote_max_path_id = remote_max_path_id; -5899 let initial_max_path_id = local_max_path_id.min(remote_max_path_id); -5900 debug!(%initial_max_path_id, "multipath negotiated"); -5901 multipath_enabled = Some(initial_max_path_id); -5902 } -5903 -5904 if let Some((max_locally_allowed_remote_addresses, max_remotely_allowed_remote_addresses)) = -5905 self.config -5906 .max_remote_nat_traversal_addresses -5907 .zip(params.max_remote_nat_traversal_addresses) -5908 { -5909 if let Some(max_initial_paths) = -5910 multipath_enabled.map(|path_id| path_id.saturating_add(1u8)) -5911 { -5912 let max_local_addresses = max_remotely_allowed_remote_addresses.get(); -5913 let max_remote_addresses = max_locally_allowed_remote_addresses.get(); -5914 self.iroh_hp = -5915 iroh_hp::State::new(max_remote_addresses, max_local_addresses, self.side()); -5916 debug!( -5917 %max_remote_addresses, %max_local_addresses, -5918 "iroh hole punching negotiated" -5919 ); -5920 -5921 match self.side() { -5922 Side::Client => { -5923 if max_initial_paths.as_u32() < max_remote_addresses as u32 + 1 { -5924 // in this case the client might try to open `max_remote_addresses` new -5925 // paths, but the current multipath configuration will not allow it -5926 warn!(%max_initial_paths, %max_remote_addresses, "local client configuration might cause nat traversal issues") -5927 } else if max_local_addresses as u64 -5928 > params.active_connection_id_limit.into_inner() -5929 { -5930 // the server allows us to send at most `params.active_connection_id_limit` -5931 // but they might need at least `max_local_addresses` to effectively send -5932 // `PATH_CHALLENGE` frames to each advertised local address -5933 warn!(%max_local_addresses, remote_cid_limit=%params.active_connection_id_limit.into_inner(), "remote server configuration might cause nat traversal issues") -5934 } -5935 } -5936 Side::Server => { -5937 if (max_initial_paths.as_u32() as u64) < crate::LOC_CID_COUNT { -5938 warn!(%max_initial_paths, local_cid_limit=%crate::LOC_CID_COUNT, "local server configuration might cause nat traversal issues") -5939 } -5940 } -5941 } -5942 } else { -5943 debug!("iroh nat traversal enabled for both endpoints, but multipath is missing") -5944 } -5945 } -5946 -5947 self.peer_params = params; -5948 let peer_max_udp_payload_size = -5949 u16::try_from(self.peer_params.max_udp_payload_size.into_inner()).unwrap_or(u16::MAX); -5950 self.path_data_mut(PathId::ZERO) -5951 .mtud -5952 .on_peer_max_udp_payload_size_received(peer_max_udp_payload_size); -5953 } -5954 -5955 /// Decrypts a packet, returning the packet number on success -5956 fn decrypt_packet( -5957 &mut self, -5958 now: Instant, -5959 path_id: PathId, -5960 packet: &mut Packet, -5961 ) -> Result<Option<u64>, Option<TransportError>> { -5962 let result = packet_crypto::decrypt_packet_body( -5963 packet, -5964 path_id, -5965 &self.spaces, -5966 self.zero_rtt_crypto.as_ref(), -5967 self.key_phase, -5968 self.prev_crypto.as_ref(), -5969 self.next_crypto.as_ref(), -5970 )?; -5971 -5972 let result = match result { -5973 Some(r) => r, -5974 None => return Ok(None), -5975 }; -5976 -5977 if result.outgoing_key_update_acked { -5978 if let Some(prev) = self.prev_crypto.as_mut() { -5979 prev.end_packet = Some((result.number, now)); -5980 self.set_key_discard_timer(now, packet.header.space()); -5981 } -5982 } -5983 -5984 if result.incoming_key_update { -5985 trace!("key update authenticated"); -5986 self.update_keys(Some((result.number, now)), true); -5987 self.set_key_discard_timer(now, packet.header.space()); -5988 } -5989 -5990 Ok(Some(result.number)) -5991 } -5992 -5993 fn update_keys(&mut self, end_packet: Option<(u64, Instant)>, remote: bool) { -5994 trace!("executing key update"); -5995 // Generate keys for the key phase after the one we're switching to, store them in -5996 // `next_crypto`, make the contents of `next_crypto` current, and move the current keys into -5997 // `prev_crypto`. -5998 let new = self -5999 .crypto -6000 .next_1rtt_keys() -6001 .expect("only called for `Data` packets"); -6002 self.key_phase_size = new -6003 .local -6004 .confidentiality_limit() -6005 .saturating_sub(KEY_UPDATE_MARGIN); -6006 let old = mem::replace( -6007 &mut self.spaces[SpaceId::Data] -6008 .crypto -6009 .as_mut() -6010 .unwrap() // safe because update_keys() can only be triggered by short packets -6011 .packet, -6012 mem::replace(self.next_crypto.as_mut().unwrap(), new), -6013 ); -6014 self.spaces[SpaceId::Data] -6015 .iter_paths_mut() -6016 .for_each(|s| s.sent_with_keys = 0); -6017 self.prev_crypto = Some(PrevCrypto { -6018 crypto: old, -6019 end_packet, -6020 update_unacked: remote, -6021 }); -6022 self.key_phase = !self.key_phase; +5874 /// The number of received bytes in the current path +5875 #[cfg(test)] +5876 pub(crate) fn total_recvd(&self) -> u64 { +5877 self.path_data(PathId::ZERO).total_recvd +5878 } +5879 +5880 #[cfg(test)] +5881 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { +5882 self.local_cid_state +5883 .get(&PathId::ZERO) +5884 .unwrap() +5885 .active_seq() +5886 } +5887 +5888 #[cfg(test)] +5889 #[track_caller] +5890 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { +5891 self.local_cid_state +5892 .get(&PathId(path_id)) +5893 .unwrap() +5894 .active_seq() +5895 } +5896 +5897 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame +5898 /// with updated `retire_prior_to` field set to `v` +5899 #[cfg(test)] +5900 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { +5901 let n = self +5902 .local_cid_state +5903 .get_mut(&PathId::ZERO) +5904 .unwrap() +5905 .assign_retire_seq(v); +5906 self.endpoint_events +5907 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); +5908 } +5909 +5910 /// Check the current active remote CID sequence for `PathId::ZERO` +5911 #[cfg(test)] +5912 pub(crate) fn active_rem_cid_seq(&self) -> u64 { +5913 self.rem_cids.get(&PathId::ZERO).unwrap().active_seq() +5914 } +5915 +5916 /// Returns the detected maximum udp payload size for the current path +5917 #[cfg(test)] +5918 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { +5919 self.path_data(path_id).current_mtu() +5920 } +5921 +5922 /// Triggers path validation on all paths +5923 #[cfg(test)] +5924 pub(crate) fn trigger_path_validation(&mut self) { +5925 for path in self.paths.values_mut() { +5926 path.data.send_new_challenge = true; +5927 } +5928 } +5929 +5930 /// Whether we have 1-RTT data to send +5931 /// +5932 /// This checks for frames that can only be sent in the data space (1-RTT): +5933 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. +5934 /// - Pending PATH_RESPONSE frames. +5935 /// - Pending data to send in STREAM frames. +5936 /// - Pending DATAGRAM frames to send. +5937 /// +5938 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that +5939 /// may need to be sent. +5940 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { +5941 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { +5942 path.data.send_new_challenge +5943 || path +5944 .prev +5945 .as_ref() +5946 .is_some_and(|(_, path)| path.send_new_challenge) +5947 || !path.data.path_responses.is_empty() +5948 }); +5949 let other = self.streams.can_send_stream_data() +5950 || self +5951 .datagrams +5952 .outgoing +5953 .front() +5954 .is_some_and(|x| x.size(true) <= max_size); +5955 SendableFrames { +5956 acks: false, +5957 other, +5958 close: false, +5959 path_exclusive, +5960 } +5961 } +5962 +5963 /// Terminate the connection instantly, without sending a close packet +5964 fn kill(&mut self, reason: ConnectionError) { +5965 self.close_common(); +5966 self.state.move_to_drained(Some(reason)); +5967 self.endpoint_events.push_back(EndpointEventInner::Drained); +5968 } +5969 +5970 /// Storage size required for the largest packet that can be transmitted on all currently +5971 /// available paths +5972 /// +5973 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. +5974 /// +5975 /// When multipath is enabled, this value is the minimum MTU across all available paths. +5976 pub fn current_mtu(&self) -> u16 { +5977 self.paths +5978 .iter() +5979 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) +5980 .map(|(_path_id, path_state)| path_state.data.current_mtu()) +5981 .min() +5982 .expect("There is always at least one available path") +5983 } +5984 +5985 /// Size of non-frame data for a 1-RTT packet +5986 /// +5987 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are +5988 /// frames. Changes if the length of the remote connection ID changes, which is expected to be +5989 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in +5990 /// latency and packet loss. +5991 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { +5992 let pn_len = PacketNumber::new( +5993 pn, +5994 self.spaces[SpaceId::Data] +5995 .for_path(path) +5996 .largest_acked_packet +5997 .unwrap_or(0), +5998 ) +5999 .len(); +6000 +6001 // 1 byte for flags +6002 1 + self +6003 .rem_cids +6004 .get(&path) +6005 .map(|cids| cids.active().len()) +6006 .unwrap_or(20) // Max CID len in QUIC v1 +6007 + pn_len +6008 + self.tag_len_1rtt() +6009 } +6010 +6011 fn predict_1rtt_overhead_no_pn(&self) -> usize { +6012 let pn_len = 4; +6013 +6014 let cid_len = self +6015 .rem_cids +6016 .values() +6017 .map(|cids| cids.active().len()) +6018 .max() +6019 .unwrap_or(20); // Max CID len in QUIC v1 +6020 +6021 // 1 byte for flags +6022 1 + cid_len + pn_len + self.tag_len_1rtt() 6023 } 6024 -6025 fn peer_supports_ack_frequency(&self) -> bool { -6026 self.peer_params.min_ack_delay.is_some() -6027 } -6028 -6029 /// Send an IMMEDIATE_ACK frame to the remote endpoint -6030 /// -6031 /// According to the spec, this will result in an error if the remote endpoint does not support -6032 /// the Acknowledgement Frequency extension -6033 pub(crate) fn immediate_ack(&mut self, path_id: PathId) { -6034 debug_assert_eq!( -6035 self.highest_space, -6036 SpaceId::Data, -6037 "immediate ack must be written in the data space" -6038 ); -6039 self.spaces[self.highest_space] -6040 .for_path(path_id) -6041 .immediate_ack_pending = true; -6042 } -6043 -6044 /// Decodes a packet, returning its decrypted payload, so it can be inspected in tests -6045 #[cfg(test)] -6046 pub(crate) fn decode_packet(&self, event: &ConnectionEvent) -> Option<Vec<u8>> { -6047 let (path_id, first_decode, remaining) = match &event.0 { -6048 ConnectionEventInner::Datagram(DatagramConnectionEvent { -6049 path_id, -6050 first_decode, -6051 remaining, -6052 .. -6053 }) => (path_id, first_decode, remaining), -6054 _ => return None, -6055 }; -6056 -6057 if remaining.is_some() { -6058 panic!("Packets should never be coalesced in tests"); -6059 } -6060 -6061 let decrypted_header = packet_crypto::unprotect_header( -6062 first_decode.clone(), -6063 &self.spaces, -6064 self.zero_rtt_crypto.as_ref(), -6065 self.peer_params.stateless_reset_token, -6066 )?; -6067 -6068 let mut packet = decrypted_header.packet?; -6069 packet_crypto::decrypt_packet_body( -6070 &mut packet, -6071 *path_id, -6072 &self.spaces, -6073 self.zero_rtt_crypto.as_ref(), -6074 self.key_phase, -6075 self.prev_crypto.as_ref(), -6076 self.next_crypto.as_ref(), -6077 ) -6078 .ok()?; -6079 -6080 Some(packet.payload.to_vec()) -6081 } -6082 -6083 /// The number of bytes of packets containing retransmittable frames that have not been -6084 /// acknowledged or declared lost. -6085 #[cfg(test)] -6086 pub(crate) fn bytes_in_flight(&self) -> u64 { -6087 // TODO(@divma): consider including for multipath? -6088 self.path_data(PathId::ZERO).in_flight.bytes -6089 } -6090 -6091 /// Number of bytes worth of non-ack-only packets that may be sent -6092 #[cfg(test)] -6093 pub(crate) fn congestion_window(&self) -> u64 { -6094 let path = self.path_data(PathId::ZERO); -6095 path.congestion -6096 .window() -6097 .saturating_sub(path.in_flight.bytes) -6098 } -6099 -6100 /// Whether no timers but keepalive, idle, rtt, pushnewcid, and key discard are running -6101 #[cfg(test)] -6102 pub(crate) fn is_idle(&self) -> bool { -6103 let current_timers = self.timers.values(); -6104 current_timers -6105 .into_iter() -6106 .filter(|(timer, _)| { -6107 !matches!( -6108 timer, -6109 Timer::Conn(ConnTimer::KeepAlive) -6110 | Timer::PerPath(_, PathTimer::PathKeepAlive) -6111 | Timer::Conn(ConnTimer::PushNewCid) -6112 | Timer::Conn(ConnTimer::KeyDiscard) -6113 ) -6114 }) -6115 .min_by_key(|(_, time)| *time) -6116 .is_none_or(|(timer, _)| timer == Timer::Conn(ConnTimer::Idle)) +6025 fn tag_len_1rtt(&self) -> usize { +6026 let key = match self.spaces[SpaceId::Data].crypto.as_ref() { +6027 Some(crypto) => Some(&*crypto.packet.local), +6028 None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet), +6029 }; +6030 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of +6031 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, +6032 // but that would needlessly prevent sending datagrams during 0-RTT. +6033 key.map_or(16, |x| x.tag_len()) +6034 } +6035 +6036 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate +6037 fn on_path_validated(&mut self, path_id: PathId) { +6038 self.path_data_mut(path_id).validated = true; +6039 let ConnectionSide::Server { server_config } = &self.side else { +6040 return; +6041 }; +6042 let network_path = self.path_data(path_id).network_path; +6043 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; +6044 new_tokens.clear(); +6045 for _ in 0..server_config.validation_token.sent { +6046 new_tokens.push(network_path); +6047 } +6048 } +6049 +6050 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP +6051 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { +6052 if let Some(path) = self.paths.get_mut(&path_id) { +6053 path.data.status.remote_update(status, status_seq_no); +6054 } else { +6055 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); +6056 } +6057 self.events.push_back( +6058 PathEvent::RemoteStatus { +6059 id: path_id, +6060 status, +6061 } +6062 .into(), +6063 ); +6064 } +6065 +6066 /// Returns the maximum [`PathId`] to be used for sending in this connection. +6067 /// +6068 /// This is calculated as minimum between the local and remote's maximums when multipath is +6069 /// enabled, or `None` when disabled. +6070 /// +6071 /// For data that's received, we should use [`Self::local_max_path_id`] instead. +6072 /// The reasoning is that the remote might already have updated to its own newer +6073 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. +6074 fn max_path_id(&self) -> Option<PathId> { +6075 if self.is_multipath_negotiated() { +6076 Some(self.remote_max_path_id.min(self.local_max_path_id)) +6077 } else { +6078 None +6079 } +6080 } +6081 +6082 /// Add addresses the local endpoint considers are reachable for nat traversal +6083 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { +6084 if let Some(added) = self.iroh_hp.add_local_address(address)? { +6085 self.spaces[SpaceId::Data].pending.add_address.insert(added); +6086 }; +6087 Ok(()) +6088 } +6089 +6090 /// Removes an address the endpoing no longer considers reachable for nat traversal +6091 /// +6092 /// Addresses not present in the set will be silently ignored. +6093 pub fn remove_nat_traversal_address( +6094 &mut self, +6095 address: SocketAddr, +6096 ) -> Result<(), iroh_hp::Error> { +6097 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { +6098 self.spaces[SpaceId::Data] +6099 .pending +6100 .remove_address +6101 .insert(removed); +6102 } +6103 Ok(()) +6104 } +6105 +6106 /// Get the current local nat traversal addresses +6107 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6108 self.iroh_hp.get_local_nat_traversal_addresses() +6109 } +6110 +6111 /// Get the currently advertised nat traversal addresses by the server +6112 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6113 Ok(self +6114 .iroh_hp +6115 .client_side()? +6116 .get_remote_nat_traversal_addresses()) 6117 } 6118 -6119 /// Whether explicit congestion notification is in use on outgoing packets. -6120 #[cfg(test)] -6121 pub(crate) fn using_ecn(&self) -> bool { -6122 self.path_data(PathId::ZERO).sending_ecn -6123 } -6124 -6125 /// The number of received bytes in the current path -6126 #[cfg(test)] -6127 pub(crate) fn total_recvd(&self) -> u64 { -6128 self.path_data(PathId::ZERO).total_recvd -6129 } -6130 -6131 #[cfg(test)] -6132 pub(crate) fn active_local_cid_seq(&self) -> (u64, u64) { -6133 self.local_cid_state -6134 .get(&PathId::ZERO) -6135 .unwrap() -6136 .active_seq() -6137 } -6138 -6139 #[cfg(test)] -6140 #[track_caller] -6141 pub(crate) fn active_local_path_cid_seq(&self, path_id: u32) -> (u64, u64) { -6142 self.local_cid_state -6143 .get(&PathId(path_id)) -6144 .unwrap() -6145 .active_seq() -6146 } -6147 -6148 /// Instruct the peer to replace previously issued CIDs by sending a NEW_CONNECTION_ID frame -6149 /// with updated `retire_prior_to` field set to `v` -6150 #[cfg(test)] -6151 pub(crate) fn rotate_local_cid(&mut self, v: u64, now: Instant) { -6152 let n = self -6153 .local_cid_state -6154 .get_mut(&PathId::ZERO) -6155 .unwrap() -6156 .assign_retire_seq(v); -6157 self.endpoint_events -6158 .push_back(EndpointEventInner::NeedIdentifiers(PathId::ZERO, now, n)); -6159 } -6160 -6161 /// Check the current active remote CID sequence for `PathId::ZERO` -6162 #[cfg(test)] -6163 pub(crate) fn active_rem_cid_seq(&self) -> u64 { -6164 self.rem_cids.get(&PathId::ZERO).unwrap().active_seq() -6165 } -6166 -6167 /// Returns the detected maximum udp payload size for the current path -6168 #[cfg(test)] -6169 pub(crate) fn path_mtu(&self, path_id: PathId) -> u16 { -6170 self.path_data(path_id).current_mtu() -6171 } -6172 -6173 /// Triggers path validation on all paths -6174 #[cfg(test)] -6175 pub(crate) fn trigger_path_validation(&mut self) { -6176 for path in self.paths.values_mut() { -6177 path.data.send_new_challenge = true; -6178 } -6179 } +6119 /// Attempts to open a path for nat traversal. +6120 /// +6121 /// `ipv6` indicates if the path should be opened using an IPV6 remote. If the address is +6122 /// ignored, it will return `None`. +6123 /// +6124 /// On success returns the [`PathId`] and remote address of the path, as well as whether the path +6125 /// existed for the adjusted remote. +6126 fn open_nat_traversal_path( +6127 &mut self, +6128 now: Instant, +6129 (ip, port): (IpAddr, u16), +6130 ipv6: bool, +6131 ) -> Result<Option<(PathId, SocketAddr, bool)>, PathError> { +6132 // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes. +6133 let remote = match ip { +6134 IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port), +6135 IpAddr::V4(addr) => SocketAddr::new(addr.into(), port), +6136 IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port), +6137 IpAddr::V6(_) => { +6138 trace!("not using IPv6 nat candidate for IPv4 socket"); +6139 return Ok(None); +6140 } +6141 }; +6142 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? +6143 // By specifying None, we do two things: 1. open_path_ensure won't generate two +6144 // paths to the same remote and 2. we let the OS choose which interface to use for +6145 // sending on that path. +6146 let network_path = FourTuple { +6147 remote, +6148 local_ip: None, +6149 }; +6150 match self.open_path_ensure(network_path, PathStatus::Backup, now) { +6151 Ok((path_id, path_was_known)) => { +6152 if path_was_known { +6153 trace!(%path_id, %remote, "nat traversal: path existed for remote"); +6154 } +6155 Ok(Some((path_id, remote, path_was_known))) +6156 } +6157 Err(e) => { +6158 debug!(%remote, %e, "nat traversal: failed to probe remote"); +6159 Err(e) +6160 } +6161 } +6162 } +6163 +6164 /// Initiates a new nat traversal round +6165 /// +6166 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` +6167 /// frames, and initiating probing of the known remote addresses. When a new round is +6168 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. +6169 /// +6170 /// Returns the server addresses that are now being probed. +6171 /// If addresses fail due to spurious errors, these might succeed later and not be returned in +6172 /// this set. +6173 pub fn initiate_nat_traversal_round( +6174 &mut self, +6175 now: Instant, +6176 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { +6177 if self.state.is_closed() { +6178 return Err(iroh_hp::Error::Closed); +6179 } 6180 -6181 /// Whether we have 1-RTT data to send -6182 /// -6183 /// This checks for frames that can only be sent in the data space (1-RTT): -6184 /// - Pending PATH_CHALLENGE frames on the active and previous path if just migrated. -6185 /// - Pending PATH_RESPONSE frames. -6186 /// - Pending data to send in STREAM frames. -6187 /// - Pending DATAGRAM frames to send. -6188 /// -6189 /// See also [`PacketSpace::can_send`] which keeps track of all other frame types that -6190 /// may need to be sent. -6191 fn can_send_1rtt(&self, path_id: PathId, max_size: usize) -> SendableFrames { -6192 let path_exclusive = self.paths.get(&path_id).is_some_and(|path| { -6193 path.data.send_new_challenge -6194 || path -6195 .prev -6196 .as_ref() -6197 .is_some_and(|(_, path)| path.send_new_challenge) -6198 || !path.data.path_responses.is_empty() -6199 }); -6200 let other = self.streams.can_send_stream_data() -6201 || self -6202 .datagrams -6203 .outgoing -6204 .front() -6205 .is_some_and(|x| x.size(true) <= max_size); -6206 SendableFrames { -6207 acks: false, -6208 other, -6209 close: false, -6210 path_exclusive, -6211 } -6212 } -6213 -6214 /// Terminate the connection instantly, without sending a close packet -6215 fn kill(&mut self, reason: ConnectionError) { -6216 self.close_common(); -6217 self.state.move_to_drained(Some(reason)); -6218 self.endpoint_events.push_back(EndpointEventInner::Drained); -6219 } -6220 -6221 /// Storage size required for the largest packet that can be transmitted on all currently -6222 /// available paths -6223 /// -6224 /// Buffers passed to [`Connection::poll_transmit`] should be at least this large. -6225 /// -6226 /// When multipath is enabled, this value is the minimum MTU across all available paths. -6227 pub fn current_mtu(&self) -> u16 { -6228 self.paths -6229 .iter() -6230 .filter(|&(path_id, _path_state)| !self.abandoned_paths.contains(path_id)) -6231 .map(|(_path_id, path_state)| path_state.data.current_mtu()) -6232 .min() -6233 .expect("There is always at least one available path") -6234 } +6181 let client_state = self.iroh_hp.client_side_mut()?; +6182 let iroh_hp::NatTraversalRound { +6183 new_round, +6184 reach_out_at, +6185 addresses_to_probe, +6186 prev_round_path_ids, +6187 } = client_state.initiate_nat_traversal_round()?; +6188 +6189 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); +6190 +6191 for path_id in prev_round_path_ids { +6192 // TODO(@divma): this sounds reasonable but we need if this actually works for the +6193 // purposes of the protocol +6194 let validated = self +6195 .path(path_id) +6196 .map(|path| path.validated) +6197 .unwrap_or(false); +6198 +6199 if !validated { +6200 let _ = self.close_path( +6201 now, +6202 path_id, +6203 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), +6204 ); +6205 } +6206 } +6207 +6208 let mut err = None; +6209 +6210 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); +6211 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); +6212 let ipv6 = self +6213 .paths +6214 .values() +6215 .any(|p| p.data.network_path.remote.is_ipv6()); +6216 +6217 for (id, address) in addresses_to_probe { +6218 match self.open_nat_traversal_path(now, address, ipv6) { +6219 Ok(None) => {} +6220 Ok(Some((path_id, remote, path_was_known))) => { +6221 if !path_was_known { +6222 path_ids.push(path_id); +6223 probed_addresses.push(remote); +6224 } +6225 } +6226 Err(e) => { +6227 self.iroh_hp +6228 .client_side_mut() +6229 .expect("validated") +6230 .report_in_continuation(id, e); +6231 err.get_or_insert(e); +6232 } +6233 } +6234 } 6235 -6236 /// Size of non-frame data for a 1-RTT packet -6237 /// -6238 /// Quantifies space consumed by the QUIC header and AEAD tag. All other bytes in a packet are -6239 /// frames. Changes if the length of the remote connection ID changes, which is expected to be -6240 /// rare. If `pn` is specified, may additionally change unpredictably due to variations in -6241 /// latency and packet loss. -6242 fn predict_1rtt_overhead(&mut self, pn: u64, path: PathId) -> usize { -6243 let pn_len = PacketNumber::new( -6244 pn, -6245 self.spaces[SpaceId::Data] -6246 .for_path(path) -6247 .largest_acked_packet -6248 .unwrap_or(0), -6249 ) -6250 .len(); -6251 -6252 // 1 byte for flags -6253 1 + self -6254 .rem_cids -6255 .get(&path) -6256 .map(|cids| cids.active().len()) -6257 .unwrap_or(20) // Max CID len in QUIC v1 -6258 + pn_len -6259 + self.tag_len_1rtt() -6260 } -6261 -6262 fn predict_1rtt_overhead_no_pn(&self) -> usize { -6263 let pn_len = 4; -6264 -6265 let cid_len = self -6266 .rem_cids -6267 .values() -6268 .map(|cids| cids.active().len()) -6269 .max() -6270 .unwrap_or(20); // Max CID len in QUIC v1 -6271 -6272 // 1 byte for flags -6273 1 + cid_len + pn_len + self.tag_len_1rtt() -6274 } -6275 -6276 fn tag_len_1rtt(&self) -> usize { -6277 let key = match self.spaces[SpaceId::Data].crypto.as_ref() { -6278 Some(crypto) => Some(&*crypto.packet.local), -6279 None => self.zero_rtt_crypto.as_ref().map(|x| &*x.packet), -6280 }; -6281 // If neither Data nor 0-RTT keys are available, make a reasonable tag length guess. As of -6282 // this writing, all QUIC cipher suites use 16-byte tags. We could return `None` instead, -6283 // but that would needlessly prevent sending datagrams during 0-RTT. -6284 key.map_or(16, |x| x.tag_len()) +6236 if let Some(err) = err { +6237 // We failed to probe any addresses, bail out +6238 if probed_addresses.is_empty() { +6239 return Err(iroh_hp::Error::Multipath(err)); +6240 } +6241 } +6242 +6243 self.iroh_hp +6244 .client_side_mut() +6245 .expect("connection side validated") +6246 .set_round_path_ids(path_ids); +6247 +6248 Ok(probed_addresses) +6249 } +6250 +6251 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. +6252 /// +6253 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was +6254 /// successfully open. +6255 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { +6256 let client_state = self.iroh_hp.client_side_mut().ok()?; +6257 let (id, address) = client_state.continue_nat_traversal_round()?; +6258 let ipv6 = self +6259 .paths +6260 .values() +6261 .any(|p| p.data.network_path.remote.is_ipv6()); +6262 let open_result = self.open_nat_traversal_path(now, address, ipv6); +6263 let client_state = self.iroh_hp.client_side_mut().expect("validated"); +6264 match open_result { +6265 Ok(None) => Some(true), +6266 Ok(Some((path_id, _remote, path_was_known))) => { +6267 if !path_was_known { +6268 client_state.add_round_path_id(path_id); +6269 } +6270 Some(true) +6271 } +6272 Err(e) => { +6273 client_state.report_in_continuation(id, e); +6274 Some(false) +6275 } +6276 } +6277 } +6278} +6279 +6280impl fmt::Debug for Connection { +6281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +6282 f.debug_struct("Connection") +6283 .field("handshake_cid", &self.handshake_cid) +6284 .finish() 6285 } -6286 -6287 /// Mark the path as validated, and enqueue NEW_TOKEN frames to be sent as appropriate -6288 fn on_path_validated(&mut self, path_id: PathId) { -6289 self.path_data_mut(path_id).validated = true; -6290 let ConnectionSide::Server { server_config } = &self.side else { -6291 return; -6292 }; -6293 let network_path = self.path_data(path_id).network_path; -6294 let new_tokens = &mut self.spaces[SpaceId::Data as usize].pending.new_tokens; -6295 new_tokens.clear(); -6296 for _ in 0..server_config.validation_token.sent { -6297 new_tokens.push(network_path); -6298 } -6299 } -6300 -6301 /// Handle new path status information: PATH_STATUS_AVAILABLE, PATH_STATUS_BACKUP -6302 fn on_path_status(&mut self, path_id: PathId, status: PathStatus, status_seq_no: VarInt) { -6303 if let Some(path) = self.paths.get_mut(&path_id) { -6304 path.data.status.remote_update(status, status_seq_no); -6305 } else { -6306 debug!("PATH_STATUS_AVAILABLE received unknown path {:?}", path_id); -6307 } -6308 self.events.push_back( -6309 PathEvent::RemoteStatus { -6310 id: path_id, -6311 status, -6312 } -6313 .into(), -6314 ); -6315 } -6316 -6317 /// Returns the maximum [`PathId`] to be used for sending in this connection. -6318 /// -6319 /// This is calculated as minimum between the local and remote's maximums when multipath is -6320 /// enabled, or `None` when disabled. -6321 /// -6322 /// For data that's received, we should use [`Self::local_max_path_id`] instead. -6323 /// The reasoning is that the remote might already have updated to its own newer -6324 /// [`Self::max_path_id`] after sending out a `MAX_PATH_ID` frame, but it got re-ordered. -6325 fn max_path_id(&self) -> Option<PathId> { -6326 if self.is_multipath_negotiated() { -6327 Some(self.remote_max_path_id.min(self.local_max_path_id)) -6328 } else { -6329 None -6330 } -6331 } -6332 -6333 /// Add addresses the local endpoint considers are reachable for nat traversal -6334 pub fn add_nat_traversal_address(&mut self, address: SocketAddr) -> Result<(), iroh_hp::Error> { -6335 if let Some(added) = self.iroh_hp.add_local_address(address)? { -6336 self.spaces[SpaceId::Data].pending.add_address.insert(added); -6337 }; -6338 Ok(()) -6339 } -6340 -6341 /// Removes an address the endpoing no longer considers reachable for nat traversal -6342 /// -6343 /// Addresses not present in the set will be silently ignored. -6344 pub fn remove_nat_traversal_address( -6345 &mut self, -6346 address: SocketAddr, -6347 ) -> Result<(), iroh_hp::Error> { -6348 if let Some(removed) = self.iroh_hp.remove_local_address(address)? { -6349 self.spaces[SpaceId::Data] -6350 .pending -6351 .remove_address -6352 .insert(removed); -6353 } -6354 Ok(()) -6355 } -6356 -6357 /// Get the current local nat traversal addresses -6358 pub fn get_local_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6359 self.iroh_hp.get_local_nat_traversal_addresses() -6360 } -6361 -6362 /// Get the currently advertised nat traversal addresses by the server -6363 pub fn get_remote_nat_traversal_addresses(&self) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6364 Ok(self -6365 .iroh_hp -6366 .client_side()? -6367 .get_remote_nat_traversal_addresses()) -6368 } -6369 -6370 /// Attempts to open a path for nat traversal. -6371 /// -6372 /// `ipv6` indicates if the path should be opened using an IPV6 remote. If the address is -6373 /// ignored, it will return `None`. -6374 /// -6375 /// On success returns the [`PathId`] and remote address of the path, as well as whether the path -6376 /// existed for the adjusted remote. -6377 fn open_nat_traversal_path( -6378 &mut self, -6379 now: Instant, -6380 (ip, port): (IpAddr, u16), -6381 ipv6: bool, -6382 ) -> Result<Option<(PathId, SocketAddr, bool)>, PathError> { -6383 // If this endpoint is an IPv6 endpoint we use IPv6 addresses for all remotes. -6384 let remote = match ip { -6385 IpAddr::V4(addr) if ipv6 => SocketAddr::new(addr.to_ipv6_mapped().into(), port), -6386 IpAddr::V4(addr) => SocketAddr::new(addr.into(), port), -6387 IpAddr::V6(_) if ipv6 => SocketAddr::new(ip, port), -6388 IpAddr::V6(_) => { -6389 trace!("not using IPv6 nat candidate for IPv4 socket"); -6390 return Ok(None); -6391 } -6392 }; -6393 // TODO(matheus23): Probe the correct 4-tuple, instead of only a remote address? -6394 // By specifying None, we do two things: 1. open_path_ensure won't generate two -6395 // paths to the same remote and 2. we let the OS choose which interface to use for -6396 // sending on that path. -6397 let network_path = FourTuple { -6398 remote, -6399 local_ip: None, -6400 }; -6401 match self.open_path_ensure(network_path, PathStatus::Backup, now) { -6402 Ok((path_id, path_was_known)) => { -6403 if path_was_known { -6404 trace!(%path_id, %remote, "nat traversal: path existed for remote"); -6405 } -6406 Ok(Some((path_id, remote, path_was_known))) -6407 } -6408 Err(e) => { -6409 debug!(%remote, %e, "nat traversal: failed to probe remote"); -6410 Err(e) -6411 } -6412 } -6413 } -6414 -6415 /// Initiates a new nat traversal round -6416 /// -6417 /// A nat traversal round involves advertising the client's local addresses in `REACH_OUT` -6418 /// frames, and initiating probing of the known remote addresses. When a new round is -6419 /// initiated, the previous one is cancelled, and paths that have not been opened are closed. -6420 /// -6421 /// Returns the server addresses that are now being probed. -6422 /// If addresses fail due to spurious errors, these might succeed later and not be returned in -6423 /// this set. -6424 pub fn initiate_nat_traversal_round( -6425 &mut self, -6426 now: Instant, -6427 ) -> Result<Vec<SocketAddr>, iroh_hp::Error> { -6428 if self.state.is_closed() { -6429 return Err(iroh_hp::Error::Closed); -6430 } -6431 -6432 let client_state = self.iroh_hp.client_side_mut()?; -6433 let iroh_hp::NatTraversalRound { -6434 new_round, -6435 reach_out_at, -6436 addresses_to_probe, -6437 prev_round_path_ids, -6438 } = client_state.initiate_nat_traversal_round()?; -6439 -6440 self.spaces[SpaceId::Data].pending.reach_out = Some((new_round, reach_out_at)); -6441 -6442 for path_id in prev_round_path_ids { -6443 // TODO(@divma): this sounds reasonable but we need if this actually works for the -6444 // purposes of the protocol -6445 let validated = self -6446 .path(path_id) -6447 .map(|path| path.validated) -6448 .unwrap_or(false); -6449 -6450 if !validated { -6451 let _ = self.close_path( -6452 now, -6453 path_id, -6454 TransportErrorCode::APPLICATION_ABANDON_PATH.into(), -6455 ); -6456 } -6457 } -6458 -6459 let mut err = None; -6460 -6461 let mut path_ids = Vec::with_capacity(addresses_to_probe.len()); -6462 let mut probed_addresses = Vec::with_capacity(addresses_to_probe.len()); -6463 let ipv6 = self -6464 .paths -6465 .values() -6466 .any(|p| p.data.network_path.remote.is_ipv6()); -6467 -6468 for (id, address) in addresses_to_probe { -6469 match self.open_nat_traversal_path(now, address, ipv6) { -6470 Ok(None) => {} -6471 Ok(Some((path_id, remote, path_was_known))) => { -6472 if !path_was_known { -6473 path_ids.push(path_id); -6474 probed_addresses.push(remote); -6475 } -6476 } -6477 Err(e) => { -6478 self.iroh_hp -6479 .client_side_mut() -6480 .expect("validated") -6481 .report_in_continuation(id, e); -6482 err.get_or_insert(e); -6483 } -6484 } -6485 } -6486 -6487 if let Some(err) = err { -6488 // We failed to probe any addresses, bail out -6489 if probed_addresses.is_empty() { -6490 return Err(iroh_hp::Error::Multipath(err)); -6491 } -6492 } -6493 -6494 self.iroh_hp -6495 .client_side_mut() -6496 .expect("connection side validated") -6497 .set_round_path_ids(path_ids); -6498 -6499 Ok(probed_addresses) -6500 } -6501 -6502 /// Attempts to continue a nat traversal round by trying to open paths for pending client probes. -6503 /// -6504 /// If there was nothing to do, it returns `None`. Otherwise it returns whether the path was -6505 /// successfully open. -6506 fn continue_nat_traversal_round(&mut self, now: Instant) -> Option<bool> { -6507 let client_state = self.iroh_hp.client_side_mut().ok()?; -6508 let (id, address) = client_state.continue_nat_traversal_round()?; -6509 let ipv6 = self -6510 .paths -6511 .values() -6512 .any(|p| p.data.network_path.remote.is_ipv6()); -6513 let open_result = self.open_nat_traversal_path(now, address, ipv6); -6514 let client_state = self.iroh_hp.client_side_mut().expect("validated"); -6515 match open_result { -6516 Ok(None) => Some(true), -6517 Ok(Some((path_id, _remote, path_was_known))) => { -6518 if !path_was_known { -6519 client_state.add_round_path_id(path_id); -6520 } -6521 Some(true) -6522 } -6523 Err(e) => { -6524 client_state.report_in_continuation(id, e); -6525 Some(false) -6526 } -6527 } +6286} +6287 +6288#[derive(Debug, Copy, Clone, PartialEq, Eq)] +6289enum PathBlocked { +6290 No, +6291 AntiAmplification, +6292 Congestion, +6293 Pacing, +6294} +6295 +6296/// Fields of `Connection` specific to it being client-side or server-side +6297enum ConnectionSide { +6298 Client { +6299 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded +6300 token: Bytes, +6301 token_store: Arc<dyn TokenStore>, +6302 server_name: String, +6303 }, +6304 Server { +6305 server_config: Arc<ServerConfig>, +6306 }, +6307} +6308 +6309impl ConnectionSide { +6310 fn remote_may_migrate(&self, state: &State) -> bool { +6311 match self { +6312 Self::Server { server_config } => server_config.migration, +6313 Self::Client { .. } => { +6314 if let Some(hs) = state.as_handshake() { +6315 hs.allow_server_migration +6316 } else { +6317 false +6318 } +6319 } +6320 } +6321 } +6322 +6323 fn is_client(&self) -> bool { +6324 self.side().is_client() +6325 } +6326 +6327 fn is_server(&self) -> bool { +6328 self.side().is_server() +6329 } +6330 +6331 fn side(&self) -> Side { +6332 match *self { +6333 Self::Client { .. } => Side::Client, +6334 Self::Server { .. } => Side::Server, +6335 } +6336 } +6337} +6338 +6339impl From<SideArgs> for ConnectionSide { +6340 fn from(side: SideArgs) -> Self { +6341 match side { +6342 SideArgs::Client { +6343 token_store, +6344 server_name, +6345 } => Self::Client { +6346 token: token_store.take(&server_name).unwrap_or_default(), +6347 token_store, +6348 server_name, +6349 }, +6350 SideArgs::Server { +6351 server_config, +6352 pref_addr_cid: _, +6353 path_validated: _, +6354 } => Self::Server { server_config }, +6355 } +6356 } +6357} +6358 +6359/// Parameters to `Connection::new` specific to it being client-side or server-side +6360pub(crate) enum SideArgs { +6361 Client { +6362 token_store: Arc<dyn TokenStore>, +6363 server_name: String, +6364 }, +6365 Server { +6366 server_config: Arc<ServerConfig>, +6367 pref_addr_cid: Option<ConnectionId>, +6368 path_validated: bool, +6369 }, +6370} +6371 +6372impl SideArgs { +6373 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { +6374 match *self { +6375 Self::Client { .. } => None, +6376 Self::Server { pref_addr_cid, .. } => pref_addr_cid, +6377 } +6378 } +6379 +6380 pub(crate) fn path_validated(&self) -> bool { +6381 match *self { +6382 Self::Client { .. } => true, +6383 Self::Server { path_validated, .. } => path_validated, +6384 } +6385 } +6386 +6387 pub(crate) fn side(&self) -> Side { +6388 match *self { +6389 Self::Client { .. } => Side::Client, +6390 Self::Server { .. } => Side::Server, +6391 } +6392 } +6393} +6394 +6395/// Reasons why a connection might be lost +6396#[derive(Debug, Error, Clone, PartialEq, Eq)] +6397pub enum ConnectionError { +6398 /// The peer doesn't implement any supported version +6399 #[error("peer doesn't implement any supported version")] +6400 VersionMismatch, +6401 /// The peer violated the QUIC specification as understood by this implementation +6402 #[error(transparent)] +6403 TransportError(#[from] TransportError), +6404 /// The peer's QUIC stack aborted the connection automatically +6405 #[error("aborted by peer: {0}")] +6406 ConnectionClosed(frame::ConnectionClose), +6407 /// The peer closed the connection +6408 #[error("closed by peer: {0}")] +6409 ApplicationClosed(frame::ApplicationClose), +6410 /// The peer is unable to continue processing this connection, usually due to having restarted +6411 #[error("reset by peer")] +6412 Reset, +6413 /// Communication with the peer has lapsed for longer than the negotiated idle timeout +6414 /// +6415 /// If neither side is sending keep-alives, a connection will time out after a long enough idle +6416 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] +6417 /// and [`TransportConfig::keep_alive_interval()`]. +6418 #[error("timed out")] +6419 TimedOut, +6420 /// The local application closed the connection +6421 #[error("closed")] +6422 LocallyClosed, +6423 /// The connection could not be created because not enough of the CID space is available +6424 /// +6425 /// Try using longer connection IDs. +6426 #[error("CIDs exhausted")] +6427 CidsExhausted, +6428} +6429 +6430impl From<Close> for ConnectionError { +6431 fn from(x: Close) -> Self { +6432 match x { +6433 Close::Connection(reason) => Self::ConnectionClosed(reason), +6434 Close::Application(reason) => Self::ApplicationClosed(reason), +6435 } +6436 } +6437} +6438 +6439// For compatibility with API consumers +6440impl From<ConnectionError> for io::Error { +6441 fn from(x: ConnectionError) -> Self { +6442 use ConnectionError::*; +6443 let kind = match x { +6444 TimedOut => io::ErrorKind::TimedOut, +6445 Reset => io::ErrorKind::ConnectionReset, +6446 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, +6447 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { +6448 io::ErrorKind::Other +6449 } +6450 }; +6451 Self::new(kind, x) +6452 } +6453} +6454 +6455/// Errors that might trigger a path being closed +6456// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API +6457#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] +6458pub enum PathError { +6459 /// The extension was not negotiated with the peer +6460 #[error("multipath extension not negotiated")] +6461 MultipathNotNegotiated, +6462 /// Paths can only be opened client-side +6463 #[error("the server side may not open a path")] +6464 ServerSideNotAllowed, +6465 /// Current limits do not allow us to open more paths +6466 #[error("maximum number of concurrent paths reached")] +6467 MaxPathIdReached, +6468 /// No remote CIDs available to open a new path +6469 #[error("remoted CIDs exhausted")] +6470 RemoteCidsExhausted, +6471 /// Path could not be validated and will be abandoned +6472 #[error("path validation failed")] +6473 ValidationFailed, +6474 /// The remote address for the path is not supported by the endpoint +6475 #[error("invalid remote address")] +6476 InvalidRemoteAddress(SocketAddr), +6477} +6478 +6479/// Errors triggered when abandoning a path +6480#[derive(Debug, Error, Clone, Eq, PartialEq)] +6481pub enum ClosePathError { +6482 /// The path is already closed or was never opened +6483 #[error("closed path")] +6484 ClosedPath, +6485 /// This is the last path, which can not be abandoned +6486 #[error("last open path")] +6487 LastOpenPath, +6488} +6489 +6490/// Error when the multipath extension was not negotiated, but attempted to be used. +6491#[derive(Debug, Error, Clone, Copy)] +6492#[error("Multipath extension not negotiated")] +6493pub struct MultipathNotNegotiated { +6494 _private: (), +6495} +6496 +6497/// Events of interest to the application +6498#[derive(Debug)] +6499pub enum Event { +6500 /// The connection's handshake data is ready +6501 HandshakeDataReady, +6502 /// The connection was successfully established +6503 Connected, +6504 /// The TLS handshake was confirmed +6505 HandshakeConfirmed, +6506 /// The connection was lost +6507 /// +6508 /// Emitted if the peer closes the connection or an error is encountered. +6509 ConnectionLost { +6510 /// Reason that the connection was closed +6511 reason: ConnectionError, +6512 }, +6513 /// Stream events +6514 Stream(StreamEvent), +6515 /// One or more application datagrams have been received +6516 DatagramReceived, +6517 /// One or more application datagrams have been sent after blocking +6518 DatagramsUnblocked, +6519 /// (Multi)Path events +6520 Path(PathEvent), +6521 /// Iroh's nat traversal events +6522 NatTraversal(iroh_hp::Event), +6523} +6524 +6525impl From<PathEvent> for Event { +6526 fn from(source: PathEvent) -> Self { +6527 Self::Path(source) 6528 } 6529} 6530 -6531impl fmt::Debug for Connection { -6532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -6533 f.debug_struct("Connection") -6534 .field("handshake_cid", &self.handshake_cid) -6535 .finish() -6536 } -6537} -6538 -6539#[derive(Debug, Copy, Clone, PartialEq, Eq)] -6540enum PathBlocked { -6541 No, -6542 AntiAmplification, -6543 Congestion, -6544 Pacing, -6545} +6531fn get_max_ack_delay(params: &TransportParameters) -> Duration { +6532 Duration::from_micros(params.max_ack_delay.0 * 1000) +6533} +6534 +6535// Prevents overflow and improves behavior in extreme circumstances +6536const MAX_BACKOFF_EXPONENT: u32 = 16; +6537 +6538/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag +6539/// +6540/// This must be at least as large as the header for a well-formed empty packet to be coalesced, +6541/// plus some space for frames. We only care about handshake headers because short header packets +6542/// necessarily have smaller headers, and initial packets are only ever the first packet in a +6543/// datagram (because we coalesce in ascending packet space order and the only reason to split a +6544/// packet is when packet space changes). +6545const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; 6546 -6547/// Fields of `Connection` specific to it being client-side or server-side -6548enum ConnectionSide { -6549 Client { -6550 /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded -6551 token: Bytes, -6552 token_store: Arc<dyn TokenStore>, -6553 server_name: String, -6554 }, -6555 Server { -6556 server_config: Arc<ServerConfig>, -6557 }, -6558} +6547/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header +6548/// +6549/// Excludes packet-type-specific fields such as packet number or Initial token +6550// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + +6551// scid len + scid + length + pn +6552const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = +6553 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; +6554 +6555/// Perform key updates this many packets before the AEAD confidentiality limit. +6556/// +6557/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss. +6558const KEY_UPDATE_MARGIN: u64 = 10_000; 6559 -6560impl ConnectionSide { -6561 fn remote_may_migrate(&self, state: &State) -> bool { -6562 match self { -6563 Self::Server { server_config } => server_config.migration, -6564 Self::Client { .. } => { -6565 if let Some(hs) = state.as_handshake() { -6566 hs.allow_server_migration -6567 } else { -6568 false -6569 } -6570 } -6571 } -6572 } -6573 -6574 fn is_client(&self) -> bool { -6575 self.side().is_client() -6576 } -6577 -6578 fn is_server(&self) -> bool { -6579 self.side().is_server() -6580 } -6581 -6582 fn side(&self) -> Side { -6583 match *self { -6584 Self::Client { .. } => Side::Client, -6585 Self::Server { .. } => Side::Server, -6586 } -6587 } -6588} -6589 -6590impl From<SideArgs> for ConnectionSide { -6591 fn from(side: SideArgs) -> Self { -6592 match side { -6593 SideArgs::Client { -6594 token_store, -6595 server_name, -6596 } => Self::Client { -6597 token: token_store.take(&server_name).unwrap_or_default(), -6598 token_store, -6599 server_name, -6600 }, -6601 SideArgs::Server { -6602 server_config, -6603 pref_addr_cid: _, -6604 path_validated: _, -6605 } => Self::Server { server_config }, -6606 } -6607 } -6608} -6609 -6610/// Parameters to `Connection::new` specific to it being client-side or server-side -6611pub(crate) enum SideArgs { -6612 Client { -6613 token_store: Arc<dyn TokenStore>, -6614 server_name: String, -6615 }, -6616 Server { -6617 server_config: Arc<ServerConfig>, -6618 pref_addr_cid: Option<ConnectionId>, -6619 path_validated: bool, -6620 }, -6621} -6622 -6623impl SideArgs { -6624 pub(crate) fn pref_addr_cid(&self) -> Option<ConnectionId> { -6625 match *self { -6626 Self::Client { .. } => None, -6627 Self::Server { pref_addr_cid, .. } => pref_addr_cid, -6628 } -6629 } -6630 -6631 pub(crate) fn path_validated(&self) -> bool { -6632 match *self { -6633 Self::Client { .. } => true, -6634 Self::Server { path_validated, .. } => path_validated, -6635 } -6636 } -6637 -6638 pub(crate) fn side(&self) -> Side { -6639 match *self { -6640 Self::Client { .. } => Side::Client, -6641 Self::Server { .. } => Side::Server, -6642 } -6643 } -6644} -6645 -6646/// Reasons why a connection might be lost -6647#[derive(Debug, Error, Clone, PartialEq, Eq)] -6648pub enum ConnectionError { -6649 /// The peer doesn't implement any supported version -6650 #[error("peer doesn't implement any supported version")] -6651 VersionMismatch, -6652 /// The peer violated the QUIC specification as understood by this implementation -6653 #[error(transparent)] -6654 TransportError(#[from] TransportError), -6655 /// The peer's QUIC stack aborted the connection automatically -6656 #[error("aborted by peer: {0}")] -6657 ConnectionClosed(frame::ConnectionClose), -6658 /// The peer closed the connection -6659 #[error("closed by peer: {0}")] -6660 ApplicationClosed(frame::ApplicationClose), -6661 /// The peer is unable to continue processing this connection, usually due to having restarted -6662 #[error("reset by peer")] -6663 Reset, -6664 /// Communication with the peer has lapsed for longer than the negotiated idle timeout -6665 /// -6666 /// If neither side is sending keep-alives, a connection will time out after a long enough idle -6667 /// period even if the peer is still reachable. See also [`TransportConfig::max_idle_timeout()`] -6668 /// and [`TransportConfig::keep_alive_interval()`]. -6669 #[error("timed out")] -6670 TimedOut, -6671 /// The local application closed the connection -6672 #[error("closed")] -6673 LocallyClosed, -6674 /// The connection could not be created because not enough of the CID space is available -6675 /// -6676 /// Try using longer connection IDs. -6677 #[error("CIDs exhausted")] -6678 CidsExhausted, -6679} -6680 -6681impl From<Close> for ConnectionError { -6682 fn from(x: Close) -> Self { -6683 match x { -6684 Close::Connection(reason) => Self::ConnectionClosed(reason), -6685 Close::Application(reason) => Self::ApplicationClosed(reason), -6686 } -6687 } -6688} -6689 -6690// For compatibility with API consumers -6691impl From<ConnectionError> for io::Error { -6692 fn from(x: ConnectionError) -> Self { -6693 use ConnectionError::*; -6694 let kind = match x { -6695 TimedOut => io::ErrorKind::TimedOut, -6696 Reset => io::ErrorKind::ConnectionReset, -6697 ApplicationClosed(_) | ConnectionClosed(_) => io::ErrorKind::ConnectionAborted, -6698 TransportError(_) | VersionMismatch | LocallyClosed | CidsExhausted => { -6699 io::ErrorKind::Other -6700 } -6701 }; -6702 Self::new(kind, x) +6560#[derive(Default)] +6561struct SentFrames { +6562 retransmits: ThinRetransmits, +6563 /// The packet number of the largest acknowledged packet for each path +6564 largest_acked: FxHashMap<PathId, u64>, +6565 stream_frames: StreamMetaVec, +6566 /// Whether the packet contains non-retransmittable frames (like datagrams) +6567 non_retransmits: bool, +6568 /// If the datagram containing these frames should be padded to the min MTU +6569 requires_padding: bool, +6570} +6571 +6572impl SentFrames { +6573 /// Returns whether the packet contains only ACKs +6574 fn is_ack_only(&self, streams: &StreamsState) -> bool { +6575 !self.largest_acked.is_empty() +6576 && !self.non_retransmits +6577 && self.stream_frames.is_empty() +6578 && self.retransmits.is_empty(streams) +6579 } +6580 +6581 fn retransmits_mut(&mut self) -> &mut Retransmits { +6582 self.retransmits.get_or_create() +6583 } +6584 +6585 fn record_sent_frame(&mut self, frame: frame::EncodableFrame<'_>) { +6586 use frame::EncodableFrame::*; +6587 match frame { +6588 PathAck(path_ack_encoder) => { +6589 if let Some(max) = path_ack_encoder.ranges.max() { +6590 self.largest_acked.insert(path_ack_encoder.path_id, max); +6591 } +6592 } +6593 Ack(ack_encoder) => { +6594 if let Some(max) = ack_encoder.ranges.max() { +6595 self.largest_acked.insert(PathId::ZERO, max); +6596 } +6597 } +6598 Close(_) => { /* non retransmittable, but after this we don't really care */ } +6599 PathResponse(_) => self.non_retransmits = true, +6600 HandshakeDone(_) => self.retransmits_mut().handshake_done = true, +6601 ReachOut(frame::ReachOut { round, ip, port }) => self +6602 .retransmits_mut() +6603 .reach_out +6604 .get_or_insert_with(|| (round, Vec::new())) +6605 .1 +6606 .push((ip, port)), +6607 ObservedAddr(_) => self.retransmits_mut().observed_addr = true, +6608 Ping(_) => self.non_retransmits = true, +6609 ImmediateAck(_) => self.non_retransmits = true, +6610 AckFrequency(_) => self.retransmits_mut().ack_frequency = true, +6611 PathChallenge(_) => self.non_retransmits = true, +6612 Crypto(crypto) => self.retransmits_mut().crypto.push_back(crypto), +6613 PathAbandon(path_abandon) => { +6614 self.retransmits_mut() +6615 .path_abandon +6616 .entry(path_abandon.path_id) +6617 .or_insert(path_abandon.error_code); +6618 } +6619 PathStatusAvailable(frame::PathStatusAvailable { path_id, .. }) +6620 | PathStatusBackup(frame::PathStatusBackup { path_id, .. }) => { +6621 self.retransmits_mut().path_status.insert(path_id); +6622 } +6623 MaxPathId(_) => self.retransmits_mut().max_path_id = true, +6624 PathsBlocked(_) => self.retransmits_mut().paths_blocked = true, +6625 PathCidsBlocked(path_cids_blocked) => { +6626 self.retransmits_mut() +6627 .path_cids_blocked +6628 .insert(path_cids_blocked.path_id); +6629 } +6630 ResetStream(reset) => self +6631 .retransmits_mut() +6632 .reset_stream +6633 .push((reset.id, reset.error_code)), +6634 StopSending(stop_sending) => self.retransmits_mut().stop_sending.push(stop_sending), +6635 NewConnectionId(new_cid) => self.retransmits_mut().new_cids.push(new_cid.issued()), +6636 RetireConnectionId(retire_cid) => self +6637 .retransmits_mut() +6638 .retire_cids +6639 .push((retire_cid.path_id.unwrap_or_default(), retire_cid.sequence)), +6640 Datagram(_) => self.non_retransmits = true, +6641 NewToken(_) => {} +6642 AddAddress(add_address) => { +6643 self.retransmits_mut().add_address.insert(add_address); +6644 } +6645 RemoveAddress(remove_address) => { +6646 self.retransmits_mut().remove_address.insert(remove_address); +6647 } +6648 StreamMeta(stream_meta_encoder) => self.stream_frames.push(stream_meta_encoder.meta), +6649 MaxData(_) => self.retransmits_mut().max_data = true, +6650 MaxStreamData(max) => { +6651 self.retransmits_mut().max_stream_data.insert(max.id); +6652 } +6653 MaxStreams(max_streams) => { +6654 self.retransmits_mut().max_stream_id[max_streams.dir as usize] = true +6655 } +6656 } +6657 } +6658} +6659 +6660/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. +6661/// +6662/// 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.> +6663/// +6664/// 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.> +6665/// +6666/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. +6667fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { +6668 match (x, y) { +6669 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, +6670 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), +6671 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), +6672 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), +6673 } +6674} +6675 +6676#[cfg(test)] +6677mod tests { +6678 use super::*; +6679 +6680 #[test] +6681 fn negotiate_max_idle_timeout_commutative() { +6682 let test_params = [ +6683 (None, None, None), +6684 (None, Some(VarInt(0)), None), +6685 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), +6686 (Some(VarInt(0)), Some(VarInt(0)), None), +6687 ( +6688 Some(VarInt(2)), +6689 Some(VarInt(0)), +6690 Some(Duration::from_millis(2)), +6691 ), +6692 ( +6693 Some(VarInt(1)), +6694 Some(VarInt(4)), +6695 Some(Duration::from_millis(1)), +6696 ), +6697 ]; +6698 +6699 for (left, right, result) in test_params { +6700 assert_eq!(negotiate_max_idle_timeout(left, right), result); +6701 assert_eq!(negotiate_max_idle_timeout(right, left), result); +6702 } 6703 } -6704} -6705 -6706/// Errors that might trigger a path being closed -6707// TODO(@divma): maybe needs to be reworked based on what we want to do with the public API -6708#[derive(Debug, Error, PartialEq, Eq, Clone, Copy)] -6709pub enum PathError { -6710 /// The extension was not negotiated with the peer -6711 #[error("multipath extension not negotiated")] -6712 MultipathNotNegotiated, -6713 /// Paths can only be opened client-side -6714 #[error("the server side may not open a path")] -6715 ServerSideNotAllowed, -6716 /// Current limits do not allow us to open more paths -6717 #[error("maximum number of concurrent paths reached")] -6718 MaxPathIdReached, -6719 /// No remote CIDs available to open a new path -6720 #[error("remoted CIDs exhausted")] -6721 RemoteCidsExhausted, -6722 /// Path could not be validated and will be abandoned -6723 #[error("path validation failed")] -6724 ValidationFailed, -6725 /// The remote address for the path is not supported by the endpoint -6726 #[error("invalid remote address")] -6727 InvalidRemoteAddress(SocketAddr), -6728} -6729 -6730/// Errors triggered when abandoning a path -6731#[derive(Debug, Error, Clone, Eq, PartialEq)] -6732pub enum ClosePathError { -6733 /// The path is already closed or was never opened -6734 #[error("closed path")] -6735 ClosedPath, -6736 /// This is the last path, which can not be abandoned -6737 #[error("last open path")] -6738 LastOpenPath, -6739} -6740 -6741/// Error when the multipath extension was not negotiated, but attempted to be used. -6742#[derive(Debug, Error, Clone, Copy)] -6743#[error("Multipath extension not negotiated")] -6744pub struct MultipathNotNegotiated { -6745 _private: (), -6746} -6747 -6748/// Events of interest to the application -6749#[derive(Debug)] -6750pub enum Event { -6751 /// The connection's handshake data is ready -6752 HandshakeDataReady, -6753 /// The connection was successfully established -6754 Connected, -6755 /// The TLS handshake was confirmed -6756 HandshakeConfirmed, -6757 /// The connection was lost -6758 /// -6759 /// Emitted if the peer closes the connection or an error is encountered. -6760 ConnectionLost { -6761 /// Reason that the connection was closed -6762 reason: ConnectionError, -6763 }, -6764 /// Stream events -6765 Stream(StreamEvent), -6766 /// One or more application datagrams have been received -6767 DatagramReceived, -6768 /// One or more application datagrams have been sent after blocking -6769 DatagramsUnblocked, -6770 /// (Multi)Path events -6771 Path(PathEvent), -6772 /// Iroh's nat traversal events -6773 NatTraversal(iroh_hp::Event), -6774} -6775 -6776impl From<PathEvent> for Event { -6777 fn from(source: PathEvent) -> Self { -6778 Self::Path(source) -6779 } -6780} -6781 -6782fn get_max_ack_delay(params: &TransportParameters) -> Duration { -6783 Duration::from_micros(params.max_ack_delay.0 * 1000) -6784} -6785 -6786// Prevents overflow and improves behavior in extreme circumstances -6787const MAX_BACKOFF_EXPONENT: u32 = 16; -6788 -6789/// Minimal remaining size to allow packet coalescing, excluding cryptographic tag -6790/// -6791/// This must be at least as large as the header for a well-formed empty packet to be coalesced, -6792/// plus some space for frames. We only care about handshake headers because short header packets -6793/// necessarily have smaller headers, and initial packets are only ever the first packet in a -6794/// datagram (because we coalesce in ascending packet space order and the only reason to split a -6795/// packet is when packet space changes). -6796const MIN_PACKET_SPACE: usize = MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE + 32; -6797 -6798/// Largest amount of space that could be occupied by a Handshake or 0-RTT packet's header -6799/// -6800/// Excludes packet-type-specific fields such as packet number or Initial token -6801// https://www.rfc-editor.org/rfc/rfc9000.html#name-0-rtt: flags + version + dcid len + dcid + -6802// scid len + scid + length + pn -6803const MAX_HANDSHAKE_OR_0RTT_HEADER_SIZE: usize = -6804 1 + 4 + 1 + MAX_CID_SIZE + 1 + MAX_CID_SIZE + VarInt::from_u32(u16::MAX as u32).size() + 4; -6805 -6806/// Perform key updates this many packets before the AEAD confidentiality limit. -6807/// -6808/// Chosen arbitrarily, intended to be large enough to prevent spurious connection loss. -6809const KEY_UPDATE_MARGIN: u64 = 10_000; -6810 -6811#[derive(Default)] -6812struct SentFrames { -6813 retransmits: ThinRetransmits, -6814 /// The packet number of the largest acknowledged packet for each path -6815 largest_acked: FxHashMap<PathId, u64>, -6816 stream_frames: StreamMetaVec, -6817 /// Whether the packet contains non-retransmittable frames (like datagrams) -6818 non_retransmits: bool, -6819 /// If the datagram containing these frames should be padded to the min MTU -6820 requires_padding: bool, -6821} -6822 -6823impl SentFrames { -6824 /// Returns whether the packet contains only ACKs -6825 fn is_ack_only(&self, streams: &StreamsState) -> bool { -6826 !self.largest_acked.is_empty() -6827 && !self.non_retransmits -6828 && self.stream_frames.is_empty() -6829 && self.retransmits.is_empty(streams) -6830 } -6831} -6832 -6833/// Compute the negotiated idle timeout based on local and remote max_idle_timeout transport parameters. -6834/// -6835/// 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.> -6836/// -6837/// 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.> -6838/// -6839/// Returns the negotiated idle timeout as a `Duration`, or `None` when both endpoints have opted out of idle timeout. -6840fn negotiate_max_idle_timeout(x: Option<VarInt>, y: Option<VarInt>) -> Option<Duration> { -6841 match (x, y) { -6842 (Some(VarInt(0)) | None, Some(VarInt(0)) | None) => None, -6843 (Some(VarInt(0)) | None, Some(y)) => Some(Duration::from_millis(y.0)), -6844 (Some(x), Some(VarInt(0)) | None) => Some(Duration::from_millis(x.0)), -6845 (Some(x), Some(y)) => Some(Duration::from_millis(cmp::min(x, y).0)), -6846 } -6847} -6848 -6849#[cfg(test)] -6850mod tests { -6851 use super::*; -6852 -6853 #[test] -6854 fn negotiate_max_idle_timeout_commutative() { -6855 let test_params = [ -6856 (None, None, None), -6857 (None, Some(VarInt(0)), None), -6858 (None, Some(VarInt(2)), Some(Duration::from_millis(2))), -6859 (Some(VarInt(0)), Some(VarInt(0)), None), -6860 ( -6861 Some(VarInt(2)), -6862 Some(VarInt(0)), -6863 Some(Duration::from_millis(2)), -6864 ), -6865 ( -6866 Some(VarInt(1)), -6867 Some(VarInt(4)), -6868 Some(Duration::from_millis(1)), -6869 ), -6870 ]; -6871 -6872 for (left, right, result) in test_params { -6873 assert_eq!(negotiate_max_idle_timeout(left, right), result); -6874 assert_eq!(negotiate_max_idle_timeout(right, left), result); -6875 } -6876 } -6877}

\ No newline at end of file +6704}

\ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html index de299604a..753742c67 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/packet_builder.rs.html @@ -4,372 +4,430 @@ 4 5use super::{Connection, PathId, SentFrames, TransmitBuf, spaces::SentPacket}; 6use crate::{ -7 ConnectionId, Instant, MIN_INITIAL_SIZE, TransportError, TransportErrorCode, -8 connection::{ConnectionSide, qlog::QlogSentPacket}, -9 frame::{self, Close}, -10 packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId}, -11}; -12 -13/// QUIC packet builder -14/// -15/// This allows building QUIC packets: it takes care of writing the header, allows writing -16/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it -17/// encrypts the packet so it is ready to be sent on the wire. -18/// -19/// The builder manages the write buffer into which the packet is written, and directly -20/// implements [`BufMut`] to write frames into the packet. -21pub(super) struct PacketBuilder<'a, 'b> { -22 pub(super) buf: &'a mut TransmitBuf<'b>, -23 pub(super) space: SpaceId, -24 path: PathId, -25 pub(super) partial_encode: PartialEncode, -26 pub(super) ack_eliciting: bool, -27 pub(super) exact_number: u64, -28 pub(super) short_header: bool, -29 /// Smallest absolute position in the associated buffer that must be occupied by this packet's -30 /// frames -31 pub(super) min_size: usize, -32 pub(super) tag_len: usize, -33 pub(super) _span: tracing::span::EnteredSpan, -34} -35 -36impl<'a, 'b> PacketBuilder<'a, 'b> { -37 /// Write a new packet header to `buffer` and determine the packet's properties -38 /// -39 /// Marks the connection drained and returns `None` if the confidentiality limit would be -40 /// violated. -41 pub(super) fn new( -42 now: Instant, -43 space_id: SpaceId, -44 path_id: PathId, -45 dst_cid: ConnectionId, -46 buffer: &'a mut TransmitBuf<'b>, -47 ack_eliciting: bool, -48 conn: &mut Connection, -49 #[allow(unused)] qlog: &mut QlogSentPacket, -50 ) -> Option<Self> -51 where -52 'b: 'a, -53 { -54 let version = conn.version; -55 // Initiate key update if we're approaching the confidentiality limit -56 let sent_with_keys = conn.spaces[space_id].sent_with_keys(); -57 if space_id == SpaceId::Data { -58 if sent_with_keys >= conn.key_phase_size { -59 debug!("routine key update due to phase exhaustion"); -60 conn.force_key_update(); -61 } -62 } else { -63 let confidentiality_limit = conn.spaces[space_id] -64 .crypto -65 .as_ref() -66 .map_or_else( -67 || &conn.zero_rtt_crypto.as_ref().unwrap().packet, -68 |keys| &keys.packet.local, -69 ) -70 .confidentiality_limit(); -71 if sent_with_keys.saturating_add(1) == confidentiality_limit { -72 // We still have time to attempt a graceful close -73 conn.close_inner( -74 now, -75 Close::Connection(frame::ConnectionClose { -76 error_code: TransportErrorCode::AEAD_LIMIT_REACHED, -77 frame_type: frame::MaybeFrame::None, -78 reason: Bytes::from_static(b"confidentiality limit reached"), -79 }), -80 ) -81 } else if sent_with_keys > confidentiality_limit { -82 // Confidentiality limited violated and there's nothing we can do -83 conn.kill( -84 TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached").into(), -85 ); -86 return None; -87 } -88 } -89 -90 let space = &mut conn.spaces[space_id]; -91 let exact_number = space.for_path(path_id).get_tx_number(&mut conn.rng); -92 let span = trace_span!("send", space = ?space_id, pn = exact_number, %path_id).entered(); +7 ConnectionId, FrameStats, Instant, MIN_INITIAL_SIZE, TransportError, TransportErrorCode, +8 coding::Encodable, +9 connection::{ConnectionSide, qlog::QlogSentPacket, spaces::Retransmits}, +10 frame::{self, Close, EncodableFrame}, +11 packet::{FIXED_BIT, Header, InitialHeader, LongType, PacketNumber, PartialEncode, SpaceId}, +12}; +13 +14/// QUIC packet builder +15/// +16/// This allows building QUIC packets: it takes care of writing the header, allows writing +17/// frames and on [`PacketBuilder::finish`] (or [`PacketBuilder::finish_and_track`]) it +18/// encrypts the packet so it is ready to be sent on the wire. +19/// +20/// The builder manages the write buffer into which the packet is written, and directly +21/// implements [`BufMut`] to write frames into the packet. +22pub(super) struct PacketBuilder<'a, 'b> { +23 pub(super) buf: &'a mut TransmitBuf<'b>, +24 pub(super) space: SpaceId, +25 path: PathId, +26 pub(super) partial_encode: PartialEncode, +27 pub(super) ack_eliciting: bool, +28 pub(super) exact_number: u64, +29 pub(super) short_header: bool, +30 /// Smallest absolute position in the associated buffer that must be occupied by this packet's +31 /// frames +32 pub(super) min_size: usize, +33 pub(super) tag_len: usize, +34 pub(super) _span: tracing::span::EnteredSpan, +35 qlog: QlogSentPacket, +36 sent_frames: SentFrames, +37} +38 +39impl<'a, 'b> PacketBuilder<'a, 'b> { +40 /// Write a new packet header to `buffer` and determine the packet's properties +41 /// +42 /// Marks the connection drained and returns `None` if the confidentiality limit would be +43 /// violated. +44 pub(super) fn new( +45 now: Instant, +46 space_id: SpaceId, +47 path_id: PathId, +48 dst_cid: ConnectionId, +49 buffer: &'a mut TransmitBuf<'b>, +50 ack_eliciting: bool, +51 conn: &mut Connection, +52 ) -> Option<Self> +53 where +54 'b: 'a, +55 { +56 let mut qlog = QlogSentPacket::default(); +57 +58 let version = conn.version; +59 // Initiate key update if we're approaching the confidentiality limit +60 let sent_with_keys = conn.spaces[space_id].sent_with_keys(); +61 if space_id == SpaceId::Data { +62 if sent_with_keys >= conn.key_phase_size { +63 debug!("routine key update due to phase exhaustion"); +64 conn.force_key_update(); +65 } +66 } else { +67 let confidentiality_limit = conn.spaces[space_id] +68 .crypto +69 .as_ref() +70 .map_or_else( +71 || &conn.zero_rtt_crypto.as_ref().unwrap().packet, +72 |keys| &keys.packet.local, +73 ) +74 .confidentiality_limit(); +75 if sent_with_keys.saturating_add(1) == confidentiality_limit { +76 // We still have time to attempt a graceful close +77 conn.close_inner( +78 now, +79 Close::Connection(frame::ConnectionClose { +80 error_code: TransportErrorCode::AEAD_LIMIT_REACHED, +81 frame_type: frame::MaybeFrame::None, +82 reason: Bytes::from_static(b"confidentiality limit reached"), +83 }), +84 ) +85 } else if sent_with_keys > confidentiality_limit { +86 // Confidentiality limited violated and there's nothing we can do +87 conn.kill( +88 TransportError::AEAD_LIMIT_REACHED("confidentiality limit reached").into(), +89 ); +90 return None; +91 } +92 } 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 } +94 let space = &mut conn.spaces[space_id]; +95 let exact_number = space.for_path(path_id).get_tx_number(&mut conn.rng); +96 let span = trace_span!("send", space = ?space_id, pn = exact_number, %path_id).entered(); +97 +98 let number = PacketNumber::new( +99 exact_number, +100 space.for_path(path_id).largest_acked_packet.unwrap_or(0), +101 ); +102 let header = match space_id { +103 SpaceId::Data if space.crypto.is_some() => Header::Short { +104 dst_cid, +105 number, +106 spin: if conn.spin_enabled { +107 conn.spin +108 } else { +109 conn.rng.random() +110 }, +111 key_phase: conn.key_phase, +112 }, +113 SpaceId::Data => Header::Long { +114 ty: LongType::ZeroRtt, +115 src_cid: conn.handshake_cid, +116 dst_cid, +117 number, +118 version, +119 }, +120 SpaceId::Handshake => Header::Long { +121 ty: LongType::Handshake, +122 src_cid: conn.handshake_cid, +123 dst_cid, +124 number, +125 version, +126 }, +127 SpaceId::Initial => Header::Initial(InitialHeader { +128 src_cid: conn.handshake_cid, +129 dst_cid, +130 token: match &conn.side { +131 ConnectionSide::Client { token, .. } => token.clone(), +132 ConnectionSide::Server { .. } => Bytes::new(), +133 }, +134 number, +135 version, +136 }), +137 }; +138 +139 let partial_encode = header.encode(buffer); +140 if conn.peer_params.grease_quic_bit && conn.rng.random() { +141 buffer.as_mut_slice()[partial_encode.start] ^= FIXED_BIT; +142 } +143 +144 let (sample_size, tag_len) = if let Some(ref crypto) = space.crypto { +145 ( +146 crypto.header.local.sample_size(), +147 crypto.packet.local.tag_len(), +148 ) +149 } else if space_id == SpaceId::Data { +150 let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap(); +151 (zero_rtt.header.sample_size(), zero_rtt.packet.tag_len()) +152 } else { +153 unreachable!(); +154 }; +155 +156 // Each packet must be large enough for header protection sampling, i.e. the combined +157 // lengths of the encoded packet number and protected payload must be at least 4 bytes +158 // longer than the sample required for header protection. Further, each packet should be at +159 // least tag_len + 6 bytes larger than the destination CID on incoming packets so that the +160 // peer may send stateless resets that are indistinguishable from regular traffic. +161 +162 // pn_len + payload_len + tag_len >= sample_size + 4 +163 // payload_len >= sample_size + 4 - pn_len - tag_len +164 let min_size = Ord::max( +165 buffer.len() + (sample_size + 4).saturating_sub(number.len() + tag_len), +166 partial_encode.start + dst_cid.len() + 6, +167 ); +168 let max_size = buffer.datagram_max_offset() - tag_len; +169 debug_assert!(max_size >= min_size); +170 +171 qlog.header( +172 &header, +173 Some(exact_number), +174 space_id, +175 space_id == SpaceId::Data && conn.spaces[SpaceId::Data].crypto.is_none(), +176 path_id, +177 ); +178 +179 Some(Self { +180 buf: buffer, +181 space: space_id, +182 path: path_id, +183 partial_encode, +184 exact_number, +185 short_header: header.is_short(), +186 min_size, +187 tag_len, +188 ack_eliciting, +189 qlog, +190 sent_frames: SentFrames::default(), +191 _span: span, +192 }) +193 } +194 +195 #[cfg(test)] +196 pub(crate) fn simple_data_buf(buf: &'a mut TransmitBuf<'b>) -> Self { +197 Self { +198 buf, +199 space: SpaceId::Data, +200 path: PathId::ZERO, +201 partial_encode: PartialEncode::dummy(), +202 ack_eliciting: true, +203 exact_number: 0, +204 short_header: false, +205 min_size: 0, +206 tag_len: 0, +207 _span: trace_span!("test").entered(), +208 qlog: QlogSentPacket::default(), +209 sent_frames: SentFrames::default(), +210 } +211 } +212 +213 /// Append the minimum amount of padding to the packet such that, after encryption, the +214 /// enclosing datagram will occupy at least `min_size` bytes +215 pub(super) fn pad_to(&mut self, min_size: u16) { +216 // The datagram might already have a larger minimum size than the caller is requesting, if +217 // e.g. we're coalescing packets and have populated more than `min_size` bytes with packets +218 // already. +219 self.min_size = Ord::max( +220 self.min_size, +221 self.buf.datagram_start_offset() + (min_size as usize) - self.tag_len, +222 ); +223 } +224 +225 pub(super) fn write_frame<'c>( +226 &mut self, +227 frame: impl Into<EncodableFrame<'c>>, +228 stats: &mut FrameStats, +229 ) { +230 self.write_frame_with_log_msg(frame, stats, None); +231 } +232 +233 pub(super) fn write_frame_with_log_msg<'c>( +234 &mut self, +235 frame: impl Into<EncodableFrame<'c>>, +236 stats: &mut FrameStats, +237 msg: Option<&'static str>, +238 ) { +239 let frame = frame.into(); +240 frame.encode(&mut self.frame_space_mut()); +241 stats.record(frame.get_type()); +242 self.qlog.record(&frame); +243 match msg { +244 Some(msg) => trace!(%frame, msg), +245 None => trace!(%frame), +246 } +247 self.sent_frames.record_sent_frame(frame); +248 } +249 +250 /// Returns a writable buffer limited to the remaining frame space +251 /// +252 /// The [`BufMut::remaining_mut`] call on the returned buffer indicates the amount of +253 /// space available to write QUIC frames into. +254 // In rust 1.82 we can use `-> impl BufMut + use<'_, 'a, 'b>` +255 pub(super) fn frame_space_mut(&mut self) -> bytes::buf::Limit<&mut TransmitBuf<'b>> { +256 self.buf.limit(self.frame_space_remaining()) +257 } +258 +259 pub(super) fn sent_frames(&self) -> &SentFrames { +260 &self.sent_frames +261 } +262 +263 pub(super) fn finish_and_track( +264 mut self, +265 now: Instant, +266 conn: &mut Connection, +267 path_id: PathId, +268 pad_datagram: PadDatagram, +269 ) { +270 match pad_datagram { +271 PadDatagram::No => (), +272 PadDatagram::ToSize(size) => self.pad_to(size), +273 PadDatagram::ToSegmentSize => self.pad_to(self.buf.segment_size() as u16), +274 PadDatagram::ToMinMtu => self.pad_to(MIN_INITIAL_SIZE), +275 } +276 let ack_eliciting = self.ack_eliciting; +277 let exact_number = self.exact_number; +278 let space_id = self.space; +279 let (size, padded, sent) = self.finish(conn, now); +280 +281 let size = match padded || ack_eliciting { +282 true => size as u16, +283 false => 0, +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); +286 let packet = SentPacket { +287 path_generation: conn.paths.get_mut(&path_id).unwrap().data.generation(), +288 largest_acked: sent.largest_acked, +289 time_sent: now, +290 size, +291 ack_eliciting, +292 retransmits: sent.retransmits, +293 stream_frames: sent.stream_frames, 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} +296 conn.paths.get_mut(&path_id).unwrap().data.sent( +297 exact_number, +298 packet, +299 conn.spaces[space_id].for_path(path_id), +300 ); +301 conn.path_stats.entry(path_id).or_default().sent_packets += 1; +302 conn.reset_keep_alive(path_id, now); +303 if size != 0 { +304 if ack_eliciting { +305 conn.spaces[space_id] +306 .for_path(path_id) +307 .time_of_last_ack_eliciting_packet = Some(now); +308 if conn.permit_idle_reset { +309 conn.reset_idle_timeout(now, space_id, path_id); +310 } +311 conn.permit_idle_reset = false; +312 } +313 conn.set_loss_detection_timer(now, path_id); +314 conn.path_data_mut(path_id).pacing.on_transmit(size); +315 } +316 } +317 +318 /// Encrypt packet, returning the length of the packet and whether padding was added +319 pub(super) fn finish( +320 mut self, +321 conn: &mut Connection, +322 now: Instant, +323 ) -> (usize, bool, SentFrames) { +324 debug_assert!( +325 self.buf.len() <= self.buf.datagram_max_offset() - self.tag_len, +326 "packet exceeds maximum size" +327 ); +328 let pad = self.buf.len() < self.min_size; +329 if pad { +330 let padding = self.min_size - self.buf.len(); +331 trace!("PADDING * {}", padding); +332 self.buf.put_bytes(0, padding); +333 self.qlog.frame_padding(padding); +334 } +335 +336 let space = &conn.spaces[self.space]; +337 let (header_crypto, packet_crypto) = if let Some(ref crypto) = space.crypto { +338 (&*crypto.header.local, &*crypto.packet.local) +339 } else if self.space == SpaceId::Data { +340 let zero_rtt = conn.zero_rtt_crypto.as_ref().unwrap(); +341 (&*zero_rtt.header, &*zero_rtt.packet) +342 } else { +343 unreachable!("tried to send {:?} packet without keys", self.space); +344 }; +345 +346 debug_assert_eq!( +347 packet_crypto.tag_len(), +348 self.tag_len, +349 "Mismatching crypto tag len" +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 +352 self.buf.put_bytes(0, packet_crypto.tag_len()); +353 let encode_start = self.partial_encode.start; +354 let packet_buf = &mut self.buf.as_mut_slice()[encode_start..]; +355 // for packet protection, PathId::ZERO and no path are equivalent. +356 self.partial_encode.finish( +357 packet_buf, +358 header_crypto, +359 Some((self.exact_number, self.path, packet_crypto)), +360 ); +361 +362 let packet_len = self.buf.len() - encode_start; +363 trace!(size = %packet_len, short_header = %self.short_header, "wrote packet"); +364 self.qlog.finalize(packet_len); +365 conn.qlog.emit_packet_sent(self.qlog, now); +366 (packet_len, pad, self.sent_frames) +367 } +368 +369 /// The number of additional bytes the current packet would take up if it was finished now +370 /// +371 /// This will include any padding which is required to make the size large enough to be +372 /// encrypted correctly. +373 pub(super) fn predict_packet_end(&self) -> usize { +374 self.buf.len().max(self.min_size) + self.tag_len - self.buf.len() +375 } +376 +377 /// Returns the remaining space in the packet that can be taken up by QUIC frames +378 /// +379 /// This leaves space in the datagram for the cryptographic tag that needs to be written +380 /// when the packet is finished. +381 pub(super) fn frame_space_remaining(&self) -> usize { +382 let max_offset = self.buf.datagram_max_offset() - self.tag_len; +383 max_offset.saturating_sub(self.buf.len()) +384 } +385 +386 pub(crate) fn require_padding(&mut self) { +387 self.sent_frames.requires_padding = true; +388 } +389 +390 pub(crate) fn retransmits_mut(&mut self) -> &mut Retransmits { +391 self.sent_frames.retransmits_mut() +392 } +393} +394 +395#[derive(Debug, Copy, Clone)] +396pub(super) enum PadDatagram { +397 /// Do not pad the datagram +398 No, +399 /// To a specific size +400 ToSize(u16), +401 /// Pad to the current MTU/segment size +402 /// +403 /// For the first datagram in a transmit the MTU is the same as the +404 /// [`TransmitBuf::segment_size`]. +405 ToSegmentSize, +406 /// Pad to [`MIN_INITIAL_SIZE`], the minimal QUIC MTU of 1200 bytes +407 ToMinMtu, +408} +409 +410impl std::ops::BitOrAssign for PadDatagram { +411 fn bitor_assign(&mut self, rhs: Self) { +412 *self = *self | rhs; +413 } +414} +415 +416impl std::ops::BitOr for PadDatagram { +417 type Output = Self; +418 +419 fn bitor(self, rhs: Self) -> Self::Output { +420 match (self, rhs) { +421 (Self::No, rhs) => rhs, +422 (Self::ToSize(size), Self::No) => Self::ToSize(size), +423 (Self::ToSize(a), Self::ToSize(b)) => Self::ToSize(a.max(b)), +424 (Self::ToSize(_), Self::ToSegmentSize) => Self::ToSegmentSize, +425 (Self::ToSize(_), Self::ToMinMtu) => Self::ToMinMtu, +426 (Self::ToSegmentSize, Self::No) => Self::ToSegmentSize, +427 (Self::ToSegmentSize, Self::ToSize(_)) => Self::ToSegmentSize, +428 (Self::ToSegmentSize, Self::ToSegmentSize) => Self::ToSegmentSize, +429 (Self::ToSegmentSize, Self::ToMinMtu) => Self::ToMinMtu, +430 (Self::ToMinMtu, _) => Self::ToMinMtu, +431 } +432 } +433} \ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/qlog.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/qlog.rs.html index fb087bc33..baaa06b38 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/qlog.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/qlog.rs.html @@ -39,1037 +39,1212 @@ 39use crate::{ 40 Connection, ConnectionId, FourTuple, Frame, Instant, PathId, 41 connection::{PathData, SentPacket, timer::Timer}, -42 frame::{EcnCounts, StreamMeta}, +42 frame::EncodableFrame, 43 packet::{Header, SpaceId}, -44 range_set::ArrayRangeSet, -45 transport_parameters::TransportParameters, -46}; -47#[cfg(feature = "qlog")] -48use crate::{ -49 QlogConfig, Side, TransportErrorCode, -50 connection::timer::{ConnTimer, PathTimer}, -51 frame::Close, -52}; -53 -54/// Shareable handle to a single qlog output stream -55#[cfg(feature = "qlog")] -56#[derive(Clone)] -57pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>); -58 -59#[cfg(feature = "qlog")] -60impl QlogStream { -61 pub(crate) fn new( -62 config: QlogConfig, -63 initial_dst_cid: ConnectionId, -64 side: Side, -65 now: Instant, -66 ) -> Result<Self, qlog::Error> { -67 let vantage_point = VantagePoint { -68 name: None, -69 ty: match side { -70 Side::Client => qlog::VantagePointType::Client, -71 Side::Server => qlog::VantagePointType::Server, -72 }, -73 flow: None, -74 }; -75 -76 let common_fields = CommonFields { -77 group_id: Some(initial_dst_cid.to_string()), -78 ..Default::default() -79 }; -80 -81 let trace = qlog::TraceSeq::new( -82 config.title.clone(), -83 config.description.clone(), -84 Some(common_fields), -85 Some(vantage_point), -86 vec![], -87 ); -88 -89 let start_time = config.start_time.unwrap_or(now); -90 -91 let mut streamer = QlogStreamer::new( -92 config.title, -93 config.description, -94 start_time, -95 trace, -96 qlog::events::EventImportance::Extra, -97 config.writer, -98 ); -99 -100 streamer.start_log()?; -101 Ok(Self(Arc::new(Mutex::new(streamer)))) -102 } -103 -104 fn emit_event(&self, event: EventData, now: Instant) { -105 self.emit_event_with_tuple_id(event, now, None); -106 } -107 -108 fn emit_event_with_tuple_id( -109 &self, -110 event: EventData, -111 now: Instant, -112 network_path: Option<String>, -113 ) { -114 // Time will be overwritten by `add_event_with_instant` -115 let mut event = Event::with_time(0.0, event); -116 event.tuple = network_path; -117 let mut qlog_streamer = self.0.lock().unwrap(); -118 if let Err(e) = qlog_streamer.add_event_with_instant(event, now) { -119 warn!("could not emit qlog event: {e}"); -120 } -121 } -122} -123 -124/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely -125#[derive(Clone, Default)] -126pub(crate) struct QlogSink { -127 #[cfg(feature = "qlog")] -128 stream: Option<QlogStream>, -129} -130 -131impl QlogSink { -132 #[cfg(feature = "qlog")] -133 pub(crate) fn new( -134 config: QlogConfig, -135 initial_dst_cid: ConnectionId, -136 side: Side, -137 now: Instant, -138 ) -> Self { -139 let stream = QlogStream::new(config, initial_dst_cid, side, now) -140 .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}")) -141 .ok(); -142 Self { stream } -143 } -144 -145 pub(crate) fn emit_connection_started( -146 &self, -147 now: Instant, -148 loc_cid: ConnectionId, -149 rem_cid: ConnectionId, -150 remote: SocketAddr, -151 local_ip: Option<IpAddr>, -152 transport_params: &TransportParameters, -153 ) { -154 #[cfg(feature = "qlog")] -155 { -156 let Some(stream) = self.stream.as_ref() else { -157 return; -158 }; -159 stream.emit_event( -160 EventData::ConnectionStarted(ConnectionStarted { -161 local: tuple_endpoint_info(local_ip, None, Some(loc_cid)), -162 remote: tuple_endpoint_info( -163 Some(remote.ip()), -164 Some(remote.port()), -165 Some(rem_cid), -166 ), -167 }), -168 now, -169 ); -170 -171 let params = transport_params.to_qlog(TransportInitiator::Local); -172 let event = EventData::ParametersSet(params); -173 stream.emit_event(event, now); -174 } -175 } -176 -177 pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) { -178 #[cfg(feature = "qlog")] -179 { -180 let Some(stream) = self.stream.as_ref() else { -181 return; -182 }; -183 -184 let Some(metrics) = path.qlog_recovery_metrics(path_id) else { -185 return; -186 }; -187 -188 stream.emit_event(EventData::MetricsUpdated(metrics), now); -189 } -190 } -191 -192 pub(super) fn emit_packet_lost( -193 &self, -194 pn: u64, -195 info: &SentPacket, -196 loss_delay: Duration, -197 space: SpaceId, -198 now: Instant, -199 ) { -200 #[cfg(feature = "qlog")] -201 { -202 let Some(stream) = self.stream.as_ref() else { -203 return; -204 }; -205 -206 let event = PacketLost { -207 header: Some(PacketHeader { -208 packet_number: Some(pn), -209 packet_type: packet_type(space, false), -210 length: Some(info.size), -211 ..Default::default() -212 }), -213 frames: None, -214 trigger: Some( -215 match info.time_sent.saturating_duration_since(now) >= loss_delay { -216 true => PacketLostTrigger::TimeThreshold, -217 false => PacketLostTrigger::ReorderingThreshold, -218 }, -219 ), -220 is_mtu_probe_packet: None, -221 }; -222 -223 stream.emit_event(EventData::PacketLost(event), now); -224 } -225 } -226 -227 pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) { -228 #[cfg(feature = "qlog")] -229 { -230 let Some(stream) = self.stream.as_ref() else { -231 return; -232 }; -233 let params = conn.peer_params.to_qlog_restored(); -234 let event = EventData::ParametersRestored(params); -235 stream.emit_event(event, now); -236 } -237 } -238 -239 pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) { -240 #[cfg(feature = "qlog")] -241 { -242 let Some(stream) = self.stream.as_ref() else { -243 return; -244 }; -245 let params = conn.peer_params.to_qlog(TransportInitiator::Remote); -246 let event = EventData::ParametersSet(params); -247 stream.emit_event(event, now); -248 } -249 } -250 -251 pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) { -252 #[cfg(feature = "qlog")] -253 { -254 let Some(stream) = self.stream.as_ref() else { -255 return; -256 }; -257 let tuple_id = fmt_tuple_id(path_id.as_u32() as u64); -258 let event = TupleAssigned { -259 tuple_id, -260 tuple_local: tuple -261 .local_ip -262 .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)), -263 tuple_remote: Some(tuple_endpoint_info( -264 Some(tuple.remote.ip()), -265 Some(tuple.remote.port()), -266 None, -267 )), -268 }; -269 -270 stream.emit_event(EventData::TupleAssigned(event), now); -271 } -272 } -273 -274 pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) { -275 #[cfg(feature = "qlog")] -276 { -277 let Some(stream) = self.stream.as_ref() else { -278 return; -279 }; -280 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id); -281 stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id); -282 } -283 } -284 -285 pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) { -286 #[cfg(feature = "qlog")] -287 { -288 let Some(stream) = self.stream.as_ref() else { -289 return; -290 }; -291 let mut packet = packet; -292 packet.emit_padding(); -293 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id); -294 let event = packet.inner; -295 stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id); -296 } -297 } -298 -299 /// Emits a timer event. -300 /// -301 /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use -302 /// its `emit_timer_` methods. -303 #[cfg(feature = "qlog")] -304 fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) { -305 let Some(stream) = self.stream.as_ref() else { -306 return; -307 }; -308 -309 let timer_type: Option<TimerType> = match timer { -310 Timer::Conn(conn_timer) => match conn_timer { -311 ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()), -312 ConnTimer::Close => Some(TimerType::custom("close")), -313 ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")), -314 ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")), -315 ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")), -316 }, -317 Timer::PerPath(_, path_timer) => match path_timer { -318 PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()), -319 PathTimer::PathIdle => Some(TimerType::custom("path_idle")), -320 PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()), -321 PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")), -322 PathTimer::PathOpen => Some(TimerType::custom("path_open")), -323 PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")), -324 PathTimer::Pacing => Some(TimerType::custom("pacing")), -325 PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()), -326 PathTimer::DiscardPath => Some(TimerType::custom("discard_path")), -327 }, -328 }; -329 -330 let Some(timer_type) = timer_type else { -331 return; -332 }; -333 -334 let delta = match op { -335 TimerOp::Set(instant) => instant -336 .checked_duration_since(now) -337 .map(|dur| dur.as_secs_f32() * 1000.), -338 _ => None, -339 }; -340 let path_id = match timer { -341 Timer::Conn(_) => None, -342 Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64), -343 }; -344 -345 let event_type = match op { -346 TimerOp::Set(_) => TimerEventType::Set, -347 TimerOp::Expire => TimerEventType::Expired, -348 TimerOp::Cancelled => TimerEventType::Cancelled, -349 }; -350 -351 let event = TimerUpdated { -352 path_id, -353 timer_type: Some(timer_type), -354 timer_id: None, -355 packet_number_space: None, -356 event_type, -357 delta, -358 }; -359 stream.emit_event(EventData::TimerUpdated(event), now); -360 } -361 -362 /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp. -363 /// -364 /// This may be used if you want to pass a [`QlogSink`] downwards together with the current -365 /// `now` timestamp, to not have to pass the latter separately as an additional argument just -366 /// for qlog support. -367 pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> { -368 #[cfg(feature = "qlog")] -369 let s = QlogSinkWithTime { sink: self, now }; -370 #[cfg(not(feature = "qlog"))] -371 let s = QlogSinkWithTime { -372 _phantom: PhantomData, -373 }; -374 s -375 } -376} -377 -378/// A [`QlogSink`] with a `now` timestamp. -379#[derive(Clone)] -380pub(super) struct QlogSinkWithTime<'a> { -381 #[cfg(feature = "qlog")] -382 sink: &'a QlogSink, -383 #[cfg(feature = "qlog")] -384 now: Instant, -385 #[cfg(not(feature = "qlog"))] -386 _phantom: PhantomData<&'a ()>, -387} -388 -389impl<'a> QlogSinkWithTime<'a> { -390 pub(super) fn emit_timer_stop(&self, timer: Timer) { -391 #[cfg(feature = "qlog")] -392 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now) -393 } -394 -395 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) { -396 #[cfg(feature = "qlog")] -397 self.sink -398 .emit_timer(timer, TimerOp::Set(expire_at), self.now) -399 } -400 -401 pub(super) fn emit_timer_expire(&self, timer: Timer) { -402 #[cfg(feature = "qlog")] -403 self.sink.emit_timer(timer, TimerOp::Expire, self.now) -404 } -405} -406 -407#[cfg(feature = "qlog")] -408enum TimerOp { -409 Set(Instant), -410 Expire, -411 Cancelled, -412} -413 -414/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled. -415#[derive(Default)] -416pub(crate) struct QlogSentPacket { -417 #[cfg(feature = "qlog")] -418 inner: PacketSent, -419} -420 -421impl QlogSentPacket { -422 /// Sets data from the packet header. -423 pub(crate) fn header( -424 &mut self, -425 header: &Header, -426 pn: Option<u64>, -427 space: SpaceId, -428 is_0rtt: bool, -429 path_id: PathId, -430 ) { -431 #[cfg(feature = "qlog")] -432 { -433 self.inner.header.scid = header.src_cid().map(stringify_cid); -434 self.inner.header.dcid = Some(stringify_cid(header.dst_cid())); -435 self.inner.header.packet_number = pn; -436 self.inner.header.packet_type = packet_type(space, is_0rtt); -437 self.inner.header.path_id = Some(path_id.as_u32() as u64); -438 } -439 } -440 -441 /// Adds a frame by pushing a [`Frame`]. -442 pub(crate) fn frame(&mut self, frame: &Frame) { -443 #[cfg(feature = "qlog")] -444 self.frame_raw(frame.to_qlog()) -445 } -446 -447 /// Adds a PADDING frame. -448 /// -449 /// This is a no-op if the `qlog` feature is not enabled. -450 pub(crate) fn frame_padding(&mut self, count: usize) { -451 #[cfg(feature = "qlog")] -452 self.frame_raw(QuicFrame::Padding { -453 raw: Some(RawInfo { -454 length: Some(count as u64), -455 payload_length: Some(count as u64), -456 data: None, -457 }), -458 }); -459 } -460 -461 /// Adds an ACK frame. -462 /// -463 /// This is a no-op if the `qlog` feature is not enabled. -464 pub(crate) fn frame_ack( -465 &mut self, -466 delay: u64, -467 ranges: &ArrayRangeSet, -468 ecn: Option<&EcnCounts>, -469 ) { -470 #[cfg(feature = "qlog")] -471 self.frame_raw(QuicFrame::Ack { -472 ack_delay: Some(delay as f32), -473 acked_ranges: Some(AckedRanges::Double( -474 ranges -475 .iter() -476 .map(|range| (range.start, range.end)) -477 .collect(), -478 )), -479 ect1: ecn.map(|e| e.ect1), -480 ect0: ecn.map(|e| e.ect0), -481 ce: ecn.map(|e| e.ce), -482 raw: None, -483 }); -484 } -485 -486 /// Adds a PATH_ACK frame. +44 transport_parameters::TransportParameters, +45}; +46#[cfg(feature = "qlog")] +47use crate::{ +48 QlogConfig, Side, TransportErrorCode, +49 connection::timer::{ConnTimer, PathTimer}, +50 frame, +51}; +52 +53/// Shareable handle to a single qlog output stream +54#[cfg(feature = "qlog")] +55#[derive(Clone)] +56pub(crate) struct QlogStream(Arc<Mutex<QlogStreamer>>); +57 +58#[cfg(feature = "qlog")] +59impl QlogStream { +60 pub(crate) fn new( +61 config: QlogConfig, +62 initial_dst_cid: ConnectionId, +63 side: Side, +64 now: Instant, +65 ) -> Result<Self, qlog::Error> { +66 let vantage_point = VantagePoint { +67 name: None, +68 ty: match side { +69 Side::Client => qlog::VantagePointType::Client, +70 Side::Server => qlog::VantagePointType::Server, +71 }, +72 flow: None, +73 }; +74 +75 let common_fields = CommonFields { +76 group_id: Some(initial_dst_cid.to_string()), +77 ..Default::default() +78 }; +79 +80 let trace = qlog::TraceSeq::new( +81 config.title.clone(), +82 config.description.clone(), +83 Some(common_fields), +84 Some(vantage_point), +85 vec![], +86 ); +87 +88 let start_time = config.start_time.unwrap_or(now); +89 +90 let mut streamer = QlogStreamer::new( +91 config.title, +92 config.description, +93 start_time, +94 trace, +95 qlog::events::EventImportance::Extra, +96 config.writer, +97 ); +98 +99 streamer.start_log()?; +100 Ok(Self(Arc::new(Mutex::new(streamer)))) +101 } +102 +103 fn emit_event(&self, event: EventData, now: Instant) { +104 self.emit_event_with_tuple_id(event, now, None); +105 } +106 +107 fn emit_event_with_tuple_id( +108 &self, +109 event: EventData, +110 now: Instant, +111 network_path: Option<String>, +112 ) { +113 // Time will be overwritten by `add_event_with_instant` +114 let mut event = Event::with_time(0.0, event); +115 event.tuple = network_path; +116 let mut qlog_streamer = self.0.lock().unwrap(); +117 if let Err(e) = qlog_streamer.add_event_with_instant(event, now) { +118 warn!("could not emit qlog event: {e}"); +119 } +120 } +121} +122 +123/// A [`QlogStream`] that may be either dynamically disabled or compiled out entirely +124#[derive(Clone, Default)] +125pub(crate) struct QlogSink { +126 #[cfg(feature = "qlog")] +127 stream: Option<QlogStream>, +128} +129 +130impl QlogSink { +131 #[cfg(feature = "qlog")] +132 pub(crate) fn new( +133 config: QlogConfig, +134 initial_dst_cid: ConnectionId, +135 side: Side, +136 now: Instant, +137 ) -> Self { +138 let stream = QlogStream::new(config, initial_dst_cid, side, now) +139 .inspect_err(|err| warn!("failed to initialize qlog streamer: {err}")) +140 .ok(); +141 Self { stream } +142 } +143 +144 pub(crate) fn emit_connection_started( +145 &self, +146 now: Instant, +147 loc_cid: ConnectionId, +148 rem_cid: ConnectionId, +149 remote: SocketAddr, +150 local_ip: Option<IpAddr>, +151 transport_params: &TransportParameters, +152 ) { +153 #[cfg(feature = "qlog")] +154 { +155 let Some(stream) = self.stream.as_ref() else { +156 return; +157 }; +158 stream.emit_event( +159 EventData::ConnectionStarted(ConnectionStarted { +160 local: tuple_endpoint_info(local_ip, None, Some(loc_cid)), +161 remote: tuple_endpoint_info( +162 Some(remote.ip()), +163 Some(remote.port()), +164 Some(rem_cid), +165 ), +166 }), +167 now, +168 ); +169 +170 let params = transport_params.to_qlog(TransportInitiator::Local); +171 let event = EventData::ParametersSet(params); +172 stream.emit_event(event, now); +173 } +174 } +175 +176 pub(super) fn emit_recovery_metrics(&self, path_id: PathId, path: &mut PathData, now: Instant) { +177 #[cfg(feature = "qlog")] +178 { +179 let Some(stream) = self.stream.as_ref() else { +180 return; +181 }; +182 +183 let Some(metrics) = path.qlog_recovery_metrics(path_id) else { +184 return; +185 }; +186 +187 stream.emit_event(EventData::MetricsUpdated(metrics), now); +188 } +189 } +190 +191 pub(super) fn emit_packet_lost( +192 &self, +193 pn: u64, +194 info: &SentPacket, +195 loss_delay: Duration, +196 space: SpaceId, +197 now: Instant, +198 ) { +199 #[cfg(feature = "qlog")] +200 { +201 let Some(stream) = self.stream.as_ref() else { +202 return; +203 }; +204 +205 let event = PacketLost { +206 header: Some(PacketHeader { +207 packet_number: Some(pn), +208 packet_type: packet_type(space, false), +209 length: Some(info.size), +210 ..Default::default() +211 }), +212 frames: None, +213 trigger: Some( +214 match info.time_sent.saturating_duration_since(now) >= loss_delay { +215 true => PacketLostTrigger::TimeThreshold, +216 false => PacketLostTrigger::ReorderingThreshold, +217 }, +218 ), +219 is_mtu_probe_packet: None, +220 }; +221 +222 stream.emit_event(EventData::PacketLost(event), now); +223 } +224 } +225 +226 pub(super) fn emit_peer_transport_params_restored(&self, conn: &Connection, now: Instant) { +227 #[cfg(feature = "qlog")] +228 { +229 let Some(stream) = self.stream.as_ref() else { +230 return; +231 }; +232 let params = conn.peer_params.to_qlog_restored(); +233 let event = EventData::ParametersRestored(params); +234 stream.emit_event(event, now); +235 } +236 } +237 +238 pub(super) fn emit_peer_transport_params_received(&self, conn: &Connection, now: Instant) { +239 #[cfg(feature = "qlog")] +240 { +241 let Some(stream) = self.stream.as_ref() else { +242 return; +243 }; +244 let params = conn.peer_params.to_qlog(TransportInitiator::Remote); +245 let event = EventData::ParametersSet(params); +246 stream.emit_event(event, now); +247 } +248 } +249 +250 pub(super) fn emit_tuple_assigned(&self, path_id: PathId, tuple: FourTuple, now: Instant) { +251 #[cfg(feature = "qlog")] +252 { +253 let Some(stream) = self.stream.as_ref() else { +254 return; +255 }; +256 let tuple_id = fmt_tuple_id(path_id.as_u32() as u64); +257 let event = TupleAssigned { +258 tuple_id, +259 tuple_local: tuple +260 .local_ip +261 .map(|local_ip| tuple_endpoint_info(Some(local_ip), None, None)), +262 tuple_remote: Some(tuple_endpoint_info( +263 Some(tuple.remote.ip()), +264 Some(tuple.remote.port()), +265 None, +266 )), +267 }; +268 +269 stream.emit_event(EventData::TupleAssigned(event), now); +270 } +271 } +272 +273 pub(super) fn emit_packet_sent(&self, packet: QlogSentPacket, now: Instant) { +274 #[cfg(feature = "qlog")] +275 { +276 let Some(stream) = self.stream.as_ref() else { +277 return; +278 }; +279 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id); +280 stream.emit_event_with_tuple_id(EventData::PacketSent(packet.inner), now, tuple_id); +281 } +282 } +283 +284 pub(super) fn emit_packet_received(&self, packet: QlogRecvPacket, now: Instant) { +285 #[cfg(feature = "qlog")] +286 { +287 let Some(stream) = self.stream.as_ref() else { +288 return; +289 }; +290 let mut packet = packet; +291 packet.emit_padding(); +292 let tuple_id = packet.inner.header.path_id.map(fmt_tuple_id); +293 let event = packet.inner; +294 stream.emit_event_with_tuple_id(EventData::PacketReceived(event), now, tuple_id); +295 } +296 } +297 +298 /// Emits a timer event. +299 /// +300 /// This function is not public: Instead, create a [`QlogSinkWithTime`] via [`Self::with_time`] and use +301 /// its `emit_timer_` methods. +302 #[cfg(feature = "qlog")] +303 fn emit_timer(&self, timer: Timer, op: TimerOp, now: Instant) { +304 let Some(stream) = self.stream.as_ref() else { +305 return; +306 }; +307 +308 let timer_type: Option<TimerType> = match timer { +309 Timer::Conn(conn_timer) => match conn_timer { +310 ConnTimer::Idle => Some(QlogTimerType::IdleTimeout.into()), +311 ConnTimer::Close => Some(TimerType::custom("close")), +312 ConnTimer::KeyDiscard => Some(TimerType::custom("key_discard")), +313 ConnTimer::KeepAlive => Some(TimerType::custom("keep_alive")), +314 ConnTimer::PushNewCid => Some(TimerType::custom("push_new_cid")), +315 }, +316 Timer::PerPath(_, path_timer) => match path_timer { +317 PathTimer::LossDetection => Some(QlogTimerType::LossTimeout.into()), +318 PathTimer::PathIdle => Some(TimerType::custom("path_idle")), +319 PathTimer::PathValidation => Some(QlogTimerType::PathValidation.into()), +320 PathTimer::PathChallengeLost => Some(TimerType::custom("path_challenge_lost")), +321 PathTimer::PathOpen => Some(TimerType::custom("path_open")), +322 PathTimer::PathKeepAlive => Some(TimerType::custom("path_keep_alive")), +323 PathTimer::Pacing => Some(TimerType::custom("pacing")), +324 PathTimer::MaxAckDelay => Some(QlogTimerType::Ack.into()), +325 PathTimer::DiscardPath => Some(TimerType::custom("discard_path")), +326 }, +327 }; +328 +329 let Some(timer_type) = timer_type else { +330 return; +331 }; +332 +333 let delta = match op { +334 TimerOp::Set(instant) => instant +335 .checked_duration_since(now) +336 .map(|dur| dur.as_secs_f32() * 1000.), +337 _ => None, +338 }; +339 let path_id = match timer { +340 Timer::Conn(_) => None, +341 Timer::PerPath(path_id, _) => Some(path_id.as_u32() as u64), +342 }; +343 +344 let event_type = match op { +345 TimerOp::Set(_) => TimerEventType::Set, +346 TimerOp::Expire => TimerEventType::Expired, +347 TimerOp::Cancelled => TimerEventType::Cancelled, +348 }; +349 +350 let event = TimerUpdated { +351 path_id, +352 timer_type: Some(timer_type), +353 timer_id: None, +354 packet_number_space: None, +355 event_type, +356 delta, +357 }; +358 stream.emit_event(EventData::TimerUpdated(event), now); +359 } +360 +361 /// Returns a [`QlogSinkWithTime`] that passes along a `now` timestamp. +362 /// +363 /// This may be used if you want to pass a [`QlogSink`] downwards together with the current +364 /// `now` timestamp, to not have to pass the latter separately as an additional argument just +365 /// for qlog support. +366 pub(super) fn with_time(&self, now: Instant) -> QlogSinkWithTime<'_> { +367 #[cfg(feature = "qlog")] +368 let s = QlogSinkWithTime { sink: self, now }; +369 #[cfg(not(feature = "qlog"))] +370 let s = QlogSinkWithTime { +371 _phantom: PhantomData, +372 }; +373 s +374 } +375} +376 +377/// A [`QlogSink`] with a `now` timestamp. +378#[derive(Clone)] +379pub(super) struct QlogSinkWithTime<'a> { +380 #[cfg(feature = "qlog")] +381 sink: &'a QlogSink, +382 #[cfg(feature = "qlog")] +383 now: Instant, +384 #[cfg(not(feature = "qlog"))] +385 _phantom: PhantomData<&'a ()>, +386} +387 +388impl<'a> QlogSinkWithTime<'a> { +389 pub(super) fn emit_timer_stop(&self, timer: Timer) { +390 #[cfg(feature = "qlog")] +391 self.sink.emit_timer(timer, TimerOp::Cancelled, self.now) +392 } +393 +394 pub(super) fn emit_timer_set(&self, timer: Timer, expire_at: Instant) { +395 #[cfg(feature = "qlog")] +396 self.sink +397 .emit_timer(timer, TimerOp::Set(expire_at), self.now) +398 } +399 +400 pub(super) fn emit_timer_expire(&self, timer: Timer) { +401 #[cfg(feature = "qlog")] +402 self.sink.emit_timer(timer, TimerOp::Expire, self.now) +403 } +404} +405 +406#[cfg(feature = "qlog")] +407enum TimerOp { +408 Set(Instant), +409 Expire, +410 Cancelled, +411} +412 +413/// Info about a sent packet. Zero-sized struct if `qlog` feature is not enabled. +414#[derive(Default)] +415pub(crate) struct QlogSentPacket { +416 #[cfg(feature = "qlog")] +417 inner: PacketSent, +418} +419 +420impl QlogSentPacket { +421 /// Sets data from the packet header. +422 pub(crate) fn header( +423 &mut self, +424 header: &Header, +425 pn: Option<u64>, +426 space: SpaceId, +427 is_0rtt: bool, +428 path_id: PathId, +429 ) { +430 #[cfg(feature = "qlog")] +431 { +432 self.inner.header.scid = header.src_cid().map(stringify_cid); +433 self.inner.header.dcid = Some(stringify_cid(header.dst_cid())); +434 self.inner.header.packet_number = pn; +435 self.inner.header.packet_type = packet_type(space, is_0rtt); +436 self.inner.header.path_id = Some(path_id.as_u32() as u64); +437 } +438 } +439 +440 /// Adds a PADDING frame. +441 /// +442 /// This is a no-op if the `qlog` feature is not enabled. +443 pub(crate) fn frame_padding(&mut self, count: usize) { +444 #[cfg(feature = "qlog")] +445 self.frame_raw(QuicFrame::Padding { +446 raw: Some(RawInfo { +447 length: Some(count as u64), +448 payload_length: Some(count as u64), +449 data: None, +450 }), +451 }); +452 } +453 +454 /// Adds a frame by pushing a [`QuicFrame`]. +455 /// +456 /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve +457 /// calculations which shouldn't be performed if the `qlog` feature is disabled. +458 #[cfg(feature = "qlog")] +459 fn frame_raw(&mut self, frame: QuicFrame) { +460 self.inner.frames.get_or_insert_default().push(frame); +461 } +462 +463 /// Finalizes the packet by setting the final packet length (after encryption). +464 pub(super) fn finalize(&mut self, len: usize) { +465 #[cfg(feature = "qlog")] +466 { +467 self.inner.header.length = Some(len as u16); +468 } +469 } +470 +471 pub(crate) fn record<'a>(&mut self, frame: &EncodableFrame<'a>) { +472 #[cfg(feature = "qlog")] +473 self.frame_raw(frame.to_qlog()); +474 } +475} +476 +477/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled. +478pub(crate) struct QlogRecvPacket { +479 #[cfg(feature = "qlog")] +480 inner: PacketReceived, +481 #[cfg(feature = "qlog")] +482 padding: usize, +483} +484 +485impl QlogRecvPacket { +486 /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled. 487 /// -488 /// This is a no-op if the `qlog` feature is not enabled. -489 pub(crate) fn frame_path_ack( -490 &mut self, -491 path_id: PathId, -492 delay: u64, -493 ranges: &ArrayRangeSet, -494 ecn: Option<&EcnCounts>, -495 ) { -496 #[cfg(feature = "qlog")] -497 self.frame_raw(QuicFrame::PathAck { -498 path_id: path_id.as_u32() as u64, -499 ack_delay: Some(delay as f32), -500 acked_ranges: Some(AckedRanges::Double( -501 ranges -502 .iter() -503 .map(|range| (range.start, range.end)) -504 .collect(), -505 )), -506 ect1: ecn.map(|e| e.ect1), -507 ect0: ecn.map(|e| e.ect0), -508 ce: ecn.map(|e| e.ce), -509 raw: None, -510 }); -511 } -512 -513 /// Adds a DATAGRAM frame. -514 /// -515 /// This is a no-op if the `qlog` feature is not enabled. -516 pub(crate) fn frame_datagram(&mut self, len: u64) { -517 #[cfg(feature = "qlog")] -518 self.frame_raw(QuicFrame::Datagram { -519 raw: Some(RawInfo { -520 length: Some(len), -521 ..Default::default() -522 }), -523 }); -524 } -525 -526 /// Adds a STREAM frame. -527 /// -528 /// This is a no-op if the `qlog` feature is not enabled. -529 pub(crate) fn frame_stream(&mut self, meta: &StreamMeta) { -530 #[cfg(feature = "qlog")] -531 self.frame_raw(QuicFrame::Stream { -532 stream_id: meta.id.into(), -533 offset: Some(meta.offsets.start), -534 fin: Some(meta.fin), -535 raw: Some(RawInfo { -536 length: Some(meta.offsets.end - meta.offsets.start), -537 ..Default::default() -538 }), -539 }); -540 } -541 -542 /// Adds a frame by pushing a [`QuicFrame`]. -543 /// -544 /// This function is only available if the `qlog` feature is enabled, because constructing a [`QuicFrame`] may involve -545 /// calculations which shouldn't be performed if the `qlog` feature is disabled. -546 #[cfg(feature = "qlog")] -547 fn frame_raw(&mut self, frame: QuicFrame) { -548 self.inner.frames.get_or_insert_default().push(frame); -549 } -550 -551 /// Finalizes the packet by setting the final packet length (after encryption). -552 pub(super) fn finalize(&mut self, len: usize) { -553 #[cfg(feature = "qlog")] -554 { -555 self.inner.header.length = Some(len as u16); -556 } -557 } -558} -559 -560/// Info about a received packet. Zero-sized struct if `qlog` feature is not enabled. -561pub(crate) struct QlogRecvPacket { -562 #[cfg(feature = "qlog")] -563 inner: PacketReceived, -564 #[cfg(feature = "qlog")] -565 padding: usize, -566} -567 -568impl QlogRecvPacket { -569 /// Creates a new [`QlogRecvPacket`]. Noop if `qlog` feature is not enabled. -570 /// -571 /// `len` is the packet's full length (before decryption). -572 pub(crate) fn new(len: usize) -> Self { -573 #[cfg(not(feature = "qlog"))] -574 let this = Self {}; -575 -576 #[cfg(feature = "qlog")] -577 let this = { -578 let mut this = Self { -579 inner: Default::default(), -580 padding: 0, -581 }; -582 this.inner.header.length = Some(len as u16); -583 this -584 }; -585 -586 this -587 } -588 -589 /// Adds info from the packet header. -590 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) { -591 #[cfg(feature = "qlog")] -592 { -593 let is_0rtt = !header.is_1rtt(); -594 self.inner.header.scid = header.src_cid().map(stringify_cid); -595 self.inner.header.dcid = Some(stringify_cid(header.dst_cid())); -596 self.inner.header.packet_number = pn; -597 self.inner.header.packet_type = packet_type(header.space(), is_0rtt); -598 self.inner.header.path_id = Some(path_id.as_u32() as u64); -599 } -600 } -601 -602 /// Adds a frame. -603 pub(crate) fn frame(&mut self, frame: &Frame) { -604 #[cfg(feature = "qlog")] -605 { -606 if matches!(frame, crate::Frame::Padding) { -607 self.padding += 1; -608 } else { -609 self.emit_padding(); -610 self.inner -611 .frames -612 .get_or_insert_default() -613 .push(frame.to_qlog()) -614 } -615 } -616 } -617 -618 #[cfg(feature = "qlog")] -619 fn emit_padding(&mut self) { -620 if self.padding > 0 { -621 self.inner -622 .frames -623 .get_or_insert_default() -624 .push(QuicFrame::Padding { -625 raw: Some(RawInfo { -626 length: Some(self.padding as u64), -627 payload_length: Some(self.padding as u64), -628 data: None, -629 }), -630 }); -631 self.padding = 0; -632 } -633 } -634} -635 -636#[cfg(feature = "qlog")] -637impl Frame { -638 /// Converts a [`crate::Frame`] into a [`QuicFrame`]. -639 pub(crate) fn to_qlog(&self) -> QuicFrame { -640 match self { -641 Self::Padding => QuicFrame::Padding { -642 raw: Some(RawInfo { -643 length: None, -644 payload_length: Some(1), -645 data: None, -646 }), -647 }, -648 Self::Ping => QuicFrame::Ping { raw: None }, -649 Self::Ack(f) => QuicFrame::Ack { -650 ack_delay: Some(f.delay as f32), -651 acked_ranges: Some(AckedRanges::Double( -652 f.iter() -653 .map(|range| (*range.start(), *range.end())) -654 .collect(), -655 )), -656 ect1: f.ecn.as_ref().map(|e| e.ect1), -657 ect0: f.ecn.as_ref().map(|e| e.ect0), -658 ce: f.ecn.as_ref().map(|e| e.ce), -659 raw: None, -660 }, -661 Self::ResetStream(f) => QuicFrame::ResetStream { -662 stream_id: f.id.into(), -663 error_code: Some(f.error_code.into_inner()), -664 final_size: f.final_offset.into(), -665 error: ApplicationError::Unknown, -666 raw: None, -667 }, -668 Self::StopSending(f) => QuicFrame::StopSending { -669 stream_id: f.id.into(), -670 error_code: Some(f.error_code.into_inner()), -671 error: ApplicationError::Unknown, -672 raw: None, -673 }, -674 Self::Crypto(f) => QuicFrame::Crypto { -675 offset: f.offset, -676 raw: Some(RawInfo { -677 length: Some(f.data.len() as u64), -678 ..Default::default() -679 }), -680 }, -681 Self::NewToken(f) => QuicFrame::NewToken { -682 token: qlog::Token { -683 ty: Some(TokenType::Retry), -684 raw: Some(RawInfo { -685 data: HexSlice::maybe_string(Some(&f.token)), -686 length: Some(f.token.len() as u64), -687 payload_length: None, -688 }), -689 details: None, -690 }, -691 raw: None, -692 }, -693 Self::Stream(s) => QuicFrame::Stream { -694 stream_id: s.id.into(), -695 offset: Some(s.offset), -696 fin: Some(s.fin), -697 raw: Some(RawInfo { -698 length: Some(s.data.len() as u64), -699 ..Default::default() -700 }), -701 }, -702 Self::MaxData(v) => QuicFrame::MaxData { -703 maximum: (*v).into(), -704 raw: None, -705 }, -706 Self::MaxStreamData { id, offset } => QuicFrame::MaxStreamData { -707 stream_id: (*id).into(), -708 maximum: *offset, -709 raw: None, -710 }, -711 Self::MaxStreams { dir, count } => QuicFrame::MaxStreams { -712 maximum: *count, -713 stream_type: (*dir).into(), -714 raw: None, -715 }, -716 Self::DataBlocked { offset } => QuicFrame::DataBlocked { -717 limit: *offset, -718 raw: None, -719 }, -720 Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked { -721 stream_id: (*id).into(), -722 limit: *offset, -723 raw: None, -724 }, -725 Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked { -726 stream_type: (*dir).into(), -727 limit: *limit, -728 raw: None, -729 }, -730 Self::NewConnectionId(f) => QuicFrame::NewConnectionId { -731 sequence_number: f.sequence, -732 retire_prior_to: f.retire_prior_to, -733 connection_id_length: Some(f.id.len() as u8), -734 connection_id: f.id.to_string(), -735 stateless_reset_token: Some(f.reset_token.to_string()), -736 raw: None, -737 }, -738 Self::RetireConnectionId(f) => QuicFrame::RetireConnectionId { -739 sequence_number: f.sequence, -740 raw: None, -741 }, -742 Self::PathChallenge(token) => QuicFrame::PathChallenge { -743 data: Some(token.to_string()), -744 raw: None, -745 }, -746 Self::PathResponse(token) => QuicFrame::PathResponse { -747 data: Some(token.to_string()), -748 raw: None, -749 }, -750 Self::Close(close) => match close { -751 Close::Connection(f) => { -752 let (error, error_code) = transport_error(f.error_code); -753 let error = error.map(|transport_error| { -754 ConnectionClosedFrameError::TransportError(transport_error) -755 }); -756 QuicFrame::ConnectionClose { -757 error_space: Some(ErrorSpace::TransportError), -758 error, -759 error_code, -760 reason: String::from_utf8(f.reason.to_vec()).ok(), -761 reason_bytes: None, -762 trigger_frame_type: None, -763 } -764 } -765 Close::Application(f) => QuicFrame::ConnectionClose { -766 error_space: Some(ErrorSpace::ApplicationError), -767 error: None, -768 error_code: Some(f.error_code.into_inner()), -769 reason: String::from_utf8(f.reason.to_vec()).ok(), -770 reason_bytes: None, -771 trigger_frame_type: None, -772 }, -773 }, -774 Self::Datagram(d) => QuicFrame::Datagram { -775 raw: Some(RawInfo { -776 length: Some(d.data.len() as u64), -777 ..Default::default() -778 }), -779 }, -780 Self::HandshakeDone => QuicFrame::HandshakeDone { raw: None }, -781 Self::PathAck(ack) => QuicFrame::PathAck { -782 path_id: ack.path_id.as_u32().into(), -783 ack_delay: Some(ack.delay as f32), -784 ect1: ack.ecn.as_ref().map(|e| e.ect1), -785 ect0: ack.ecn.as_ref().map(|e| e.ect0), -786 ce: ack.ecn.as_ref().map(|e| e.ce), -787 raw: None, -788 acked_ranges: Some(AckedRanges::Double( -789 ack.into_iter() -790 .map(|range| (*range.start(), *range.end())) -791 .collect(), -792 )), -793 }, -794 Self::PathAbandon(frame) => QuicFrame::PathAbandon { -795 path_id: frame.path_id.as_u32().into(), -796 error_code: frame.error_code.into(), -797 raw: None, -798 }, -799 Self::PathStatusAvailable(frame) => QuicFrame::PathStatusAvailable { -800 path_id: frame.path_id.as_u32().into(), -801 path_status_sequence_number: frame.status_seq_no.into(), -802 raw: None, -803 }, -804 Self::PathStatusBackup(frame) => QuicFrame::PathStatusBackup { -805 path_id: frame.path_id.as_u32().into(), -806 path_status_sequence_number: frame.status_seq_no.into(), -807 raw: None, -808 }, -809 Self::PathsBlocked(frame) => QuicFrame::PathsBlocked { -810 maximum_path_id: frame.0.as_u32().into(), -811 raw: None, -812 }, -813 Self::PathCidsBlocked(frame) => QuicFrame::PathCidsBlocked { -814 path_id: frame.path_id.as_u32().into(), -815 next_sequence_number: frame.next_seq.into(), -816 raw: None, -817 }, -818 Self::MaxPathId(id) => QuicFrame::MaxPathId { -819 maximum_path_id: id.0.as_u32().into(), -820 raw: None, -821 }, -822 Self::AckFrequency(f) => QuicFrame::AckFrequency { -823 sequence_number: f.sequence.into_inner(), -824 ack_eliciting_threshold: f.ack_eliciting_threshold.into_inner(), -825 requested_max_ack_delay: f.request_max_ack_delay.into_inner(), -826 reordering_threshold: f.reordering_threshold.into_inner(), -827 raw: None, -828 }, -829 Self::ImmediateAck => QuicFrame::ImmediateAck { raw: None }, -830 Self::ObservedAddr(f) => QuicFrame::ObservedAddress { -831 sequence_number: f.seq_no.into_inner(), -832 ip_v4: match f.ip { -833 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), -834 IpAddr::V6(ipv6_addr) => None, -835 }, -836 ip_v6: match f.ip { -837 IpAddr::V4(ipv4_addr) => None, -838 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), -839 }, -840 port: f.port, -841 raw: None, -842 }, -843 Self::AddAddress(f) => QuicFrame::AddAddress { -844 sequence_number: f.seq_no.into_inner(), -845 ip_v4: match f.ip { -846 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), -847 IpAddr::V6(ipv6_addr) => None, -848 }, -849 ip_v6: match f.ip { -850 IpAddr::V4(ipv4_addr) => None, -851 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), -852 }, -853 port: f.port, -854 }, -855 Self::ReachOut(f) => QuicFrame::ReachOut { -856 round: f.round.into_inner(), -857 ip_v4: match f.ip { -858 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), -859 IpAddr::V6(ipv6_addr) => None, -860 }, -861 ip_v6: match f.ip { -862 IpAddr::V4(ipv4_addr) => None, -863 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), -864 }, -865 port: f.port, -866 }, -867 Self::RemoveAddress(f) => QuicFrame::RemoveAddress { -868 sequence_number: f.seq_no.into_inner(), -869 }, -870 } -871 } -872} -873 -874#[cfg(feature = "qlog")] -875impl From<crate::Dir> for StreamType { -876 fn from(value: crate::Dir) -> Self { -877 match value { -878 crate::Dir::Bi => Self::Bidirectional, -879 crate::Dir::Uni => Self::Unidirectional, -880 } -881 } -882} -883 -884#[cfg(feature = "qlog")] -885fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType { -886 match space { -887 SpaceId::Initial => PacketType::Initial, -888 SpaceId::Handshake => PacketType::Handshake, -889 SpaceId::Data if is_0rtt => PacketType::ZeroRtt, -890 SpaceId::Data => PacketType::OneRtt, -891 } -892} -893 -894#[cfg(feature = "qlog")] -895fn stringify_cid(cid: ConnectionId) -> String { -896 format!("{cid}") -897} -898 -899#[cfg(feature = "qlog")] -900fn tuple_endpoint_info( -901 ip: Option<IpAddr>, -902 port: Option<u16>, -903 cid: Option<ConnectionId>, -904) -> TupleEndpointInfo { -905 let (ip_v4, port_v4, ip_v6, port_v6) = match ip { -906 Some(addr) => match addr { -907 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None), -908 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port), -909 }, -910 None => (None, None, None, None), -911 }; -912 TupleEndpointInfo { -913 ip_v4, -914 port_v4, -915 ip_v6, -916 port_v6, -917 connection_ids: cid.map(|cid| vec![cid.to_string()]), -918 } -919} -920 -921#[cfg(feature = "qlog")] -922fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) { -923 let transport_error = match code { -924 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError), -925 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError), -926 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused), -927 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError), -928 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError), -929 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError), -930 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError), -931 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError), -932 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => { -933 Some(quic::TransportError::TransportParameterError) -934 } -935 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => { -936 Some(quic::TransportError::ConnectionIdLimitError) -937 } -938 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation), -939 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken), -940 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError), -941 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => { -942 Some(quic::TransportError::CryptoBufferExceeded) -943 } -944 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError), -945 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached), -946 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath), -947 // multipath -948 TransportErrorCode::APPLICATION_ABANDON_PATH => { -949 Some(quic::TransportError::ApplicationAbandonPath) -950 } -951 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => { -952 Some(quic::TransportError::PathResourceLimitReached) -953 } -954 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor), -955 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => { -956 Some(quic::TransportError::NoCidsAvailableForPath) -957 } -958 _ => None, -959 }; -960 let code = match transport_error { -961 None => Some(code.into()), -962 Some(_) => None, -963 }; -964 (transport_error, code) -965} -966 -967#[cfg(feature = "qlog")] -968fn fmt_tuple_id(path_id: u64) -> String { -969 format!("p{path_id}") -970} -971 -972#[cfg(feature = "qlog")] -973impl TransportParameters { -974 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet { -975 ParametersSet { -976 initiator: Some(initiator), -977 resumption_allowed: None, -978 early_data_enabled: None, -979 tls_cipher: None, -980 original_destination_connection_id: self -981 .original_dst_cid -982 .as_ref() -983 .map(ToString::to_string), -984 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string), -985 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string), -986 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string), -987 disable_active_migration: Some(self.disable_active_migration), -988 max_idle_timeout: Some(self.max_idle_timeout.into()), -989 max_udp_payload_size: Some(self.max_udp_payload_size.into()), -990 ack_delay_exponent: Some(self.ack_delay_exponent.into()), -991 max_ack_delay: Some(self.max_ack_delay.into()), -992 active_connection_id_limit: Some(self.active_connection_id_limit.into()), -993 initial_max_data: Some(self.initial_max_data.into()), -994 initial_max_stream_data_bidi_local: Some( -995 self.initial_max_stream_data_bidi_local.into(), -996 ), -997 initial_max_stream_data_bidi_remote: Some( -998 self.initial_max_stream_data_bidi_remote.into(), -999 ), -1000 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()), -1001 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()), -1002 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()), -1003 preferred_address: self.preferred_address.as_ref().map(Into::into), -1004 min_ack_delay: self.min_ack_delay.map(Into::into), -1005 address_discovery: self.address_discovery_role.to_qlog(), -1006 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64), -1007 max_remote_nat_traversal_addresses: self -1008 .max_remote_nat_traversal_addresses -1009 .map(|v| u64::from(v.get())), -1010 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into), -1011 grease_quic_bit: Some(self.grease_quic_bit), -1012 unknown_parameters: Default::default(), -1013 } -1014 } -1015 -1016 fn to_qlog_restored(self) -> ParametersRestored { -1017 ParametersRestored { -1018 disable_active_migration: Some(self.disable_active_migration), -1019 max_idle_timeout: Some(self.max_idle_timeout.into()), -1020 max_udp_payload_size: Some(self.max_udp_payload_size.into()), -1021 active_connection_id_limit: Some(self.active_connection_id_limit.into()), -1022 initial_max_data: Some(self.initial_max_data.into()), -1023 initial_max_stream_data_bidi_local: Some( -1024 self.initial_max_stream_data_bidi_local.into(), -1025 ), -1026 initial_max_stream_data_bidi_remote: Some( -1027 self.initial_max_stream_data_bidi_remote.into(), -1028 ), -1029 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()), -1030 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()), -1031 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()), -1032 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into), -1033 grease_quic_bit: Some(self.grease_quic_bit), -1034 } -1035 } -1036} -1037 -1038#[cfg(feature = "qlog")] -1039impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress { -1040 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self { -1041 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default(); -1042 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default(); -1043 let ip_v4 = value -1044 .address_v4 -1045 .map(|addr| addr.ip().to_string()) -1046 .unwrap_or_default(); -1047 let ip_v6 = value -1048 .address_v6 -1049 .map(|addr| addr.ip().to_string()) -1050 .unwrap_or_default(); -1051 let connection_id = value.connection_id.to_string(); -1052 let stateless_reset_token = value.stateless_reset_token.to_string(); -1053 -1054 Self { -1055 ip_v4, -1056 ip_v6, -1057 port_v4, -1058 port_v6, -1059 connection_id, -1060 stateless_reset_token, -1061 } -1062 } -1063} -1064 -1065#[cfg(feature = "qlog")] -1066impl crate::address_discovery::Role { -1067 fn to_qlog(self) -> Option<AddressDiscoveryRole> { -1068 match self { -1069 Self::SendOnly => Some(AddressDiscoveryRole::SendOnly), -1070 Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly), -1071 Self::Both => Some(AddressDiscoveryRole::Both), -1072 Self::Disabled => None, -1073 } -1074 } -1075} \ No newline at end of file +488 /// `len` is the packet's full length (before decryption). +489 pub(crate) fn new(len: usize) -> Self { +490 #[cfg(not(feature = "qlog"))] +491 let this = Self {}; +492 +493 #[cfg(feature = "qlog")] +494 let this = { +495 let mut this = Self { +496 inner: Default::default(), +497 padding: 0, +498 }; +499 this.inner.header.length = Some(len as u16); +500 this +501 }; +502 +503 this +504 } +505 +506 /// Adds info from the packet header. +507 pub(crate) fn header(&mut self, header: &Header, pn: Option<u64>, path_id: PathId) { +508 #[cfg(feature = "qlog")] +509 { +510 let is_0rtt = !header.is_1rtt(); +511 self.inner.header.scid = header.src_cid().map(stringify_cid); +512 self.inner.header.dcid = Some(stringify_cid(header.dst_cid())); +513 self.inner.header.packet_number = pn; +514 self.inner.header.packet_type = packet_type(header.space(), is_0rtt); +515 self.inner.header.path_id = Some(path_id.as_u32() as u64); +516 } +517 } +518 +519 /// Adds a frame. +520 pub(crate) fn frame(&mut self, frame: &Frame) { +521 #[cfg(feature = "qlog")] +522 { +523 if matches!(frame, crate::Frame::Padding) { +524 self.padding += 1; +525 } else { +526 self.emit_padding(); +527 self.inner +528 .frames +529 .get_or_insert_default() +530 .push(frame.to_qlog()) +531 } +532 } +533 } +534 +535 #[cfg(feature = "qlog")] +536 fn emit_padding(&mut self) { +537 if self.padding > 0 { +538 self.inner +539 .frames +540 .get_or_insert_default() +541 .push(QuicFrame::Padding { +542 raw: Some(RawInfo { +543 length: Some(self.padding as u64), +544 payload_length: Some(self.padding as u64), +545 data: None, +546 }), +547 }); +548 self.padding = 0; +549 } +550 } +551} +552 +553/* Frame conversions to qlog */ +554#[cfg(feature = "qlog")] +555pub(crate) trait ToQlog { +556 fn to_qlog(&self) -> QuicFrame; +557} +558 +559#[cfg(feature = "qlog")] +560impl<'a> ToQlog for frame::AckEncoder<'a> { +561 fn to_qlog(&self) -> QuicFrame { +562 QuicFrame::Ack { +563 ack_delay: Some(self.delay as f32), +564 acked_ranges: Some(AckedRanges::Double( +565 self.ranges +566 .iter() +567 .map(|range| (range.start, range.end)) +568 .collect(), +569 )), +570 ect1: self.ecn.map(|e| e.ect1), +571 ect0: self.ecn.map(|e| e.ect0), +572 ce: self.ecn.map(|e| e.ce), +573 raw: None, +574 } +575 } +576} +577 +578#[cfg(feature = "qlog")] +579impl ToQlog for frame::AckFrequency { +580 fn to_qlog(&self) -> QuicFrame { +581 QuicFrame::AckFrequency { +582 sequence_number: self.sequence.into_inner(), +583 ack_eliciting_threshold: self.ack_eliciting_threshold.into_inner(), +584 requested_max_ack_delay: self.request_max_ack_delay.into_inner(), +585 reordering_threshold: self.reordering_threshold.into_inner(), +586 raw: None, +587 } +588 } +589} +590 +591#[cfg(feature = "qlog")] +592impl ToQlog for frame::AddAddress { +593 fn to_qlog(&self) -> QuicFrame { +594 QuicFrame::AddAddress { +595 sequence_number: self.seq_no.into_inner(), +596 ip_v4: match self.ip { +597 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), +598 IpAddr::V6(ipv6_addr) => None, +599 }, +600 ip_v6: match self.ip { +601 IpAddr::V4(ipv4_addr) => None, +602 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), +603 }, +604 port: self.port, +605 } +606 } +607} +608 +609#[cfg(feature = "qlog")] +610impl ToQlog for frame::CloseEncoder<'_> { +611 fn to_qlog(&self) -> QuicFrame { +612 self.close.to_qlog() +613 } +614} +615 +616#[cfg(feature = "qlog")] +617impl ToQlog for frame::Close { +618 fn to_qlog(&self) -> QuicFrame { +619 match self { +620 Self::Connection(f) => { +621 let (error, error_code) = transport_error(f.error_code); +622 let error = error.map(|transport_error| { +623 ConnectionClosedFrameError::TransportError(transport_error) +624 }); +625 QuicFrame::ConnectionClose { +626 error_space: Some(ErrorSpace::TransportError), +627 error, +628 error_code, +629 reason: String::from_utf8(f.reason.to_vec()).ok(), +630 reason_bytes: None, +631 trigger_frame_type: None, +632 } +633 } +634 Self::Application(f) => QuicFrame::ConnectionClose { +635 error_space: Some(ErrorSpace::ApplicationError), +636 error: None, +637 error_code: Some(f.error_code.into_inner()), +638 reason: String::from_utf8(f.reason.to_vec()).ok(), +639 reason_bytes: None, +640 trigger_frame_type: None, +641 }, +642 } +643 } +644} +645 +646#[cfg(feature = "qlog")] +647impl ToQlog for frame::Crypto { +648 fn to_qlog(&self) -> QuicFrame { +649 QuicFrame::Crypto { +650 offset: self.offset, +651 raw: Some(RawInfo { +652 length: Some(self.data.len() as u64), +653 ..Default::default() +654 }), +655 } +656 } +657} +658 +659#[cfg(feature = "qlog")] +660impl ToQlog for frame::Datagram { +661 fn to_qlog(&self) -> QuicFrame { +662 QuicFrame::Datagram { +663 raw: Some(RawInfo { +664 length: Some(self.data.len() as u64), +665 ..Default::default() +666 }), +667 } +668 } +669} +670 +671#[cfg(feature = "qlog")] +672impl ToQlog for frame::HandshakeDone { +673 fn to_qlog(&self) -> QuicFrame { +674 QuicFrame::HandshakeDone { raw: None } +675 } +676} +677 +678#[cfg(feature = "qlog")] +679impl ToQlog for frame::ImmediateAck { +680 fn to_qlog(&self) -> QuicFrame { +681 QuicFrame::ImmediateAck { raw: None } +682 } +683} +684 +685#[cfg(feature = "qlog")] +686impl ToQlog for frame::MaxData { +687 fn to_qlog(&self) -> QuicFrame { +688 QuicFrame::MaxData { +689 maximum: self.0.into(), +690 raw: None, +691 } +692 } +693} +694 +695#[cfg(feature = "qlog")] +696impl ToQlog for frame::MaxPathId { +697 fn to_qlog(&self) -> QuicFrame { +698 QuicFrame::MaxPathId { +699 maximum_path_id: self.0.as_u32().into(), +700 raw: None, +701 } +702 } +703} +704 +705#[cfg(feature = "qlog")] +706impl ToQlog for frame::MaxStreamData { +707 fn to_qlog(&self) -> QuicFrame { +708 QuicFrame::MaxStreamData { +709 stream_id: self.id.into(), +710 maximum: self.offset, +711 raw: None, +712 } +713 } +714} +715 +716#[cfg(feature = "qlog")] +717impl ToQlog for frame::MaxStreams { +718 fn to_qlog(&self) -> QuicFrame { +719 QuicFrame::MaxStreams { +720 maximum: self.count, +721 stream_type: self.dir.into(), +722 raw: None, +723 } +724 } +725} +726 +727#[cfg(feature = "qlog")] +728impl ToQlog for frame::NewConnectionId { +729 fn to_qlog(&self) -> QuicFrame { +730 QuicFrame::NewConnectionId { +731 sequence_number: self.sequence, +732 retire_prior_to: self.retire_prior_to, +733 connection_id_length: Some(self.id.len() as u8), +734 connection_id: self.id.to_string(), +735 stateless_reset_token: Some(self.reset_token.to_string()), +736 raw: None, +737 } +738 } +739} +740 +741#[cfg(feature = "qlog")] +742impl ToQlog for frame::NewToken { +743 fn to_qlog(&self) -> QuicFrame { +744 QuicFrame::NewToken { +745 token: qlog::Token { +746 ty: Some(TokenType::Retry), +747 raw: Some(RawInfo { +748 data: HexSlice::maybe_string(Some(&self.token)), +749 length: Some(self.token.len() as u64), +750 payload_length: None, +751 }), +752 details: None, +753 }, +754 raw: None, +755 } +756 } +757} +758 +759#[cfg(feature = "qlog")] +760impl ToQlog for frame::ObservedAddr { +761 fn to_qlog(&self) -> QuicFrame { +762 QuicFrame::ObservedAddress { +763 sequence_number: self.seq_no.into_inner(), +764 ip_v4: match self.ip { +765 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), +766 IpAddr::V6(ipv6_addr) => None, +767 }, +768 ip_v6: match self.ip { +769 IpAddr::V4(ipv4_addr) => None, +770 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), +771 }, +772 port: self.port, +773 raw: None, +774 } +775 } +776} +777 +778#[cfg(feature = "qlog")] +779impl ToQlog for frame::PathAbandon { +780 fn to_qlog(&self) -> QuicFrame { +781 QuicFrame::PathAbandon { +782 path_id: self.path_id.as_u32().into(), +783 error_code: self.error_code.into(), +784 raw: None, +785 } +786 } +787} +788 +789#[cfg(feature = "qlog")] +790impl ToQlog for frame::PathAckEncoder<'_> { +791 fn to_qlog(&self) -> QuicFrame { +792 QuicFrame::PathAck { +793 path_id: self.path_id.as_u32() as u64, +794 ack_delay: Some(self.delay as f32), +795 acked_ranges: Some(AckedRanges::Double( +796 self.ranges +797 .iter() +798 .map(|range| (range.start, range.end)) +799 .collect(), +800 )), +801 ect1: self.ecn.map(|e| e.ect1), +802 ect0: self.ecn.map(|e| e.ect0), +803 ce: self.ecn.map(|e| e.ce), +804 raw: None, +805 } +806 } +807} +808 +809#[cfg(feature = "qlog")] +810impl ToQlog for frame::PathChallenge { +811 #[cfg(feature = "qlog")] +812 fn to_qlog(&self) -> QuicFrame { +813 QuicFrame::PathChallenge { +814 data: Some(self.to_string()), +815 raw: None, +816 } +817 } +818} +819 +820#[cfg(feature = "qlog")] +821impl ToQlog for frame::PathCidsBlocked { +822 fn to_qlog(&self) -> QuicFrame { +823 QuicFrame::PathCidsBlocked { +824 path_id: self.path_id.as_u32().into(), +825 next_sequence_number: self.next_seq.into(), +826 raw: None, +827 } +828 } +829} +830 +831#[cfg(feature = "qlog")] +832impl ToQlog for frame::PathResponse { +833 fn to_qlog(&self) -> QuicFrame { +834 QuicFrame::PathResponse { +835 data: Some(self.to_string()), +836 raw: None, +837 } +838 } +839} +840 +841#[cfg(feature = "qlog")] +842impl ToQlog for frame::ReachOut { +843 fn to_qlog(&self) -> QuicFrame { +844 QuicFrame::ReachOut { +845 round: self.round.into_inner(), +846 ip_v4: match self.ip { +847 IpAddr::V4(ipv4_addr) => Some(ipv4_addr.to_string()), +848 IpAddr::V6(ipv6_addr) => None, +849 }, +850 ip_v6: match self.ip { +851 IpAddr::V4(ipv4_addr) => None, +852 IpAddr::V6(ipv6_addr) => Some(ipv6_addr.to_string()), +853 }, +854 port: self.port, +855 } +856 } +857} +858 +859#[cfg(feature = "qlog")] +860impl ToQlog for frame::Ping { +861 fn to_qlog(&self) -> QuicFrame { +862 QuicFrame::Ping { raw: None } +863 } +864} +865 +866#[cfg(feature = "qlog")] +867impl ToQlog for frame::PathStatusAvailable { +868 fn to_qlog(&self) -> QuicFrame { +869 QuicFrame::PathStatusAvailable { +870 path_id: self.path_id.as_u32().into(), +871 path_status_sequence_number: self.status_seq_no.into(), +872 raw: None, +873 } +874 } +875} +876 +877#[cfg(feature = "qlog")] +878impl ToQlog for frame::PathStatusBackup { +879 fn to_qlog(&self) -> QuicFrame { +880 QuicFrame::PathStatusBackup { +881 path_id: self.path_id.as_u32().into(), +882 path_status_sequence_number: self.status_seq_no.into(), +883 raw: None, +884 } +885 } +886} +887 +888#[cfg(feature = "qlog")] +889impl ToQlog for frame::PathsBlocked { +890 fn to_qlog(&self) -> QuicFrame { +891 QuicFrame::PathsBlocked { +892 maximum_path_id: self.0.as_u32().into(), +893 raw: None, +894 } +895 } +896} +897 +898#[cfg(feature = "qlog")] +899impl ToQlog for frame::ResetStream { +900 fn to_qlog(&self) -> QuicFrame { +901 QuicFrame::ResetStream { +902 stream_id: self.id.into(), +903 error_code: Some(self.error_code.into_inner()), +904 final_size: self.final_offset.into(), +905 error: ApplicationError::Unknown, +906 raw: None, +907 } +908 } +909} +910 +911#[cfg(feature = "qlog")] +912impl ToQlog for frame::StopSending { +913 fn to_qlog(&self) -> QuicFrame { +914 QuicFrame::StopSending { +915 stream_id: self.id.into(), +916 error_code: Some(self.error_code.into_inner()), +917 error: ApplicationError::Unknown, +918 raw: None, +919 } +920 } +921} +922 +923#[cfg(feature = "qlog")] +924impl ToQlog for frame::RetireConnectionId { +925 fn to_qlog(&self) -> QuicFrame { +926 QuicFrame::RetireConnectionId { +927 sequence_number: self.sequence, +928 raw: None, +929 } +930 } +931} +932 +933#[cfg(feature = "qlog")] +934impl ToQlog for frame::RemoveAddress { +935 fn to_qlog(&self) -> QuicFrame { +936 QuicFrame::RemoveAddress { +937 sequence_number: self.seq_no.into_inner(), +938 } +939 } +940} +941 +942#[cfg(feature = "qlog")] +943impl ToQlog for frame::StreamMetaEncoder { +944 fn to_qlog(&self) -> QuicFrame { +945 let meta = &self.meta; +946 QuicFrame::Stream { +947 stream_id: meta.id.into(), +948 offset: Some(meta.offsets.start), +949 fin: Some(meta.fin), +950 raw: Some(RawInfo { +951 length: Some(meta.offsets.end - meta.offsets.start), +952 ..Default::default() +953 }), +954 } +955 } +956} +957 +958#[cfg(feature = "qlog")] +959impl Frame { +960 /// Converts a [`crate::Frame`] into a [`QuicFrame`]. +961 pub(crate) fn to_qlog(&self) -> QuicFrame { +962 match self { +963 Self::Padding => QuicFrame::Padding { +964 raw: Some(RawInfo { +965 length: None, +966 payload_length: Some(1), +967 data: None, +968 }), +969 }, +970 Self::Ping => frame::Ping.to_qlog(), +971 Self::Ack(f) => QuicFrame::Ack { +972 ack_delay: Some(f.delay as f32), +973 acked_ranges: Some(AckedRanges::Double( +974 f.iter() +975 .map(|range| (*range.start(), *range.end())) +976 .collect(), +977 )), +978 ect1: f.ecn.as_ref().map(|e| e.ect1), +979 ect0: f.ecn.as_ref().map(|e| e.ect0), +980 ce: f.ecn.as_ref().map(|e| e.ce), +981 raw: None, +982 }, +983 Self::ResetStream(f) => f.to_qlog(), +984 Self::StopSending(f) => f.to_qlog(), +985 Self::Crypto(f) => f.to_qlog(), +986 Self::NewToken(f) => f.to_qlog(), +987 Self::Stream(s) => QuicFrame::Stream { +988 stream_id: s.id.into(), +989 offset: Some(s.offset), +990 fin: Some(s.fin), +991 raw: Some(RawInfo { +992 length: Some(s.data.len() as u64), +993 ..Default::default() +994 }), +995 }, +996 Self::MaxData(v) => v.to_qlog(), +997 Self::MaxStreamData(f) => f.to_qlog(), +998 Self::MaxStreams(f) => f.to_qlog(), +999 Self::DataBlocked { offset } => QuicFrame::DataBlocked { +1000 limit: *offset, +1001 raw: None, +1002 }, +1003 Self::StreamDataBlocked { id, offset } => QuicFrame::StreamDataBlocked { +1004 stream_id: (*id).into(), +1005 limit: *offset, +1006 raw: None, +1007 }, +1008 Self::StreamsBlocked { dir, limit } => QuicFrame::StreamsBlocked { +1009 stream_type: (*dir).into(), +1010 limit: *limit, +1011 raw: None, +1012 }, +1013 Self::NewConnectionId(f) => f.to_qlog(), +1014 Self::RetireConnectionId(f) => f.to_qlog(), +1015 Self::PathChallenge(f) => f.to_qlog(), +1016 Self::PathResponse(f) => f.to_qlog(), +1017 Self::Close(close) => close.to_qlog(), +1018 Self::Datagram(d) => d.to_qlog(), +1019 Self::HandshakeDone => frame::HandshakeDone.to_qlog(), +1020 Self::PathAck(ack) => QuicFrame::PathAck { +1021 path_id: ack.path_id.as_u32().into(), +1022 ack_delay: Some(ack.delay as f32), +1023 ect1: ack.ecn.as_ref().map(|e| e.ect1), +1024 ect0: ack.ecn.as_ref().map(|e| e.ect0), +1025 ce: ack.ecn.as_ref().map(|e| e.ce), +1026 raw: None, +1027 acked_ranges: Some(AckedRanges::Double( +1028 ack.into_iter() +1029 .map(|range| (*range.start(), *range.end())) +1030 .collect(), +1031 )), +1032 }, +1033 Self::PathAbandon(frame) => frame.to_qlog(), +1034 Self::PathStatusAvailable(frame) => frame.to_qlog(), +1035 Self::PathStatusBackup(frame) => frame.to_qlog(), +1036 Self::PathsBlocked(frame) => frame.to_qlog(), +1037 Self::PathCidsBlocked(frame) => frame.to_qlog(), +1038 Self::MaxPathId(f) => f.to_qlog(), +1039 Self::AckFrequency(f) => f.to_qlog(), +1040 Self::ImmediateAck => frame::ImmediateAck.to_qlog(), +1041 Self::ObservedAddr(f) => f.to_qlog(), +1042 Self::AddAddress(f) => f.to_qlog(), +1043 Self::ReachOut(f) => f.to_qlog(), +1044 Self::RemoveAddress(f) => f.to_qlog(), +1045 } +1046 } +1047} +1048 +1049#[cfg(feature = "qlog")] +1050impl From<crate::Dir> for StreamType { +1051 fn from(value: crate::Dir) -> Self { +1052 match value { +1053 crate::Dir::Bi => Self::Bidirectional, +1054 crate::Dir::Uni => Self::Unidirectional, +1055 } +1056 } +1057} +1058 +1059#[cfg(feature = "qlog")] +1060fn packet_type(space: SpaceId, is_0rtt: bool) -> PacketType { +1061 match space { +1062 SpaceId::Initial => PacketType::Initial, +1063 SpaceId::Handshake => PacketType::Handshake, +1064 SpaceId::Data if is_0rtt => PacketType::ZeroRtt, +1065 SpaceId::Data => PacketType::OneRtt, +1066 } +1067} +1068 +1069#[cfg(feature = "qlog")] +1070fn stringify_cid(cid: ConnectionId) -> String { +1071 format!("{cid}") +1072} +1073 +1074#[cfg(feature = "qlog")] +1075fn tuple_endpoint_info( +1076 ip: Option<IpAddr>, +1077 port: Option<u16>, +1078 cid: Option<ConnectionId>, +1079) -> TupleEndpointInfo { +1080 let (ip_v4, port_v4, ip_v6, port_v6) = match ip { +1081 Some(addr) => match addr { +1082 IpAddr::V4(ipv4_addr) => (Some(ipv4_addr.to_string()), port, None, None), +1083 IpAddr::V6(ipv6_addr) => (None, None, Some(ipv6_addr.to_string()), port), +1084 }, +1085 None => (None, None, None, None), +1086 }; +1087 TupleEndpointInfo { +1088 ip_v4, +1089 port_v4, +1090 ip_v6, +1091 port_v6, +1092 connection_ids: cid.map(|cid| vec![cid.to_string()]), +1093 } +1094} +1095 +1096#[cfg(feature = "qlog")] +1097fn transport_error(code: TransportErrorCode) -> (Option<quic::TransportError>, Option<u64>) { +1098 let transport_error = match code { +1099 TransportErrorCode::NO_ERROR => Some(quic::TransportError::NoError), +1100 TransportErrorCode::INTERNAL_ERROR => Some(quic::TransportError::InternalError), +1101 TransportErrorCode::CONNECTION_REFUSED => Some(quic::TransportError::ConnectionRefused), +1102 TransportErrorCode::FLOW_CONTROL_ERROR => Some(quic::TransportError::FlowControlError), +1103 TransportErrorCode::STREAM_LIMIT_ERROR => Some(quic::TransportError::StreamLimitError), +1104 TransportErrorCode::STREAM_STATE_ERROR => Some(quic::TransportError::StreamStateError), +1105 TransportErrorCode::FINAL_SIZE_ERROR => Some(quic::TransportError::FinalSizeError), +1106 TransportErrorCode::FRAME_ENCODING_ERROR => Some(quic::TransportError::FrameEncodingError), +1107 TransportErrorCode::TRANSPORT_PARAMETER_ERROR => { +1108 Some(quic::TransportError::TransportParameterError) +1109 } +1110 TransportErrorCode::CONNECTION_ID_LIMIT_ERROR => { +1111 Some(quic::TransportError::ConnectionIdLimitError) +1112 } +1113 TransportErrorCode::PROTOCOL_VIOLATION => Some(quic::TransportError::ProtocolViolation), +1114 TransportErrorCode::INVALID_TOKEN => Some(quic::TransportError::InvalidToken), +1115 TransportErrorCode::APPLICATION_ERROR => Some(quic::TransportError::ApplicationError), +1116 TransportErrorCode::CRYPTO_BUFFER_EXCEEDED => { +1117 Some(quic::TransportError::CryptoBufferExceeded) +1118 } +1119 TransportErrorCode::KEY_UPDATE_ERROR => Some(quic::TransportError::KeyUpdateError), +1120 TransportErrorCode::AEAD_LIMIT_REACHED => Some(quic::TransportError::AeadLimitReached), +1121 TransportErrorCode::NO_VIABLE_PATH => Some(quic::TransportError::NoViablePath), +1122 // multipath +1123 TransportErrorCode::APPLICATION_ABANDON_PATH => { +1124 Some(quic::TransportError::ApplicationAbandonPath) +1125 } +1126 TransportErrorCode::PATH_RESOURCE_LIMIT_REACHED => { +1127 Some(quic::TransportError::PathResourceLimitReached) +1128 } +1129 TransportErrorCode::PATH_UNSTABLE_OR_POOR => Some(quic::TransportError::PathUnstableOrPoor), +1130 TransportErrorCode::NO_CID_AVAILABLE_FOR_PATH => { +1131 Some(quic::TransportError::NoCidsAvailableForPath) +1132 } +1133 _ => None, +1134 }; +1135 let code = match transport_error { +1136 None => Some(code.into()), +1137 Some(_) => None, +1138 }; +1139 (transport_error, code) +1140} +1141 +1142#[cfg(feature = "qlog")] +1143fn fmt_tuple_id(path_id: u64) -> String { +1144 format!("p{path_id}") +1145} +1146 +1147#[cfg(feature = "qlog")] +1148impl TransportParameters { +1149 fn to_qlog(self, initiator: TransportInitiator) -> ParametersSet { +1150 ParametersSet { +1151 initiator: Some(initiator), +1152 resumption_allowed: None, +1153 early_data_enabled: None, +1154 tls_cipher: None, +1155 original_destination_connection_id: self +1156 .original_dst_cid +1157 .as_ref() +1158 .map(ToString::to_string), +1159 initial_source_connection_id: self.initial_src_cid.as_ref().map(ToString::to_string), +1160 retry_source_connection_id: self.retry_src_cid.as_ref().map(ToString::to_string), +1161 stateless_reset_token: self.stateless_reset_token.as_ref().map(ToString::to_string), +1162 disable_active_migration: Some(self.disable_active_migration), +1163 max_idle_timeout: Some(self.max_idle_timeout.into()), +1164 max_udp_payload_size: Some(self.max_udp_payload_size.into()), +1165 ack_delay_exponent: Some(self.ack_delay_exponent.into()), +1166 max_ack_delay: Some(self.max_ack_delay.into()), +1167 active_connection_id_limit: Some(self.active_connection_id_limit.into()), +1168 initial_max_data: Some(self.initial_max_data.into()), +1169 initial_max_stream_data_bidi_local: Some( +1170 self.initial_max_stream_data_bidi_local.into(), +1171 ), +1172 initial_max_stream_data_bidi_remote: Some( +1173 self.initial_max_stream_data_bidi_remote.into(), +1174 ), +1175 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()), +1176 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()), +1177 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()), +1178 preferred_address: self.preferred_address.as_ref().map(Into::into), +1179 min_ack_delay: self.min_ack_delay.map(Into::into), +1180 address_discovery: self.address_discovery_role.to_qlog(), +1181 initial_max_path_id: self.initial_max_path_id.map(|p| p.as_u32() as u64), +1182 max_remote_nat_traversal_addresses: self +1183 .max_remote_nat_traversal_addresses +1184 .map(|v| u64::from(v.get())), +1185 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into), +1186 grease_quic_bit: Some(self.grease_quic_bit), +1187 unknown_parameters: Default::default(), +1188 } +1189 } +1190 +1191 fn to_qlog_restored(self) -> ParametersRestored { +1192 ParametersRestored { +1193 disable_active_migration: Some(self.disable_active_migration), +1194 max_idle_timeout: Some(self.max_idle_timeout.into()), +1195 max_udp_payload_size: Some(self.max_udp_payload_size.into()), +1196 active_connection_id_limit: Some(self.active_connection_id_limit.into()), +1197 initial_max_data: Some(self.initial_max_data.into()), +1198 initial_max_stream_data_bidi_local: Some( +1199 self.initial_max_stream_data_bidi_local.into(), +1200 ), +1201 initial_max_stream_data_bidi_remote: Some( +1202 self.initial_max_stream_data_bidi_remote.into(), +1203 ), +1204 initial_max_stream_data_uni: Some(self.initial_max_stream_data_uni.into()), +1205 initial_max_streams_bidi: Some(self.initial_max_streams_bidi.into()), +1206 initial_max_streams_uni: Some(self.initial_max_streams_uni.into()), +1207 max_datagram_frame_size: self.max_datagram_frame_size.map(Into::into), +1208 grease_quic_bit: Some(self.grease_quic_bit), +1209 } +1210 } +1211} +1212 +1213#[cfg(feature = "qlog")] +1214impl From<&crate::transport_parameters::PreferredAddress> for PreferredAddress { +1215 fn from(value: &crate::transport_parameters::PreferredAddress) -> Self { +1216 let port_v4 = value.address_v4.map(|addr| addr.port()).unwrap_or_default(); +1217 let port_v6 = value.address_v6.map(|addr| addr.port()).unwrap_or_default(); +1218 let ip_v4 = value +1219 .address_v4 +1220 .map(|addr| addr.ip().to_string()) +1221 .unwrap_or_default(); +1222 let ip_v6 = value +1223 .address_v6 +1224 .map(|addr| addr.ip().to_string()) +1225 .unwrap_or_default(); +1226 let connection_id = value.connection_id.to_string(); +1227 let stateless_reset_token = value.stateless_reset_token.to_string(); +1228 +1229 Self { +1230 ip_v4, +1231 ip_v6, +1232 port_v4, +1233 port_v6, +1234 connection_id, +1235 stateless_reset_token, +1236 } +1237 } +1238} +1239 +1240#[cfg(feature = "qlog")] +1241impl crate::address_discovery::Role { +1242 fn to_qlog(self) -> Option<AddressDiscoveryRole> { +1243 match self { +1244 Self::SendOnly => Some(AddressDiscoveryRole::SendOnly), +1245 Self::ReceiveOnly => Some(AddressDiscoveryRole::ReceiveOnly), +1246 Self::Both => Some(AddressDiscoveryRole::Both), +1247 Self::Disabled => None, +1248 } +1249 } +1250} \ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/stats.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/stats.rs.html index 1cf7bb5d8..5726f7e70 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/stats.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/stats.rs.html @@ -1,7 +1,7 @@ stats.rs - source

iroh_quinn_proto/connection/
stats.rs

1//! Connection statistics
 2
-3use crate::FrameType;
-4use crate::{Dir, Duration, frame::Frame};
+3use crate::Duration;
+4use crate::FrameType;
 5
 6/// Statistics about UDP datagrams transmitted or received on a connection
 7///
@@ -72,148 +72,132 @@
 72}
 73
 74impl FrameStats {
-75    pub(crate) fn record(&mut self, frame: &Frame) {
-76        match frame {
-77            Frame::Padding => {}
-78            Frame::Ping => self.ping += 1,
-79            Frame::Ack(_) => self.acks += 1,
-80            Frame::PathAck(_) => self.path_acks += 1,
-81            Frame::ResetStream(_) => self.reset_stream += 1,
-82            Frame::StopSending(_) => self.stop_sending += 1,
-83            Frame::Crypto(_) => self.crypto += 1,
-84            Frame::Datagram(_) => self.datagram += 1,
-85            Frame::NewToken(_) => self.new_token += 1,
-86            Frame::MaxData(_) => self.max_data += 1,
-87            Frame::MaxStreamData { .. } => self.max_stream_data += 1,
-88            Frame::MaxStreams { dir, .. } => {
-89                if *dir == Dir::Bi {
-90                    self.max_streams_bidi += 1;
-91                } else {
-92                    self.max_streams_uni += 1;
-93                }
-94            }
-95            Frame::DataBlocked { .. } => self.data_blocked += 1,
-96            Frame::Stream(_) => self.stream += 1,
-97            Frame::StreamDataBlocked { .. } => self.stream_data_blocked += 1,
-98            Frame::StreamsBlocked { dir, .. } => {
-99                if *dir == Dir::Bi {
-100                    self.streams_blocked_bidi += 1;
-101                } else {
-102                    self.streams_blocked_uni += 1;
-103                }
-104            }
-105            Frame::NewConnectionId(frame) => match frame.path_id {
-106                Some(_) => self.path_new_connection_id += 1,
-107                None => self.new_connection_id += 1,
-108            },
-109            Frame::RetireConnectionId(frame) => match frame.get_type() {
-110                FrameType::RetireConnectionId => self.retire_connection_id += 1,
-111                FrameType::PathRetireConnectionId => self.path_retire_connection_id += 1,
-112                _ => unreachable!(),
-113            },
-114            Frame::PathChallenge(_) => self.path_challenge += 1,
-115            Frame::PathResponse(_) => self.path_response += 1,
-116            Frame::Close(_) => self.connection_close += 1,
-117            Frame::AckFrequency(_) => self.ack_frequency += 1,
-118            Frame::ImmediateAck => self.immediate_ack += 1,
-119            Frame::HandshakeDone => self.handshake_done = self.handshake_done.saturating_add(1),
-120            Frame::ObservedAddr(_) => self.observed_addr += 1,
-121            Frame::PathAbandon(_) => self.path_abandon = self.path_abandon.saturating_add(1),
-122            Frame::PathStatusAvailable(_) => {
-123                self.path_status_available = self.path_status_available.saturating_add(1)
-124            }
-125            Frame::PathStatusBackup(_) => {
-126                self.path_status_backup = self.path_status_backup.saturating_add(1)
-127            }
-128            Frame::MaxPathId(_) => self.max_path_id = self.max_path_id.saturating_add(1),
-129            Frame::PathsBlocked(_) => self.paths_blocked = self.paths_blocked.saturating_add(1),
-130            Frame::PathCidsBlocked(_) => {
-131                self.path_cids_blocked = self.path_cids_blocked.saturating_add(1)
-132            }
-133            Frame::AddAddress(_) => self.add_address = self.add_address.saturating_add(1),
-134            Frame::ReachOut(_) => self.reach_out = self.reach_out.saturating_add(1),
-135            Frame::RemoveAddress(_) => self.remove_address = self.remove_address.saturating_add(1),
-136        }
-137    }
-138}
-139
-140impl std::fmt::Debug for FrameStats {
-141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-142        f.debug_struct("FrameStats")
-143            .field("ACK", &self.acks)
-144            .field("ACK_FREQUENCY", &self.ack_frequency)
-145            .field("CONNECTION_CLOSE", &self.connection_close)
-146            .field("CRYPTO", &self.crypto)
-147            .field("DATA_BLOCKED", &self.data_blocked)
-148            .field("DATAGRAM", &self.datagram)
-149            .field("HANDSHAKE_DONE", &self.handshake_done)
-150            .field("IMMEDIATE_ACK", &self.immediate_ack)
-151            .field("MAX_DATA", &self.max_data)
-152            .field("MAX_PATH_ID", &self.max_path_id)
-153            .field("MAX_STREAM_DATA", &self.max_stream_data)
-154            .field("MAX_STREAMS_BIDI", &self.max_streams_bidi)
-155            .field("MAX_STREAMS_UNI", &self.max_streams_uni)
-156            .field("NEW_CONNECTION_ID", &self.new_connection_id)
-157            .field("NEW_TOKEN", &self.new_token)
-158            .field("PATHS_BLOCKED", &self.paths_blocked)
-159            .field("PATH_ABANDON", &self.path_abandon)
-160            .field("PATH_ACK", &self.path_acks)
-161            .field("PATH_STATUS_AVAILABLE", &self.path_status_available)
-162            .field("PATH_STATUS_BACKUP", &self.path_status_backup)
-163            .field("PATH_CHALLENGE", &self.path_challenge)
-164            .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked)
-165            .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id)
-166            .field("PATH_RESPONSE", &self.path_response)
-167            .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id)
-168            .field("PING", &self.ping)
-169            .field("RESET_STREAM", &self.reset_stream)
-170            .field("RETIRE_CONNECTION_ID", &self.retire_connection_id)
-171            .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked)
-172            .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi)
-173            .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni)
-174            .field("STOP_SENDING", &self.stop_sending)
-175            .field("STREAM", &self.stream)
-176            .finish()
-177    }
-178}
-179
-180/// Statistics related to a transmission path
-181#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
-182#[non_exhaustive]
-183pub struct PathStats {
-184    /// Current best estimate of this connection's latency (round-trip-time)
-185    pub rtt: Duration,
-186    /// Current congestion window of the connection
-187    pub cwnd: u64,
-188    /// Congestion events on the connection
-189    pub congestion_events: u64,
-190    /// The amount of packets lost on this path
-191    pub lost_packets: u64,
-192    /// The amount of bytes lost on this path
-193    pub lost_bytes: u64,
-194    /// The amount of packets sent on this path
-195    pub sent_packets: u64,
-196    /// The amount of PLPMTUD probe packets sent on this path (also counted by `sent_packets`)
-197    pub sent_plpmtud_probes: u64,
-198    /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and
-199    /// `lost_bytes`)
-200    pub lost_plpmtud_probes: u64,
-201    /// The number of times a black hole was detected in the path
-202    pub black_holes_detected: u64,
-203    /// Largest UDP payload size the path currently supports
-204    pub current_mtu: u16,
-205}
-206
-207/// Connection statistics
-208#[derive(Debug, Default, Clone)]
-209#[non_exhaustive]
-210pub struct ConnectionStats {
-211    /// Statistics about UDP datagrams transmitted on a connection
-212    pub udp_tx: UdpStats,
-213    /// Statistics about UDP datagrams received on a connection
-214    pub udp_rx: UdpStats,
-215    /// Statistics about frames transmitted on a connection
-216    pub frame_tx: FrameStats,
-217    /// Statistics about frames received on a connection
-218    pub frame_rx: FrameStats,
-219}
\ No newline at end of file +75 pub(crate) fn record(&mut self, frame_type: FrameType) { +76 use FrameType::*; +77 // Increments the field. Added for readability +78 macro_rules! inc { +79 ($field_name: ident) => {{ self.$field_name = self.$field_name.saturating_add(1) }}; +80 } +81 match frame_type { +82 Padding => {} +83 Ping => inc!(ping), +84 Ack | AckEcn => inc!(acks), +85 PathAck | PathAckEcn => inc!(path_acks), +86 ResetStream => inc!(reset_stream), +87 StopSending => inc!(stop_sending), +88 Crypto => inc!(crypto), +89 Datagram(_) => inc!(datagram), +90 NewToken => inc!(new_token), +91 MaxData => inc!(max_data), +92 MaxStreamData => inc!(max_stream_data), +93 MaxStreamsBidi => inc!(max_streams_bidi), +94 MaxStreamsUni => inc!(max_streams_uni), +95 DataBlocked => inc!(data_blocked), +96 Stream(_) => inc!(stream), +97 StreamDataBlocked => inc!(stream_data_blocked), +98 StreamsBlockedUni => inc!(streams_blocked_uni), +99 StreamsBlockedBidi => inc!(streams_blocked_bidi), +100 NewConnectionId => inc!(new_connection_id), +101 PathNewConnectionId => inc!(path_new_connection_id), +102 RetireConnectionId => inc!(retire_connection_id), +103 PathRetireConnectionId => inc!(path_retire_connection_id), +104 PathChallenge => inc!(path_challenge), +105 PathResponse => inc!(path_response), +106 ConnectionClose | ApplicationClose => inc!(connection_close), +107 AckFrequency => inc!(ack_frequency), +108 ImmediateAck => inc!(immediate_ack), +109 HandshakeDone => inc!(handshake_done), +110 ObservedIpv4Addr | ObservedIpv6Addr => inc!(observed_addr), +111 PathAbandon => inc!(path_abandon), +112 PathStatusAvailable => inc!(path_status_available), +113 PathStatusBackup => inc!(path_status_backup), +114 MaxPathId => inc!(max_path_id), +115 PathsBlocked => inc!(paths_blocked), +116 PathCidsBlocked => inc!(path_cids_blocked), +117 AddIpv4Address | AddIpv6Address => inc!(add_address), +118 ReachOutAtIpv4 | ReachOutAtIpv6 => inc!(reach_out), +119 RemoveAddress => inc!(remove_address), +120 }; +121 } +122} +123 +124impl std::fmt::Debug for FrameStats { +125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +126 f.debug_struct("FrameStats") +127 .field("ACK", &self.acks) +128 .field("ACK_FREQUENCY", &self.ack_frequency) +129 .field("CONNECTION_CLOSE", &self.connection_close) +130 .field("CRYPTO", &self.crypto) +131 .field("DATA_BLOCKED", &self.data_blocked) +132 .field("DATAGRAM", &self.datagram) +133 .field("HANDSHAKE_DONE", &self.handshake_done) +134 .field("IMMEDIATE_ACK", &self.immediate_ack) +135 .field("MAX_DATA", &self.max_data) +136 .field("MAX_PATH_ID", &self.max_path_id) +137 .field("MAX_STREAM_DATA", &self.max_stream_data) +138 .field("MAX_STREAMS_BIDI", &self.max_streams_bidi) +139 .field("MAX_STREAMS_UNI", &self.max_streams_uni) +140 .field("NEW_CONNECTION_ID", &self.new_connection_id) +141 .field("NEW_TOKEN", &self.new_token) +142 .field("PATHS_BLOCKED", &self.paths_blocked) +143 .field("PATH_ABANDON", &self.path_abandon) +144 .field("PATH_ACK", &self.path_acks) +145 .field("PATH_STATUS_AVAILABLE", &self.path_status_available) +146 .field("PATH_STATUS_BACKUP", &self.path_status_backup) +147 .field("PATH_CHALLENGE", &self.path_challenge) +148 .field("PATH_CIDS_BLOCKED", &self.path_cids_blocked) +149 .field("PATH_NEW_CONNECTION_ID", &self.path_new_connection_id) +150 .field("PATH_RESPONSE", &self.path_response) +151 .field("PATH_RETIRE_CONNECTION_ID", &self.path_retire_connection_id) +152 .field("PING", &self.ping) +153 .field("RESET_STREAM", &self.reset_stream) +154 .field("RETIRE_CONNECTION_ID", &self.retire_connection_id) +155 .field("STREAM_DATA_BLOCKED", &self.stream_data_blocked) +156 .field("STREAMS_BLOCKED_BIDI", &self.streams_blocked_bidi) +157 .field("STREAMS_BLOCKED_UNI", &self.streams_blocked_uni) +158 .field("STOP_SENDING", &self.stop_sending) +159 .field("STREAM", &self.stream) +160 .finish() +161 } +162} +163 +164/// Statistics related to a transmission path +165#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +166#[non_exhaustive] +167pub struct PathStats { +168 /// Current best estimate of this connection's latency (round-trip-time) +169 pub rtt: Duration, +170 /// Current congestion window of the connection +171 pub cwnd: u64, +172 /// Congestion events on the connection +173 pub congestion_events: u64, +174 /// The amount of packets lost on this path +175 pub lost_packets: u64, +176 /// The amount of bytes lost on this path +177 pub lost_bytes: u64, +178 /// The amount of packets sent on this path +179 pub sent_packets: u64, +180 /// The amount of PLPMTUD probe packets sent on this path (also counted by `sent_packets`) +181 pub sent_plpmtud_probes: u64, +182 /// The amount of PLPMTUD probe packets lost on this path (ignored by `lost_packets` and +183 /// `lost_bytes`) +184 pub lost_plpmtud_probes: u64, +185 /// The number of times a black hole was detected in the path +186 pub black_holes_detected: u64, +187 /// Largest UDP payload size the path currently supports +188 pub current_mtu: u16, +189} +190 +191/// Connection statistics +192#[derive(Debug, Default, Clone)] +193#[non_exhaustive] +194pub struct ConnectionStats { +195 /// Statistics about UDP datagrams transmitted on a connection +196 pub udp_tx: UdpStats, +197 /// Statistics about UDP datagrams received on a connection +198 pub udp_rx: UdpStats, +199 /// Statistics about frames transmitted on a connection +200 pub frame_tx: FrameStats, +201 /// Statistics about frames received on a connection +202 pub frame_rx: FrameStats, +203} \ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html index 3764c8cd9..44a9bbfb2 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/streams/mod.rs.html @@ -7,7 +7,7 @@ 7use thiserror::Error; 8use tracing::trace; 9 -10use super::spaces::{Retransmits, ThinRetransmits}; +10use super::spaces::Retransmits; 11use crate::{ 12 Dir, StreamId, VarInt, 13 connection::streams::state::{get_or_insert_recv, get_or_insert_send}, diff --git a/pr/323/docs/src/iroh_quinn_proto/connection/streams/state.rs.html b/pr/323/docs/src/iroh_quinn_proto/connection/streams/state.rs.html index 5ad3dee95..dce69a065 100644 --- a/pr/323/docs/src/iroh_quinn_proto/connection/streams/state.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/connection/streams/state.rs.html @@ -4,2096 +4,2047 @@ 4 mem, 5}; 6 -7use bytes::BufMut; -8use rustc_hash::FxHashMap; -9use tracing::{debug, trace}; -10 -11use super::{ -12 PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent, -13 StreamHalf, ThinRetransmits, -14}; -15use crate::{ -16 Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt, -17 coding::{BufMutExt, Encodable}, -18 connection::{qlog::QlogSentPacket, stats::FrameStats}, -19 frame::{self, Frame, FrameStruct, StreamMetaVec}, -20 transport_parameters::TransportParameters, -21}; -22 -23/// Wrapper around `Recv` that facilitates reusing `Recv` instances -24#[derive(Debug)] -25pub(super) enum StreamRecv { -26 /// A `Recv` that is ready to be opened -27 Free(Box<Recv>), -28 /// A `Recv` that has been opened -29 Open(Box<Recv>), -30} -31 -32impl StreamRecv { -33 /// Returns a reference to the inner `Recv` if the stream is open -34 pub(super) fn as_open_recv(&self) -> Option<&Recv> { -35 match self { -36 Self::Open(r) => Some(r), -37 _ => None, -38 } -39 } -40 -41 // Returns a mutable reference to the inner `Recv` if the stream is open -42 pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> { -43 match self { -44 Self::Open(r) => Some(r), -45 _ => None, -46 } -47 } -48 -49 // Returns the inner `Recv` -50 pub(super) fn into_inner(self) -> Box<Recv> { -51 match self { -52 Self::Free(r) | Self::Open(r) => r, -53 } -54 } -55 -56 // Reinitialize the stream so the inner `Recv` can be reused -57 pub(super) fn free(self, initial_max_data: u64) -> Self { -58 match self { -59 Self::Free(_) => unreachable!("Self::Free on reinit()"), -60 Self::Open(mut recv) => { -61 recv.reinit(initial_max_data); -62 Self::Free(recv) -63 } -64 } -65 } -66} -67 -68#[allow(unreachable_pub)] // fuzzing only -69pub struct StreamsState { -70 pub(super) side: Side, -71 // Set of streams that are currently open, or could be immediately opened by the peer -72 pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>, -73 pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>, -74 pub(super) free_recv: Vec<StreamRecv>, -75 pub(super) next: [u64; 2], -76 /// Maximum number of locally-initiated streams that may be opened over the lifetime of the -77 /// connection so far, per direction -78 pub(super) max: [u64; 2], -79 /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the -80 /// connection so far, per direction -81 pub(super) max_remote: [u64; 2], -82 /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame -83 sent_max_remote: [u64; 2], -84 /// Number of streams that we've given the peer permission to open and which aren't fully closed -85 pub(super) allocated_remote_count: [u64; 2], -86 /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count` -87 /// due to `set_max_concurrent` calls. -88 max_concurrent_remote_count: [u64; 2], -89 /// Whether `max_concurrent_remote_count` has ever changed -90 flow_control_adjusted: bool, -91 /// Lowest remotely-initiated stream index that haven't actually been opened by the peer -92 pub(super) next_remote: [u64; 2], -93 /// Whether the remote endpoint has opened any streams the application doesn't know about yet, -94 /// per directionality -95 opened: [bool; 2], -96 // Next to report to the application, once opened -97 pub(super) next_reported_remote: [u64; 2], -98 /// Number of outbound streams -99 /// -100 /// This differs from `self.send.len()` in that it does not include streams that the peer is -101 /// permitted to open but which have not yet been opened. -102 pub(super) send_streams: usize, -103 /// Streams with outgoing data queued, sorted by priority -104 pub(super) pending: PendingStreamsQueue, -105 -106 events: VecDeque<StreamEvent>, -107 /// Streams blocked on connection-level flow control or stream window space -108 /// -109 /// Streams are only added to this list when a write fails. -110 pub(super) connection_blocked: Vec<StreamId>, -111 /// Connection-level flow control budget dictated by the peer -112 pub(super) max_data: u64, -113 /// The initial receive window -114 receive_window: u64, -115 /// Limit on incoming data, which is transmitted through `MAX_DATA` frames -116 local_max_data: u64, -117 /// The last value of `MAX_DATA` which had been queued for transmission in -118 /// an outgoing `MAX_DATA` frame -119 sent_max_data: VarInt, -120 /// Sum of current offsets of all send streams. -121 pub(super) data_sent: u64, -122 /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound. -123 data_recvd: u64, -124 /// Total quantity of unacknowledged outgoing data -125 pub(super) unacked_data: u64, -126 /// Configured upper bound for `unacked_data`. -127 /// -128 /// Note this may be less than `unacked_data` if the user has set a new value. -129 pub(super) send_window: u64, -130 /// Configured upper bound for how much unacked data the peer can send us per stream -131 pub(super) stream_receive_window: u64, -132 -133 // Pertinent state from the TransportParameters supplied by the peer -134 initial_max_stream_data_uni: VarInt, -135 initial_max_stream_data_bidi_local: VarInt, -136 initial_max_stream_data_bidi_remote: VarInt, -137 -138 /// The shrink to be applied to local_max_data when receive_window is shrunk -139 receive_window_shrink_debt: u64, -140} -141 -142impl StreamsState { -143 #[allow(unreachable_pub)] // fuzzing only -144 pub fn new( -145 side: Side, -146 max_remote_uni: VarInt, -147 max_remote_bi: VarInt, -148 send_window: u64, -149 receive_window: VarInt, -150 stream_receive_window: VarInt, -151 ) -> Self { -152 let mut this = Self { -153 side, -154 send: FxHashMap::default(), -155 recv: FxHashMap::default(), -156 free_recv: Vec::new(), -157 next: [0, 0], -158 max: [0, 0], -159 max_remote: [max_remote_bi.into(), max_remote_uni.into()], -160 sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()], -161 allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()], -162 max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()], -163 flow_control_adjusted: false, -164 next_remote: [0, 0], -165 opened: [false, false], -166 next_reported_remote: [0, 0], -167 send_streams: 0, -168 pending: PendingStreamsQueue::new(), -169 events: VecDeque::new(), -170 connection_blocked: Vec::new(), -171 max_data: 0, -172 receive_window: receive_window.into(), -173 local_max_data: receive_window.into(), -174 sent_max_data: receive_window, -175 data_sent: 0, -176 data_recvd: 0, -177 unacked_data: 0, -178 send_window, -179 stream_receive_window: stream_receive_window.into(), -180 initial_max_stream_data_uni: 0u32.into(), -181 initial_max_stream_data_bidi_local: 0u32.into(), -182 initial_max_stream_data_bidi_remote: 0u32.into(), -183 receive_window_shrink_debt: 0, -184 }; -185 -186 for dir in Dir::iter() { -187 for i in 0..this.max_remote[dir as usize] { -188 this.insert(true, StreamId::new(!side, dir, i)); -189 } -190 } -191 -192 this -193 } -194 -195 pub(crate) fn set_params(&mut self, params: &TransportParameters) { -196 self.initial_max_stream_data_uni = params.initial_max_stream_data_uni; -197 self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local; -198 self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote; -199 self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into(); -200 self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into(); -201 self.received_max_data(params.initial_max_data); -202 for i in 0..self.max_remote[Dir::Bi as usize] { -203 let id = StreamId::new(!self.side, Dir::Bi, i); -204 if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) { -205 s.max_data = params.initial_max_stream_data_bidi_local.into(); -206 } -207 } -208 } -209 -210 /// Ensure we have space for at least a full flow control window of remotely-initiated streams -211 /// to be open, and notify the peer if the window has moved -212 fn ensure_remote_streams(&mut self, dir: Dir) { -213 let new_count = self.max_concurrent_remote_count[dir as usize] -214 .saturating_sub(self.allocated_remote_count[dir as usize]); -215 for i in 0..new_count { -216 let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i); -217 self.insert(true, id); -218 } -219 self.allocated_remote_count[dir as usize] += new_count; -220 self.max_remote[dir as usize] += new_count; -221 } -222 -223 pub(crate) fn zero_rtt_rejected(&mut self) { -224 // Revert to initial state for outgoing streams -225 for dir in Dir::iter() { -226 for i in 0..self.next[dir as usize] { -227 // We don't bother calling `stream_freed` here because we explicitly reset affected -228 // counters below. -229 let id = StreamId::new(self.side, dir, i); -230 self.send.remove(&id).unwrap(); -231 if let Dir::Bi = dir { -232 self.recv.remove(&id).unwrap(); -233 } -234 } -235 self.next[dir as usize] = 0; -236 -237 // If 0-RTT was rejected, any flow control frames we sent were lost. -238 if self.flow_control_adjusted { -239 // Conservative approximation of whatever we sent in transport parameters -240 self.sent_max_remote[dir as usize] = 0; -241 } -242 } -243 -244 self.pending.clear(); -245 self.send_streams = 0; -246 self.data_sent = 0; -247 self.connection_blocked.clear(); -248 } -249 -250 /// Process incoming stream frame -251 /// -252 /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted -253 pub(crate) fn received( -254 &mut self, -255 frame: frame::Stream, -256 payload_len: usize, -257 ) -> Result<ShouldTransmit, TransportError> { -258 let id = frame.id; -259 self.validate_receive_id(id).inspect_err(|_e| { -260 debug!("received illegal STREAM frame"); -261 })?; -262 -263 let rs = match self -264 .recv -265 .get_mut(&id) -266 .map(get_or_insert_recv(self.stream_receive_window)) -267 { -268 Some(rs) => rs, -269 None => { -270 trace!("dropping frame for closed stream"); -271 return Ok(ShouldTransmit(false)); -272 } -273 }; -274 -275 if !rs.is_receiving() { -276 trace!("dropping frame for finished stream"); -277 return Ok(ShouldTransmit(false)); -278 } -279 -280 let (new_bytes, closed) = -281 rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?; -282 self.data_recvd = self.data_recvd.saturating_add(new_bytes); -283 -284 if !rs.stopped { -285 self.on_stream_frame(true, id); -286 return Ok(ShouldTransmit(false)); -287 } -288 -289 // Stopped streams become closed instantly on FIN, so check whether we need to clean up -290 if closed { -291 let rs = self.recv.remove(&id).flatten().unwrap(); -292 self.stream_recv_freed(id, rs); -293 } -294 -295 // We don't buffer data on stopped streams, so issue flow control credit immediately -296 Ok(self.add_read_credits(new_bytes)) -297 } -298 -299 /// Process incoming RESET_STREAM frame -300 /// -301 /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted -302 #[allow(unreachable_pub)] // fuzzing only -303 pub fn received_reset( -304 &mut self, -305 frame: frame::ResetStream, -306 ) -> Result<ShouldTransmit, TransportError> { -307 let frame::ResetStream { -308 id, -309 error_code, -310 final_offset, -311 } = frame; -312 self.validate_receive_id(id).inspect_err(|_e| { -313 debug!("received illegal RESET_STREAM frame"); -314 })?; -315 -316 let rs = match self -317 .recv -318 .get_mut(&id) -319 .map(get_or_insert_recv(self.stream_receive_window)) -320 { -321 Some(stream) => stream, -322 None => { -323 trace!("received RESET_STREAM on closed stream"); -324 return Ok(ShouldTransmit(false)); -325 } -326 }; -327 -328 // State transition -329 if !rs.reset( -330 error_code, -331 final_offset, -332 self.data_recvd, -333 self.local_max_data, -334 )? { -335 // Redundant reset -336 return Ok(ShouldTransmit(false)); -337 } -338 let bytes_read = rs.assembler.bytes_read(); -339 let stopped = rs.stopped; -340 let end = rs.end; -341 if stopped { -342 // Stopped streams should be disposed immediately on reset -343 let rs = self.recv.remove(&id).flatten().unwrap(); -344 self.stream_recv_freed(id, rs); -345 } -346 self.on_stream_frame(!stopped, id); -347 -348 // Update connection-level flow control -349 Ok(if bytes_read != final_offset.into_inner() { -350 // bytes_read is always <= end, so this won't underflow. -351 self.data_recvd = self -352 .data_recvd -353 .saturating_add(u64::from(final_offset) - end); -354 self.add_read_credits(u64::from(final_offset) - bytes_read) -355 } else { -356 ShouldTransmit(false) -357 }) -358 } -359 -360 /// Process incoming `STOP_SENDING` frame -361 #[allow(unreachable_pub)] // fuzzing only -362 pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) { -363 let max_send_data = self.max_send_data(id); -364 let stream = match self -365 .send -366 .get_mut(&id) -367 .map(get_or_insert_send(max_send_data)) -368 { -369 Some(ss) => ss, -370 None => return, -371 }; -372 -373 if stream.try_stop(error_code) { -374 self.events -375 .push_back(StreamEvent::Stopped { id, error_code }); -376 self.on_stream_frame(false, id); -377 } -378 } -379 -380 pub(crate) fn reset_acked(&mut self, id: StreamId) { -381 match self.send.entry(id) { -382 hash_map::Entry::Vacant(_) => {} -383 hash_map::Entry::Occupied(e) => { -384 if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) { -385 e.remove_entry(); -386 self.stream_freed(id, StreamHalf::Send); -387 } -388 } -389 } -390 } -391 -392 /// Whether any stream data is queued, regardless of control frames -393 pub(crate) fn can_send_stream_data(&self) -> bool { -394 // Reset streams may linger in the pending stream list, but will never produce stream frames -395 self.pending.iter().any(|stream| { -396 self.send -397 .get(&stream.id) -398 .and_then(|s| s.as_ref()) -399 .is_some_and(|s| !s.is_reset()) -400 }) -401 } -402 -403 /// Whether MAX_STREAM_DATA frames could be sent for stream `id` -404 pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool { -405 self.recv -406 .get(&id) -407 .and_then(|s| s.as_ref()) -408 .and_then(|s| s.as_open_recv()) -409 .is_some_and(|s| s.can_send_flow_control()) -410 } -411 -412 pub(in crate::connection) fn write_control_frames( -413 &mut self, -414 buf: &mut impl BufMut, -415 pending: &mut Retransmits, -416 retransmits: &mut ThinRetransmits, -417 stats: &mut FrameStats, -418 #[allow(unused)] qlog: &mut QlogSentPacket, -419 ) { -420 // RESET_STREAM -421 while buf.remaining_mut() > frame::ResetStream::SIZE_BOUND { -422 let (id, error_code) = match pending.reset_stream.pop() { +7use rustc_hash::FxHashMap; +8use tracing::{debug, trace}; +9 +10use super::{ +11 PendingStreamsQueue, Recv, Retransmits, Send, SendState, ShouldTransmit, StreamEvent, +12 StreamHalf, +13}; +14use crate::{ +15 Dir, MAX_STREAM_COUNT, Side, StreamId, TransportError, VarInt, +16 connection::{PacketBuilder, stats::FrameStats}, +17 frame::{self, FrameStruct}, +18 transport_parameters::TransportParameters, +19}; +20 +21/// Wrapper around `Recv` that facilitates reusing `Recv` instances +22#[derive(Debug)] +23pub(super) enum StreamRecv { +24 /// A `Recv` that is ready to be opened +25 Free(Box<Recv>), +26 /// A `Recv` that has been opened +27 Open(Box<Recv>), +28} +29 +30impl StreamRecv { +31 /// Returns a reference to the inner `Recv` if the stream is open +32 pub(super) fn as_open_recv(&self) -> Option<&Recv> { +33 match self { +34 Self::Open(r) => Some(r), +35 _ => None, +36 } +37 } +38 +39 // Returns a mutable reference to the inner `Recv` if the stream is open +40 pub(super) fn as_open_recv_mut(&mut self) -> Option<&mut Recv> { +41 match self { +42 Self::Open(r) => Some(r), +43 _ => None, +44 } +45 } +46 +47 // Returns the inner `Recv` +48 pub(super) fn into_inner(self) -> Box<Recv> { +49 match self { +50 Self::Free(r) | Self::Open(r) => r, +51 } +52 } +53 +54 // Reinitialize the stream so the inner `Recv` can be reused +55 pub(super) fn free(self, initial_max_data: u64) -> Self { +56 match self { +57 Self::Free(_) => unreachable!("Self::Free on reinit()"), +58 Self::Open(mut recv) => { +59 recv.reinit(initial_max_data); +60 Self::Free(recv) +61 } +62 } +63 } +64} +65 +66#[allow(unreachable_pub)] // fuzzing only +67pub struct StreamsState { +68 pub(super) side: Side, +69 // Set of streams that are currently open, or could be immediately opened by the peer +70 pub(super) send: FxHashMap<StreamId, Option<Box<Send>>>, +71 pub(super) recv: FxHashMap<StreamId, Option<StreamRecv>>, +72 pub(super) free_recv: Vec<StreamRecv>, +73 pub(super) next: [u64; 2], +74 /// Maximum number of locally-initiated streams that may be opened over the lifetime of the +75 /// connection so far, per direction +76 pub(super) max: [u64; 2], +77 /// Maximum number of remotely-initiated streams that may be opened over the lifetime of the +78 /// connection so far, per direction +79 pub(super) max_remote: [u64; 2], +80 /// Value of `max_remote` most recently transmitted to the peer in a `MAX_STREAMS` frame +81 sent_max_remote: [u64; 2], +82 /// Number of streams that we've given the peer permission to open and which aren't fully closed +83 pub(super) allocated_remote_count: [u64; 2], +84 /// Size of the desired stream flow control window. May be smaller than `allocated_remote_count` +85 /// due to `set_max_concurrent` calls. +86 max_concurrent_remote_count: [u64; 2], +87 /// Whether `max_concurrent_remote_count` has ever changed +88 flow_control_adjusted: bool, +89 /// Lowest remotely-initiated stream index that haven't actually been opened by the peer +90 pub(super) next_remote: [u64; 2], +91 /// Whether the remote endpoint has opened any streams the application doesn't know about yet, +92 /// per directionality +93 opened: [bool; 2], +94 // Next to report to the application, once opened +95 pub(super) next_reported_remote: [u64; 2], +96 /// Number of outbound streams +97 /// +98 /// This differs from `self.send.len()` in that it does not include streams that the peer is +99 /// permitted to open but which have not yet been opened. +100 pub(super) send_streams: usize, +101 /// Streams with outgoing data queued, sorted by priority +102 pub(super) pending: PendingStreamsQueue, +103 +104 events: VecDeque<StreamEvent>, +105 /// Streams blocked on connection-level flow control or stream window space +106 /// +107 /// Streams are only added to this list when a write fails. +108 pub(super) connection_blocked: Vec<StreamId>, +109 /// Connection-level flow control budget dictated by the peer +110 pub(super) max_data: u64, +111 /// The initial receive window +112 receive_window: u64, +113 /// Limit on incoming data, which is transmitted through `MAX_DATA` frames +114 local_max_data: u64, +115 /// The last value of `MAX_DATA` which had been queued for transmission in +116 /// an outgoing `MAX_DATA` frame +117 sent_max_data: VarInt, +118 /// Sum of current offsets of all send streams. +119 pub(super) data_sent: u64, +120 /// Sum of end offsets of all receive streams. Includes gaps, so it's an upper bound. +121 data_recvd: u64, +122 /// Total quantity of unacknowledged outgoing data +123 pub(super) unacked_data: u64, +124 /// Configured upper bound for `unacked_data`. +125 /// +126 /// Note this may be less than `unacked_data` if the user has set a new value. +127 pub(super) send_window: u64, +128 /// Configured upper bound for how much unacked data the peer can send us per stream +129 pub(super) stream_receive_window: u64, +130 +131 // Pertinent state from the TransportParameters supplied by the peer +132 initial_max_stream_data_uni: VarInt, +133 initial_max_stream_data_bidi_local: VarInt, +134 initial_max_stream_data_bidi_remote: VarInt, +135 +136 /// The shrink to be applied to local_max_data when receive_window is shrunk +137 receive_window_shrink_debt: u64, +138} +139 +140impl StreamsState { +141 #[allow(unreachable_pub)] // fuzzing only +142 pub fn new( +143 side: Side, +144 max_remote_uni: VarInt, +145 max_remote_bi: VarInt, +146 send_window: u64, +147 receive_window: VarInt, +148 stream_receive_window: VarInt, +149 ) -> Self { +150 let mut this = Self { +151 side, +152 send: FxHashMap::default(), +153 recv: FxHashMap::default(), +154 free_recv: Vec::new(), +155 next: [0, 0], +156 max: [0, 0], +157 max_remote: [max_remote_bi.into(), max_remote_uni.into()], +158 sent_max_remote: [max_remote_bi.into(), max_remote_uni.into()], +159 allocated_remote_count: [max_remote_bi.into(), max_remote_uni.into()], +160 max_concurrent_remote_count: [max_remote_bi.into(), max_remote_uni.into()], +161 flow_control_adjusted: false, +162 next_remote: [0, 0], +163 opened: [false, false], +164 next_reported_remote: [0, 0], +165 send_streams: 0, +166 pending: PendingStreamsQueue::new(), +167 events: VecDeque::new(), +168 connection_blocked: Vec::new(), +169 max_data: 0, +170 receive_window: receive_window.into(), +171 local_max_data: receive_window.into(), +172 sent_max_data: receive_window, +173 data_sent: 0, +174 data_recvd: 0, +175 unacked_data: 0, +176 send_window, +177 stream_receive_window: stream_receive_window.into(), +178 initial_max_stream_data_uni: 0u32.into(), +179 initial_max_stream_data_bidi_local: 0u32.into(), +180 initial_max_stream_data_bidi_remote: 0u32.into(), +181 receive_window_shrink_debt: 0, +182 }; +183 +184 for dir in Dir::iter() { +185 for i in 0..this.max_remote[dir as usize] { +186 this.insert(true, StreamId::new(!side, dir, i)); +187 } +188 } +189 +190 this +191 } +192 +193 pub(crate) fn set_params(&mut self, params: &TransportParameters) { +194 self.initial_max_stream_data_uni = params.initial_max_stream_data_uni; +195 self.initial_max_stream_data_bidi_local = params.initial_max_stream_data_bidi_local; +196 self.initial_max_stream_data_bidi_remote = params.initial_max_stream_data_bidi_remote; +197 self.max[Dir::Bi as usize] = params.initial_max_streams_bidi.into(); +198 self.max[Dir::Uni as usize] = params.initial_max_streams_uni.into(); +199 self.received_max_data(params.initial_max_data); +200 for i in 0..self.max_remote[Dir::Bi as usize] { +201 let id = StreamId::new(!self.side, Dir::Bi, i); +202 if let Some(s) = self.send.get_mut(&id).and_then(|s| s.as_mut()) { +203 s.max_data = params.initial_max_stream_data_bidi_local.into(); +204 } +205 } +206 } +207 +208 /// Ensure we have space for at least a full flow control window of remotely-initiated streams +209 /// to be open, and notify the peer if the window has moved +210 fn ensure_remote_streams(&mut self, dir: Dir) { +211 let new_count = self.max_concurrent_remote_count[dir as usize] +212 .saturating_sub(self.allocated_remote_count[dir as usize]); +213 for i in 0..new_count { +214 let id = StreamId::new(!self.side, dir, self.max_remote[dir as usize] + i); +215 self.insert(true, id); +216 } +217 self.allocated_remote_count[dir as usize] += new_count; +218 self.max_remote[dir as usize] += new_count; +219 } +220 +221 pub(crate) fn zero_rtt_rejected(&mut self) { +222 // Revert to initial state for outgoing streams +223 for dir in Dir::iter() { +224 for i in 0..self.next[dir as usize] { +225 // We don't bother calling `stream_freed` here because we explicitly reset affected +226 // counters below. +227 let id = StreamId::new(self.side, dir, i); +228 self.send.remove(&id).unwrap(); +229 if let Dir::Bi = dir { +230 self.recv.remove(&id).unwrap(); +231 } +232 } +233 self.next[dir as usize] = 0; +234 +235 // If 0-RTT was rejected, any flow control frames we sent were lost. +236 if self.flow_control_adjusted { +237 // Conservative approximation of whatever we sent in transport parameters +238 self.sent_max_remote[dir as usize] = 0; +239 } +240 } +241 +242 self.pending.clear(); +243 self.send_streams = 0; +244 self.data_sent = 0; +245 self.connection_blocked.clear(); +246 } +247 +248 /// Process incoming stream frame +249 /// +250 /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted +251 pub(crate) fn received( +252 &mut self, +253 frame: frame::Stream, +254 payload_len: usize, +255 ) -> Result<ShouldTransmit, TransportError> { +256 let id = frame.id; +257 self.validate_receive_id(id).inspect_err(|_e| { +258 debug!("received illegal STREAM frame"); +259 })?; +260 +261 let rs = match self +262 .recv +263 .get_mut(&id) +264 .map(get_or_insert_recv(self.stream_receive_window)) +265 { +266 Some(rs) => rs, +267 None => { +268 trace!("dropping frame for closed stream"); +269 return Ok(ShouldTransmit(false)); +270 } +271 }; +272 +273 if !rs.is_receiving() { +274 trace!("dropping frame for finished stream"); +275 return Ok(ShouldTransmit(false)); +276 } +277 +278 let (new_bytes, closed) = +279 rs.ingest(frame, payload_len, self.data_recvd, self.local_max_data)?; +280 self.data_recvd = self.data_recvd.saturating_add(new_bytes); +281 +282 if !rs.stopped { +283 self.on_stream_frame(true, id); +284 return Ok(ShouldTransmit(false)); +285 } +286 +287 // Stopped streams become closed instantly on FIN, so check whether we need to clean up +288 if closed { +289 let rs = self.recv.remove(&id).flatten().unwrap(); +290 self.stream_recv_freed(id, rs); +291 } +292 +293 // We don't buffer data on stopped streams, so issue flow control credit immediately +294 Ok(self.add_read_credits(new_bytes)) +295 } +296 +297 /// Process incoming RESET_STREAM frame +298 /// +299 /// If successful, returns whether a `MAX_DATA` frame needs to be transmitted +300 #[allow(unreachable_pub)] // fuzzing only +301 pub fn received_reset( +302 &mut self, +303 frame: frame::ResetStream, +304 ) -> Result<ShouldTransmit, TransportError> { +305 let frame::ResetStream { +306 id, +307 error_code, +308 final_offset, +309 } = frame; +310 self.validate_receive_id(id).inspect_err(|_e| { +311 debug!("received illegal RESET_STREAM frame"); +312 })?; +313 +314 let rs = match self +315 .recv +316 .get_mut(&id) +317 .map(get_or_insert_recv(self.stream_receive_window)) +318 { +319 Some(stream) => stream, +320 None => { +321 trace!("received RESET_STREAM on closed stream"); +322 return Ok(ShouldTransmit(false)); +323 } +324 }; +325 +326 // State transition +327 if !rs.reset( +328 error_code, +329 final_offset, +330 self.data_recvd, +331 self.local_max_data, +332 )? { +333 // Redundant reset +334 return Ok(ShouldTransmit(false)); +335 } +336 let bytes_read = rs.assembler.bytes_read(); +337 let stopped = rs.stopped; +338 let end = rs.end; +339 if stopped { +340 // Stopped streams should be disposed immediately on reset +341 let rs = self.recv.remove(&id).flatten().unwrap(); +342 self.stream_recv_freed(id, rs); +343 } +344 self.on_stream_frame(!stopped, id); +345 +346 // Update connection-level flow control +347 Ok(if bytes_read != final_offset.into_inner() { +348 // bytes_read is always <= end, so this won't underflow. +349 self.data_recvd = self +350 .data_recvd +351 .saturating_add(u64::from(final_offset) - end); +352 self.add_read_credits(u64::from(final_offset) - bytes_read) +353 } else { +354 ShouldTransmit(false) +355 }) +356 } +357 +358 /// Process incoming `STOP_SENDING` frame +359 #[allow(unreachable_pub)] // fuzzing only +360 pub fn received_stop_sending(&mut self, id: StreamId, error_code: VarInt) { +361 let max_send_data = self.max_send_data(id); +362 let stream = match self +363 .send +364 .get_mut(&id) +365 .map(get_or_insert_send(max_send_data)) +366 { +367 Some(ss) => ss, +368 None => return, +369 }; +370 +371 if stream.try_stop(error_code) { +372 self.events +373 .push_back(StreamEvent::Stopped { id, error_code }); +374 self.on_stream_frame(false, id); +375 } +376 } +377 +378 pub(crate) fn reset_acked(&mut self, id: StreamId) { +379 match self.send.entry(id) { +380 hash_map::Entry::Vacant(_) => {} +381 hash_map::Entry::Occupied(e) => { +382 if let Some(SendState::ResetSent) = e.get().as_ref().map(|s| s.state) { +383 e.remove_entry(); +384 self.stream_freed(id, StreamHalf::Send); +385 } +386 } +387 } +388 } +389 +390 /// Whether any stream data is queued, regardless of control frames +391 pub(crate) fn can_send_stream_data(&self) -> bool { +392 // Reset streams may linger in the pending stream list, but will never produce stream frames +393 self.pending.iter().any(|stream| { +394 self.send +395 .get(&stream.id) +396 .and_then(|s| s.as_ref()) +397 .is_some_and(|s| !s.is_reset()) +398 }) +399 } +400 +401 /// Whether MAX_STREAM_DATA frames could be sent for stream `id` +402 pub(crate) fn can_send_flow_control(&self, id: StreamId) -> bool { +403 self.recv +404 .get(&id) +405 .and_then(|s| s.as_ref()) +406 .and_then(|s| s.as_open_recv()) +407 .is_some_and(|s| s.can_send_flow_control()) +408 } +409 +410 pub(in crate::connection) fn write_control_frames<'a, 'b>( +411 &mut self, +412 builder: &mut PacketBuilder<'a, 'b>, +413 pending: &mut Retransmits, +414 stats: &mut FrameStats, +415 ) { +416 // RESET_STREAM +417 while builder.frame_space_remaining() > frame::ResetStream::SIZE_BOUND { +418 let (id, error_code) = match pending.reset_stream.pop() { +419 Some(x) => x, +420 None => break, +421 }; +422 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { 423 Some(x) => x, -424 None => break, +424 None => continue, 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"), +426 let frame = frame::ResetStream { +427 id, +428 error_code, +429 final_offset: VarInt::try_from(stream.offset()).expect("impossibly large offset"), +430 }; +431 builder.write_frame(frame, stats); +432 } +433 +434 // STOP_SENDING +435 while builder.frame_space_remaining() > frame::StopSending::SIZE_BOUND { +436 let frame = match pending.stop_sending.pop() { +437 Some(x) => x, +438 None => break, 439 }; -440 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; +440 // We may need to transmit STOP_SENDING even for streams whose state we have discarded, +441 // because we are able to discard local state for stopped streams immediately upon +442 // receiving FIN, even if the peer still has arbitrarily large amounts of data to +443 // (re)transmit due to loss or unconventional sending strategy. We could fine-tune this +444 // a little by dropping the frame if we specifically know the stream's been reset by the +445 // peer, but we discard that information as soon as the application consumes it, so it +446 // can't be relied upon regardless. +447 builder.write_frame(frame, stats); +448 } +449 +450 // MAX_DATA +451 if pending.max_data && builder.frame_space_remaining() > 9 { +452 pending.max_data = false; +453 +454 // `local_max_data` can grow bigger than `VarInt`. +455 // For transmission inside QUIC frames we need to clamp it to the +456 // maximum allowed `VarInt` size. +457 let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX); +458 +459 if max > self.sent_max_data { +460 // Record that a `MAX_DATA` announcing a certain window was sent. This will +461 // suppress enqueuing further `MAX_DATA` frames unless either the previous +462 // transmission was not acknowledged or the window further increased. +463 self.sent_max_data = max; +464 } +465 +466 builder.write_frame(frame::MaxData(max), stats); +467 } 468 -469 // `local_max_data` can grow bigger than `VarInt`. -470 // For transmission inside QUIC frames we need to clamp it to the -471 // maximum allowed `VarInt` size. -472 let max = VarInt::try_from(self.local_max_data).unwrap_or(VarInt::MAX); -473 -474 trace!(value = max.into_inner(), "MAX_DATA"); -475 if max > self.sent_max_data { -476 // Record that a `MAX_DATA` announcing a certain window was sent. This will -477 // suppress enqueuing further `MAX_DATA` frames unless either the previous -478 // transmission was not acknowledged or the window further increased. -479 self.sent_max_data = max; -480 } -481 -482 retransmits.get_or_create().max_data = true; -483 buf.write(frame::FrameType::MaxData); -484 buf.write(max); -485 qlog.frame(&Frame::MaxData(max)); -486 stats.max_data += 1; -487 } +469 // MAX_STREAM_DATA +470 while builder.frame_space_remaining() > 17 { +471 let id = match pending.max_stream_data.iter().next() { +472 Some(x) => *x, +473 None => break, +474 }; +475 pending.max_stream_data.remove(&id); +476 let rs = match self +477 .recv +478 .get_mut(&id) +479 .and_then(|s| s.as_mut()) +480 .and_then(|s| s.as_open_recv_mut()) +481 { +482 Some(x) => x, +483 None => continue, +484 }; +485 if !rs.can_send_flow_control() { +486 continue; +487 } 488 -489 // MAX_STREAM_DATA -490 while buf.remaining_mut() > 17 { -491 let id = match pending.max_stream_data.iter().next() { -492 Some(x) => *x, -493 None => break, -494 }; -495 pending.max_stream_data.remove(&id); -496 let rs = match self -497 .recv -498 .get_mut(&id) -499 .and_then(|s| s.as_mut()) -500 .and_then(|s| s.as_open_recv_mut()) -501 { -502 Some(x) => x, -503 None => continue, -504 }; -505 if !rs.can_send_flow_control() { -506 continue; -507 } -508 retransmits.get_or_create().max_stream_data.insert(id); -509 -510 let (max, _) = rs.max_stream_data(self.stream_receive_window); -511 rs.record_sent_max_stream_data(max); -512 -513 trace!(stream = %id, max = max, "MAX_STREAM_DATA"); -514 buf.write(frame::FrameType::MaxStreamData); -515 buf.write(id); -516 buf.write_var(max); -517 qlog.frame(&Frame::MaxStreamData { id, offset: max }); -518 stats.max_stream_data += 1; -519 } +489 let (max, _) = rs.max_stream_data(self.stream_receive_window); +490 rs.record_sent_max_stream_data(max); +491 builder.write_frame(frame::MaxStreamData { id, offset: max }, stats); +492 } +493 +494 // MAX_STREAMS +495 for dir in Dir::iter() { +496 if !pending.max_stream_id[dir as usize] || builder.frame_space_remaining() <= 9 { +497 continue; +498 } +499 +500 pending.max_stream_id[dir as usize] = false; +501 self.sent_max_remote[dir as usize] = self.max_remote[dir as usize]; +502 let count = self.max_remote[dir as usize]; +503 builder.write_frame(frame::MaxStreams { dir, count }, stats); +504 } +505 } +506 +507 pub(in crate::connection) fn write_stream_frames<'a, 'b>( +508 &mut self, +509 builder: &mut PacketBuilder<'a, 'b>, +510 fair: bool, +511 stats: &mut FrameStats, +512 ) { +513 while builder.frame_space_remaining() > frame::Stream::SIZE_BOUND { +514 // Pop the stream of the highest priority that currently has pending data. If +515 // the stream still has some pending data left after writing, it will be +516 // reinserted, otherwise not +517 let Some(stream) = self.pending.pop() else { +518 break; +519 }; 520 -521 // MAX_STREAMS -522 for dir in Dir::iter() { -523 if !pending.max_stream_id[dir as usize] || buf.remaining_mut() <= 9 { -524 continue; -525 } -526 -527 pending.max_stream_id[dir as usize] = false; -528 retransmits.get_or_create().max_stream_id[dir as usize] = true; -529 self.sent_max_remote[dir as usize] = self.max_remote[dir as usize]; -530 trace!( -531 value = self.max_remote[dir as usize], -532 "MAX_STREAMS ({:?})", dir -533 ); -534 buf.write(match dir { -535 Dir::Uni => frame::FrameType::MaxStreamsUni, -536 Dir::Bi => frame::FrameType::MaxStreamsBidi, -537 }); -538 let count = self.max_remote[dir as usize]; -539 buf.write_var(count); -540 qlog.frame(&Frame::MaxStreams { dir, count }); -541 match dir { -542 Dir::Uni => stats.max_streams_uni += 1, -543 Dir::Bi => stats.max_streams_bidi += 1, +521 let id = stream.id; +522 +523 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { +524 Some(s) => s, +525 // Stream was reset with pending data and the reset was acknowledged +526 None => continue, +527 }; +528 +529 // Reset streams aren't removed from the pending list and still exist while the peer +530 // hasn't acknowledged the reset, but should not generate STREAM frames, so we need to +531 // check for them explicitly. +532 if stream.is_reset() { +533 continue; +534 } +535 +536 // Now that we know the `StreamId`, we can better account for how many bytes +537 // are required to encode it. +538 let max_buf_size = builder.frame_space_remaining() - 1 - VarInt::size(id.into()); +539 let (offsets, encode_length) = stream.pending.poll_transmit(max_buf_size); +540 let fin = offsets.end == stream.pending.offset() +541 && matches!(stream.state, SendState::DataSent { .. }); +542 if fin { +543 stream.fin_pending = false; 544 } -545 } -546 } -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; +545 +546 if stream.is_pending() { +547 // If the stream still has pending data, reinsert it, possibly with an updated priority value +548 // Fairness with other streams is achieved by implementing round-robin scheduling, +549 // so that the other streams will have a chance to write data +550 // before we touch this stream again. +551 if fair { +552 self.pending.push_pending(id, stream.priority); +553 } else { +554 self.pending.reinsert_pending(id, stream.priority); +555 } +556 } +557 +558 let range = offsets.clone(); +559 let meta = frame::StreamMeta { id, offsets, fin }; +560 builder.write_frame(meta.encoder(encode_length), stats); +561 stream.pending.get_into(range, builder.buf); +562 } +563 } 564 -565 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 } +565 #[cfg(test)] +566 fn write_frames_for_test(&mut self, capacity: usize, fair: bool) -> frame::StreamMetaVec { +567 let buf = &mut Vec::with_capacity(capacity); +568 let mut tbuf = crate::connection::TransmitBuf::new(buf, std::num::NonZeroUsize::MIN, 1_200); +569 tbuf.start_new_datagram_with_size(capacity); +570 let builder = &mut PacketBuilder::simple_data_buf(&mut tbuf); +571 let stats = &mut FrameStats::default(); +572 self.write_stream_frames(builder, fair, stats); +573 builder.sent_frames().stream_frames.clone() +574 } +575 +576 /// Notify the application that new streams were opened or a stream became readable. +577 fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) { +578 if stream.initiator() == self.side { +579 // Notifying about the opening of locally-initiated streams would be redundant. +580 if notify_readable { +581 self.events.push_back(StreamEvent::Readable { id: stream }); +582 } +583 return; +584 } +585 let next = &mut self.next_remote[stream.dir() as usize]; +586 if stream.index() >= *next { +587 *next = stream.index() + 1; +588 self.opened[stream.dir() as usize] = true; +589 } else if notify_readable { +590 self.events.push_back(StreamEvent::Readable { id: stream }); +591 } +592 } +593 +594 pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) { +595 let mut entry = match self.send.entry(frame.id) { +596 hash_map::Entry::Vacant(_) => return, +597 hash_map::Entry::Occupied(e) => e, +598 }; 599 -600 let meta = frame::StreamMeta { id, offsets, fin }; -601 trace!(id = %meta.id, off = meta.offsets.start, len = meta.offsets.end - meta.offsets.start, fin = meta.fin, "STREAM"); -602 meta.encoder(encode_length).encode(buf); -603 qlog.frame_stream(&meta); -604 -605 stream.pending.get_into(meta.offsets.clone(), buf); -606 stream_frames.push(meta); -607 } -608 -609 stream_frames -610 } -611 -612 /// Notify the application that new streams were opened or a stream became readable. -613 fn on_stream_frame(&mut self, notify_readable: bool, stream: StreamId) { -614 if stream.initiator() == self.side { -615 // Notifying about the opening of locally-initiated streams would be redundant. -616 if notify_readable { -617 self.events.push_back(StreamEvent::Readable { id: stream }); -618 } -619 return; +600 let stream = match entry.get_mut().as_mut() { +601 Some(s) => s, +602 None => { +603 // Because we only call this after sending data on this stream, +604 // this closure should be unreachable. If we did somehow screw that up, +605 // then we might hit an underflow below with unpredictable effects down +606 // the line. Best to short-circuit. +607 return; +608 } +609 }; +610 +611 if stream.is_reset() { +612 // We account for outstanding data on reset streams at time of reset +613 return; +614 } +615 let id = frame.id; +616 self.unacked_data -= frame.offsets.end - frame.offsets.start; +617 if !stream.ack(frame) { +618 // The stream is unfinished or may still need retransmits +619 return; 620 } -621 let next = &mut self.next_remote[stream.dir() as usize]; -622 if stream.index() >= *next { -623 *next = stream.index() + 1; -624 self.opened[stream.dir() as usize] = true; -625 } else if notify_readable { -626 self.events.push_back(StreamEvent::Readable { id: stream }); -627 } -628 } -629 -630 pub(crate) fn received_ack_of(&mut self, frame: frame::StreamMeta) { -631 let mut entry = match self.send.entry(frame.id) { -632 hash_map::Entry::Vacant(_) => return, -633 hash_map::Entry::Occupied(e) => e, -634 }; -635 -636 let stream = match entry.get_mut().as_mut() { -637 Some(s) => s, -638 None => { -639 // Because we only call this after sending data on this stream, -640 // this closure should be unreachable. If we did somehow screw that up, -641 // then we might hit an underflow below with unpredictable effects down -642 // the line. Best to short-circuit. -643 return; -644 } -645 }; -646 -647 if stream.is_reset() { -648 // We account for outstanding data on reset streams at time of reset -649 return; -650 } -651 let id = frame.id; -652 self.unacked_data -= frame.offsets.end - frame.offsets.start; -653 if !stream.ack(frame) { -654 // The stream is unfinished or may still need retransmits -655 return; -656 } -657 -658 entry.remove_entry(); -659 self.stream_freed(id, StreamHalf::Send); -660 self.events.push_back(StreamEvent::Finished { id }); -661 } -662 -663 pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) { -664 let stream = match self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) { -665 // Loss of data on a closed stream is a noop -666 None => return, -667 Some(x) => x, -668 }; -669 if !stream.is_pending() { -670 self.pending.push_pending(frame.id, stream.priority); -671 } -672 stream.fin_pending |= frame.fin; -673 stream.pending.retransmit(frame.offsets); -674 } -675 -676 pub(crate) fn retransmit_all_for_0rtt(&mut self) { -677 for dir in Dir::iter() { -678 for index in 0..self.next[dir as usize] { -679 let id = StreamId::new(Side::Client, dir, index); -680 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { -681 Some(stream) => stream, -682 None => continue, -683 }; -684 if stream.pending.is_fully_acked() && !stream.fin_pending { -685 // Stream data can't be acked in 0-RTT, so we must not have sent anything on -686 // this stream -687 continue; -688 } -689 if !stream.is_pending() { -690 self.pending.push_pending(id, stream.priority); -691 } -692 stream.pending.retransmit_all_for_0rtt(); -693 } -694 } -695 } -696 -697 pub(crate) fn received_max_streams( -698 &mut self, -699 dir: Dir, -700 count: u64, -701 ) -> Result<(), TransportError> { -702 if count > MAX_STREAM_COUNT { -703 return Err(TransportError::FRAME_ENCODING_ERROR( -704 "unrepresentable stream limit", -705 )); -706 } -707 -708 let current = &mut self.max[dir as usize]; -709 if count > *current { -710 *current = count; -711 self.events.push_back(StreamEvent::Available { dir }); -712 } -713 -714 Ok(()) -715 } -716 -717 /// Handle increase to connection-level flow control limit -718 pub(crate) fn received_max_data(&mut self, n: VarInt) { -719 self.max_data = self.max_data.max(n.into()); -720 } -721 -722 pub(crate) fn received_max_stream_data( -723 &mut self, -724 id: StreamId, -725 offset: u64, -726 ) -> Result<(), TransportError> { -727 if id.initiator() != self.side && id.dir() == Dir::Uni { -728 debug!("got MAX_STREAM_DATA on recv-only {}", id); -729 return Err(TransportError::STREAM_STATE_ERROR( -730 "MAX_STREAM_DATA on recv-only stream", -731 )); -732 } +621 +622 entry.remove_entry(); +623 self.stream_freed(id, StreamHalf::Send); +624 self.events.push_back(StreamEvent::Finished { id }); +625 } +626 +627 pub(crate) fn retransmit(&mut self, frame: frame::StreamMeta) { +628 let stream = match self.send.get_mut(&frame.id).and_then(|s| s.as_mut()) { +629 // Loss of data on a closed stream is a noop +630 None => return, +631 Some(x) => x, +632 }; +633 if !stream.is_pending() { +634 self.pending.push_pending(frame.id, stream.priority); +635 } +636 stream.fin_pending |= frame.fin; +637 stream.pending.retransmit(frame.offsets); +638 } +639 +640 pub(crate) fn retransmit_all_for_0rtt(&mut self) { +641 for dir in Dir::iter() { +642 for index in 0..self.next[dir as usize] { +643 let id = StreamId::new(Side::Client, dir, index); +644 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { +645 Some(stream) => stream, +646 None => continue, +647 }; +648 if stream.pending.is_fully_acked() && !stream.fin_pending { +649 // Stream data can't be acked in 0-RTT, so we must not have sent anything on +650 // this stream +651 continue; +652 } +653 if !stream.is_pending() { +654 self.pending.push_pending(id, stream.priority); +655 } +656 stream.pending.retransmit_all_for_0rtt(); +657 } +658 } +659 } +660 +661 pub(crate) fn received_max_streams( +662 &mut self, +663 dir: Dir, +664 count: u64, +665 ) -> Result<(), TransportError> { +666 if count > MAX_STREAM_COUNT { +667 return Err(TransportError::FRAME_ENCODING_ERROR( +668 "unrepresentable stream limit", +669 )); +670 } +671 +672 let current = &mut self.max[dir as usize]; +673 if count > *current { +674 *current = count; +675 self.events.push_back(StreamEvent::Available { dir }); +676 } +677 +678 Ok(()) +679 } +680 +681 /// Handle increase to connection-level flow control limit +682 pub(crate) fn received_max_data(&mut self, n: VarInt) { +683 self.max_data = self.max_data.max(n.into()); +684 } +685 +686 pub(crate) fn received_max_stream_data( +687 &mut self, +688 id: StreamId, +689 offset: u64, +690 ) -> Result<(), TransportError> { +691 if id.initiator() != self.side && id.dir() == Dir::Uni { +692 debug!("got MAX_STREAM_DATA on recv-only {}", id); +693 return Err(TransportError::STREAM_STATE_ERROR( +694 "MAX_STREAM_DATA on recv-only stream", +695 )); +696 } +697 +698 let write_limit = self.write_limit(); +699 let max_send_data = self.max_send_data(id); +700 if let Some(ss) = self +701 .send +702 .get_mut(&id) +703 .map(get_or_insert_send(max_send_data)) +704 { +705 if ss.increase_max_data(offset) { +706 if write_limit > 0 { +707 self.events.push_back(StreamEvent::Writable { id }); +708 } else if !ss.connection_blocked { +709 // The stream is still blocked on the connection flow control +710 // window. In order to get unblocked when the window relaxes +711 // it needs to be in the connection blocked list. +712 ss.connection_blocked = true; +713 self.connection_blocked.push(id); +714 } +715 } +716 } else if id.initiator() == self.side && self.is_local_unopened(id) { +717 debug!("got MAX_STREAM_DATA on unopened {}", id); +718 return Err(TransportError::STREAM_STATE_ERROR( +719 "MAX_STREAM_DATA on unopened stream", +720 )); +721 } +722 +723 self.on_stream_frame(false, id); +724 Ok(()) +725 } +726 +727 /// Returns the maximum amount of data this is allowed to be written on the connection +728 pub(crate) fn write_limit(&self) -> u64 { +729 (self.max_data - self.data_sent) +730 // `send_window` can be set after construction to something *less* than `unacked_data` +731 .min(self.send_window.saturating_sub(self.unacked_data)) +732 } 733 -734 let write_limit = self.write_limit(); -735 let max_send_data = self.max_send_data(id); -736 if let Some(ss) = self -737 .send -738 .get_mut(&id) -739 .map(get_or_insert_send(max_send_data)) -740 { -741 if ss.increase_max_data(offset) { -742 if write_limit > 0 { -743 self.events.push_back(StreamEvent::Writable { id }); -744 } else if !ss.connection_blocked { -745 // The stream is still blocked on the connection flow control -746 // window. In order to get unblocked when the window relaxes -747 // it needs to be in the connection blocked list. -748 ss.connection_blocked = true; -749 self.connection_blocked.push(id); -750 } -751 } -752 } else if id.initiator() == self.side && self.is_local_unopened(id) { -753 debug!("got MAX_STREAM_DATA on unopened {}", id); -754 return Err(TransportError::STREAM_STATE_ERROR( -755 "MAX_STREAM_DATA on unopened stream", -756 )); +734 /// Yield stream events +735 pub(crate) fn poll(&mut self) -> Option<StreamEvent> { +736 if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false)) +737 { +738 return Some(StreamEvent::Opened { dir }); +739 } +740 +741 if self.write_limit() > 0 { +742 while let Some(id) = self.connection_blocked.pop() { +743 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { +744 None => continue, +745 Some(s) => s, +746 }; +747 +748 debug_assert!(stream.connection_blocked); +749 stream.connection_blocked = false; +750 +751 // If it's no longer sensible to write to a stream (even to detect an error) then don't +752 // report it. +753 if stream.is_writable() && stream.max_data > stream.offset() { +754 return Some(StreamEvent::Writable { id }); +755 } +756 } 757 } 758 -759 self.on_stream_frame(false, id); -760 Ok(()) -761 } -762 -763 /// Returns the maximum amount of data this is allowed to be written on the connection -764 pub(crate) fn write_limit(&self) -> u64 { -765 (self.max_data - self.data_sent) -766 // `send_window` can be set after construction to something *less* than `unacked_data` -767 .min(self.send_window.saturating_sub(self.unacked_data)) -768 } -769 -770 /// Yield stream events -771 pub(crate) fn poll(&mut self) -> Option<StreamEvent> { -772 if let Some(dir) = Dir::iter().find(|&i| mem::replace(&mut self.opened[i as usize], false)) -773 { -774 return Some(StreamEvent::Opened { dir }); +759 self.events.pop_front() +760 } +761 +762 /// Queues MAX_STREAM_ID frames in `pending` if needed +763 /// +764 /// Returns whether any frames were queued. +765 pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool { +766 let mut queued = false; +767 for dir in Dir::iter() { +768 let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize]; +769 // To reduce traffic, only announce updates if at least 1/8 of the flow control window +770 // has been consumed. +771 if diff > self.max_concurrent_remote_count[dir as usize] / 8 { +772 pending.max_stream_id[dir as usize] = true; +773 queued = true; +774 } 775 } -776 -777 if self.write_limit() > 0 { -778 while let Some(id) = self.connection_blocked.pop() { -779 let stream = match self.send.get_mut(&id).and_then(|s| s.as_mut()) { -780 None => continue, -781 Some(s) => s, -782 }; -783 -784 debug_assert!(stream.connection_blocked); -785 stream.connection_blocked = false; -786 -787 // If it's no longer sensible to write to a stream (even to detect an error) then don't -788 // report it. -789 if stream.is_writable() && stream.max_data > stream.offset() { -790 return Some(StreamEvent::Writable { id }); -791 } -792 } -793 } -794 -795 self.events.pop_front() -796 } -797 -798 /// Queues MAX_STREAM_ID frames in `pending` if needed -799 /// -800 /// Returns whether any frames were queued. -801 pub(crate) fn queue_max_stream_id(&mut self, pending: &mut Retransmits) -> bool { -802 let mut queued = false; -803 for dir in Dir::iter() { -804 let diff = self.max_remote[dir as usize] - self.sent_max_remote[dir as usize]; -805 // To reduce traffic, only announce updates if at least 1/8 of the flow control window -806 // has been consumed. -807 if diff > self.max_concurrent_remote_count[dir as usize] / 8 { -808 pending.max_stream_id[dir as usize] = true; -809 queued = true; -810 } -811 } -812 queued +776 queued +777 } +778 +779 /// Check for errors entailed by the peer's use of `id` as a send stream +780 fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> { +781 if self.side == id.initiator() { +782 match id.dir() { +783 Dir::Uni => { +784 return Err(TransportError::STREAM_STATE_ERROR( +785 "illegal operation on send-only stream", +786 )); +787 } +788 Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => { +789 return Err(TransportError::STREAM_STATE_ERROR( +790 "operation on unopened stream", +791 )); +792 } +793 Dir::Bi => {} +794 }; +795 } else { +796 let limit = self.max_remote[id.dir() as usize]; +797 if id.index() >= limit { +798 return Err(TransportError::STREAM_LIMIT_ERROR("")); +799 } +800 } +801 Ok(()) +802 } +803 +804 /// Whether a locally initiated stream has never been open +805 pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool { +806 id.index() >= self.next[id.dir() as usize] +807 } +808 +809 pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) { +810 self.flow_control_adjusted = true; +811 self.max_concurrent_remote_count[dir as usize] = count.into(); +812 self.ensure_remote_streams(dir); 813 } 814 -815 /// Check for errors entailed by the peer's use of `id` as a send stream -816 fn validate_receive_id(&mut self, id: StreamId) -> Result<(), TransportError> { -817 if self.side == id.initiator() { -818 match id.dir() { -819 Dir::Uni => { -820 return Err(TransportError::STREAM_STATE_ERROR( -821 "illegal operation on send-only stream", -822 )); -823 } -824 Dir::Bi if id.index() >= self.next[Dir::Bi as usize] => { -825 return Err(TransportError::STREAM_STATE_ERROR( -826 "operation on unopened stream", -827 )); -828 } -829 Dir::Bi => {} -830 }; -831 } else { -832 let limit = self.max_remote[id.dir() as usize]; -833 if id.index() >= limit { -834 return Err(TransportError::STREAM_LIMIT_ERROR("")); -835 } +815 pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 { +816 self.allocated_remote_count[dir as usize] +817 } +818 +819 pub(crate) fn set_send_window(&mut self, send_window: u64) { +820 self.send_window = send_window; +821 } +822 +823 /// Set the receive_window and returns whether the receive_window has been +824 /// expanded or shrunk: true if expanded, false if shrunk. +825 pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool { +826 let receive_window = receive_window.into(); +827 let mut expanded = false; +828 if receive_window > self.receive_window { +829 self.local_max_data = self +830 .local_max_data +831 .saturating_add(receive_window - self.receive_window); +832 expanded = true; +833 } else { +834 let diff = self.receive_window - receive_window; +835 self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff); 836 } -837 Ok(()) -838 } -839 -840 /// Whether a locally initiated stream has never been open -841 pub(crate) fn is_local_unopened(&self, id: StreamId) -> bool { -842 id.index() >= self.next[id.dir() as usize] -843 } -844 -845 pub(crate) fn set_max_concurrent(&mut self, dir: Dir, count: VarInt) { -846 self.flow_control_adjusted = true; -847 self.max_concurrent_remote_count[dir as usize] = count.into(); -848 self.ensure_remote_streams(dir); -849 } -850 -851 pub(crate) fn max_concurrent(&self, dir: Dir) -> u64 { -852 self.allocated_remote_count[dir as usize] -853 } -854 -855 pub(crate) fn set_send_window(&mut self, send_window: u64) { -856 self.send_window = send_window; -857 } -858 -859 /// Set the receive_window and returns whether the receive_window has been -860 /// expanded or shrunk: true if expanded, false if shrunk. -861 pub(crate) fn set_receive_window(&mut self, receive_window: VarInt) -> bool { -862 let receive_window = receive_window.into(); -863 let mut expanded = false; -864 if receive_window > self.receive_window { -865 self.local_max_data = self -866 .local_max_data -867 .saturating_add(receive_window - self.receive_window); -868 expanded = true; -869 } else { -870 let diff = self.receive_window - receive_window; -871 self.receive_window_shrink_debt = self.receive_window_shrink_debt.saturating_add(diff); -872 } -873 self.receive_window = receive_window; -874 expanded -875 } -876 -877 pub(super) fn insert(&mut self, remote: bool, id: StreamId) { -878 let bi = id.dir() == Dir::Bi; -879 // bidirectional OR (unidirectional AND NOT remote) -880 if bi || !remote { -881 assert!(self.send.insert(id, None).is_none()); -882 } -883 // bidirectional OR (unidirectional AND remote) -884 if bi || remote { -885 let recv = self.free_recv.pop(); -886 assert!(self.recv.insert(id, recv).is_none()); -887 } -888 } -889 -890 /// Adds credits to the connection flow control window -891 /// -892 /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible. -893 /// This will only be the case if the window update would is significant -894 /// enough. As soon as a window update with a `MAX_DATA` frame has been -895 /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to -896 /// suppress sending further updates until the window increases significantly -897 /// again. -898 pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit { -899 if credits > self.receive_window_shrink_debt { -900 let net_credits = credits - self.receive_window_shrink_debt; -901 self.local_max_data = self.local_max_data.saturating_add(net_credits); -902 self.receive_window_shrink_debt = 0; -903 } else { -904 self.receive_window_shrink_debt -= credits; -905 } +837 self.receive_window = receive_window; +838 expanded +839 } +840 +841 pub(super) fn insert(&mut self, remote: bool, id: StreamId) { +842 let bi = id.dir() == Dir::Bi; +843 // bidirectional OR (unidirectional AND NOT remote) +844 if bi || !remote { +845 assert!(self.send.insert(id, None).is_none()); +846 } +847 // bidirectional OR (unidirectional AND remote) +848 if bi || remote { +849 let recv = self.free_recv.pop(); +850 assert!(self.recv.insert(id, recv).is_none()); +851 } +852 } +853 +854 /// Adds credits to the connection flow control window +855 /// +856 /// Returns whether a `MAX_DATA` frame should be enqueued as soon as possible. +857 /// This will only be the case if the window update would is significant +858 /// enough. As soon as a window update with a `MAX_DATA` frame has been +859 /// queued, the [`Recv::record_sent_max_stream_data`] function should be called to +860 /// suppress sending further updates until the window increases significantly +861 /// again. +862 pub(super) fn add_read_credits(&mut self, credits: u64) -> ShouldTransmit { +863 if credits > self.receive_window_shrink_debt { +864 let net_credits = credits - self.receive_window_shrink_debt; +865 self.local_max_data = self.local_max_data.saturating_add(net_credits); +866 self.receive_window_shrink_debt = 0; +867 } else { +868 self.receive_window_shrink_debt -= credits; +869 } +870 +871 if self.local_max_data > VarInt::MAX.into_inner() { +872 return ShouldTransmit(false); +873 } +874 +875 // Only announce a window update if it's significant enough +876 // to make it worthwhile sending a MAX_DATA frame. +877 // We use a fraction of the configured connection receive window to make +878 // the decision, to accommodate for connection using bigger windows requiring +879 // less updates. +880 let diff = self.local_max_data - self.sent_max_data.into_inner(); +881 ShouldTransmit(diff >= (self.receive_window / 8)) +882 } +883 +884 /// Update counters for removal of a stream +885 pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) { +886 if id.initiator() != self.side { +887 let fully_free = id.dir() == Dir::Uni +888 || match half { +889 StreamHalf::Send => !self.recv.contains_key(&id), +890 StreamHalf::Recv => !self.send.contains_key(&id), +891 }; +892 if fully_free { +893 self.allocated_remote_count[id.dir() as usize] -= 1; +894 self.ensure_remote_streams(id.dir()); +895 } +896 } +897 if half == StreamHalf::Send { +898 self.send_streams -= 1; +899 } +900 } +901 +902 pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) { +903 self.free_recv.push(recv.free(self.stream_receive_window)); +904 self.stream_freed(id, StreamHalf::Recv); +905 } 906 -907 if self.local_max_data > VarInt::MAX.into_inner() { -908 return ShouldTransmit(false); -909 } -910 -911 // Only announce a window update if it's significant enough -912 // to make it worthwhile sending a MAX_DATA frame. -913 // We use a fraction of the configured connection receive window to make -914 // the decision, to accommodate for connection using bigger windows requiring -915 // less updates. -916 let diff = self.local_max_data - self.sent_max_data.into_inner(); -917 ShouldTransmit(diff >= (self.receive_window / 8)) -918 } -919 -920 /// Update counters for removal of a stream -921 pub(super) fn stream_freed(&mut self, id: StreamId, half: StreamHalf) { -922 if id.initiator() != self.side { -923 let fully_free = id.dir() == Dir::Uni -924 || match half { -925 StreamHalf::Send => !self.recv.contains_key(&id), -926 StreamHalf::Recv => !self.send.contains_key(&id), -927 }; -928 if fully_free { -929 self.allocated_remote_count[id.dir() as usize] -= 1; -930 self.ensure_remote_streams(id.dir()); -931 } -932 } -933 if half == StreamHalf::Send { -934 self.send_streams -= 1; -935 } -936 } -937 -938 pub(super) fn stream_recv_freed(&mut self, id: StreamId, recv: StreamRecv) { -939 self.free_recv.push(recv.free(self.stream_receive_window)); -940 self.stream_freed(id, StreamHalf::Recv); -941 } -942 -943 pub(super) fn max_send_data(&self, id: StreamId) -> VarInt { -944 let remote = self.side != id.initiator(); -945 match id.dir() { -946 Dir::Uni => self.initial_max_stream_data_uni, -947 // Remote/local appear reversed here because the transport parameters are named from -948 // the perspective of the peer. -949 Dir::Bi if remote => self.initial_max_stream_data_bidi_local, -950 Dir::Bi => self.initial_max_stream_data_bidi_remote, -951 } -952 } -953} -954 -955#[inline] -956pub(super) fn get_or_insert_send( -957 max_data: VarInt, -958) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> { -959 move |opt| opt.get_or_insert_with(|| Send::new(max_data)) -960} -961 -962#[inline] -963pub(super) fn get_or_insert_recv( -964 initial_max_data: u64, -965) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv { -966 move |opt| { -967 *opt = opt.take().map(|s| match s { -968 StreamRecv::Free(recv) => StreamRecv::Open(recv), -969 s => s, -970 }); -971 opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data))) -972 .as_open_recv_mut() -973 .unwrap() -974 } -975} -976 -977#[cfg(test)] -978mod tests { -979 use super::*; -980 use crate::{ -981 ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError, -982 connection::State as ConnState, connection::Streams, -983 }; -984 use bytes::Bytes; -985 -986 fn make(side: Side) -> StreamsState { -987 StreamsState::new( -988 side, -989 128u32.into(), -990 128u32.into(), -991 1024 * 1024, -992 (1024 * 1024u32).into(), -993 (1024 * 1024u32).into(), -994 ) -995 } -996 -997 #[test] -998 fn trivial_flow_control() { -999 let mut client = StreamsState::new( -1000 Side::Client, -1001 1u32.into(), -1002 1u32.into(), -1003 1024 * 1024, -1004 (1024 * 1024u32).into(), -1005 (1024 * 1024u32).into(), -1006 ); -1007 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1008 let initial_max = client.local_max_data; -1009 const MESSAGE_SIZE: usize = 2048; -1010 assert_eq!( -1011 client -1012 .received( -1013 frame::Stream { -1014 id, -1015 offset: 0, -1016 fin: true, -1017 data: Bytes::from_static(&[0; MESSAGE_SIZE]), -1018 }, -1019 2048 -1020 ) -1021 .unwrap(), -1022 ShouldTransmit(false) -1023 ); -1024 assert_eq!(client.data_recvd, 2048); -1025 assert_eq!(client.local_max_data - initial_max, 0); -1026 -1027 let mut pending = Retransmits::default(); -1028 let mut recv = RecvStream { -1029 id, -1030 state: &mut client, -1031 pending: &mut pending, -1032 }; -1033 -1034 let mut chunks = recv.read(true).unwrap(); -1035 assert_eq!( -1036 chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(), -1037 MESSAGE_SIZE -1038 ); -1039 assert!(chunks.next(0).unwrap().is_none()); -1040 let should_transmit = chunks.finalize(); -1041 assert!(should_transmit.0); -1042 assert!(pending.max_stream_id[Dir::Uni as usize]); -1043 assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64); -1044 } -1045 -1046 #[test] -1047 fn reset_flow_control() { -1048 let mut client = make(Side::Client); -1049 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1050 let initial_max = client.local_max_data; -1051 assert_eq!( -1052 client -1053 .received( -1054 frame::Stream { -1055 id, -1056 offset: 0, -1057 fin: false, -1058 data: Bytes::from_static(&[0; 2048]), -1059 }, -1060 2048 -1061 ) -1062 .unwrap(), -1063 ShouldTransmit(false) -1064 ); -1065 assert_eq!(client.data_recvd, 2048); -1066 assert_eq!(client.local_max_data - initial_max, 0); -1067 -1068 let mut pending = Retransmits::default(); -1069 let mut recv = RecvStream { -1070 id, -1071 state: &mut client, -1072 pending: &mut pending, -1073 }; -1074 -1075 let mut chunks = recv.read(true).unwrap(); -1076 chunks.next(1024).unwrap(); -1077 let _ = chunks.finalize(); -1078 assert_eq!(client.local_max_data - initial_max, 1024); -1079 assert_eq!( -1080 client -1081 .received_reset(frame::ResetStream { -1082 id, -1083 error_code: 0u32.into(), -1084 final_offset: 4096u32.into(), -1085 }) -1086 .unwrap(), -1087 ShouldTransmit(false) -1088 ); -1089 -1090 assert_eq!(client.data_recvd, 4096); -1091 assert_eq!(client.local_max_data - initial_max, 4096); -1092 -1093 // Ensure reading after a reset doesn't issue redundant credit -1094 let mut recv = RecvStream { -1095 id, -1096 state: &mut client, -1097 pending: &mut pending, -1098 }; -1099 let mut chunks = recv.read(true).unwrap(); -1100 assert_eq!( -1101 chunks.next(1024).unwrap_err(), -1102 crate::ReadError::Reset(0u32.into()) +907 pub(super) fn max_send_data(&self, id: StreamId) -> VarInt { +908 let remote = self.side != id.initiator(); +909 match id.dir() { +910 Dir::Uni => self.initial_max_stream_data_uni, +911 // Remote/local appear reversed here because the transport parameters are named from +912 // the perspective of the peer. +913 Dir::Bi if remote => self.initial_max_stream_data_bidi_local, +914 Dir::Bi => self.initial_max_stream_data_bidi_remote, +915 } +916 } +917} +918 +919#[inline] +920pub(super) fn get_or_insert_send( +921 max_data: VarInt, +922) -> impl Fn(&mut Option<Box<Send>>) -> &mut Box<Send> { +923 move |opt| opt.get_or_insert_with(|| Send::new(max_data)) +924} +925 +926#[inline] +927pub(super) fn get_or_insert_recv( +928 initial_max_data: u64, +929) -> impl FnMut(&mut Option<StreamRecv>) -> &mut Recv { +930 move |opt| { +931 *opt = opt.take().map(|s| match s { +932 StreamRecv::Free(recv) => StreamRecv::Open(recv), +933 s => s, +934 }); +935 opt.get_or_insert_with(|| StreamRecv::Open(Recv::new(initial_max_data))) +936 .as_open_recv_mut() +937 .unwrap() +938 } +939} +940 +941#[cfg(test)] +942mod tests { +943 use super::*; +944 use crate::{ +945 ReadableError, RecvStream, SendStream, TransportErrorCode, WriteError, +946 connection::State as ConnState, connection::Streams, +947 }; +948 use bytes::Bytes; +949 +950 fn make(side: Side) -> StreamsState { +951 StreamsState::new( +952 side, +953 128u32.into(), +954 128u32.into(), +955 1024 * 1024, +956 (1024 * 1024u32).into(), +957 (1024 * 1024u32).into(), +958 ) +959 } +960 +961 #[test] +962 fn trivial_flow_control() { +963 let mut client = StreamsState::new( +964 Side::Client, +965 1u32.into(), +966 1u32.into(), +967 1024 * 1024, +968 (1024 * 1024u32).into(), +969 (1024 * 1024u32).into(), +970 ); +971 let id = StreamId::new(Side::Server, Dir::Uni, 0); +972 let initial_max = client.local_max_data; +973 const MESSAGE_SIZE: usize = 2048; +974 assert_eq!( +975 client +976 .received( +977 frame::Stream { +978 id, +979 offset: 0, +980 fin: true, +981 data: Bytes::from_static(&[0; MESSAGE_SIZE]), +982 }, +983 2048 +984 ) +985 .unwrap(), +986 ShouldTransmit(false) +987 ); +988 assert_eq!(client.data_recvd, 2048); +989 assert_eq!(client.local_max_data - initial_max, 0); +990 +991 let mut pending = Retransmits::default(); +992 let mut recv = RecvStream { +993 id, +994 state: &mut client, +995 pending: &mut pending, +996 }; +997 +998 let mut chunks = recv.read(true).unwrap(); +999 assert_eq!( +1000 chunks.next(MESSAGE_SIZE).unwrap().unwrap().bytes.len(), +1001 MESSAGE_SIZE +1002 ); +1003 assert!(chunks.next(0).unwrap().is_none()); +1004 let should_transmit = chunks.finalize(); +1005 assert!(should_transmit.0); +1006 assert!(pending.max_stream_id[Dir::Uni as usize]); +1007 assert_eq!(client.local_max_data - initial_max, MESSAGE_SIZE as u64); +1008 } +1009 +1010 #[test] +1011 fn reset_flow_control() { +1012 let mut client = make(Side::Client); +1013 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1014 let initial_max = client.local_max_data; +1015 assert_eq!( +1016 client +1017 .received( +1018 frame::Stream { +1019 id, +1020 offset: 0, +1021 fin: false, +1022 data: Bytes::from_static(&[0; 2048]), +1023 }, +1024 2048 +1025 ) +1026 .unwrap(), +1027 ShouldTransmit(false) +1028 ); +1029 assert_eq!(client.data_recvd, 2048); +1030 assert_eq!(client.local_max_data - initial_max, 0); +1031 +1032 let mut pending = Retransmits::default(); +1033 let mut recv = RecvStream { +1034 id, +1035 state: &mut client, +1036 pending: &mut pending, +1037 }; +1038 +1039 let mut chunks = recv.read(true).unwrap(); +1040 chunks.next(1024).unwrap(); +1041 let _ = chunks.finalize(); +1042 assert_eq!(client.local_max_data - initial_max, 1024); +1043 assert_eq!( +1044 client +1045 .received_reset(frame::ResetStream { +1046 id, +1047 error_code: 0u32.into(), +1048 final_offset: 4096u32.into(), +1049 }) +1050 .unwrap(), +1051 ShouldTransmit(false) +1052 ); +1053 +1054 assert_eq!(client.data_recvd, 4096); +1055 assert_eq!(client.local_max_data - initial_max, 4096); +1056 +1057 // Ensure reading after a reset doesn't issue redundant credit +1058 let mut recv = RecvStream { +1059 id, +1060 state: &mut client, +1061 pending: &mut pending, +1062 }; +1063 let mut chunks = recv.read(true).unwrap(); +1064 assert_eq!( +1065 chunks.next(1024).unwrap_err(), +1066 crate::ReadError::Reset(0u32.into()) +1067 ); +1068 let _ = chunks.finalize(); +1069 assert_eq!(client.data_recvd, 4096); +1070 assert_eq!(client.local_max_data - initial_max, 4096); +1071 } +1072 +1073 #[test] +1074 fn reset_after_empty_frame_flow_control() { +1075 let mut client = make(Side::Client); +1076 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1077 let initial_max = client.local_max_data; +1078 assert_eq!( +1079 client +1080 .received( +1081 frame::Stream { +1082 id, +1083 offset: 4096, +1084 fin: false, +1085 data: Bytes::from_static(&[0; 0]), +1086 }, +1087 0 +1088 ) +1089 .unwrap(), +1090 ShouldTransmit(false) +1091 ); +1092 assert_eq!(client.data_recvd, 4096); +1093 assert_eq!(client.local_max_data - initial_max, 0); +1094 assert_eq!( +1095 client +1096 .received_reset(frame::ResetStream { +1097 id, +1098 error_code: 0u32.into(), +1099 final_offset: 4096u32.into(), +1100 }) +1101 .unwrap(), +1102 ShouldTransmit(false) 1103 ); -1104 let _ = chunks.finalize(); -1105 assert_eq!(client.data_recvd, 4096); -1106 assert_eq!(client.local_max_data - initial_max, 4096); -1107 } -1108 -1109 #[test] -1110 fn reset_after_empty_frame_flow_control() { -1111 let mut client = make(Side::Client); -1112 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1113 let initial_max = client.local_max_data; -1114 assert_eq!( -1115 client -1116 .received( -1117 frame::Stream { -1118 id, -1119 offset: 4096, -1120 fin: false, -1121 data: Bytes::from_static(&[0; 0]), -1122 }, -1123 0 -1124 ) -1125 .unwrap(), -1126 ShouldTransmit(false) -1127 ); -1128 assert_eq!(client.data_recvd, 4096); -1129 assert_eq!(client.local_max_data - initial_max, 0); -1130 assert_eq!( -1131 client -1132 .received_reset(frame::ResetStream { -1133 id, -1134 error_code: 0u32.into(), -1135 final_offset: 4096u32.into(), -1136 }) -1137 .unwrap(), -1138 ShouldTransmit(false) -1139 ); -1140 assert_eq!(client.data_recvd, 4096); -1141 assert_eq!(client.local_max_data - initial_max, 4096); -1142 } -1143 -1144 #[test] -1145 fn duplicate_reset_flow_control() { -1146 let mut client = make(Side::Client); -1147 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1148 assert_eq!( -1149 client -1150 .received_reset(frame::ResetStream { -1151 id, -1152 error_code: 0u32.into(), -1153 final_offset: 4096u32.into(), -1154 }) -1155 .unwrap(), -1156 ShouldTransmit(false) -1157 ); -1158 assert_eq!(client.data_recvd, 4096); -1159 assert_eq!( -1160 client -1161 .received_reset(frame::ResetStream { -1162 id, -1163 error_code: 0u32.into(), -1164 final_offset: 4096u32.into(), -1165 }) -1166 .unwrap(), -1167 ShouldTransmit(false) -1168 ); -1169 assert_eq!(client.data_recvd, 4096); -1170 } +1104 assert_eq!(client.data_recvd, 4096); +1105 assert_eq!(client.local_max_data - initial_max, 4096); +1106 } +1107 +1108 #[test] +1109 fn duplicate_reset_flow_control() { +1110 let mut client = make(Side::Client); +1111 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1112 assert_eq!( +1113 client +1114 .received_reset(frame::ResetStream { +1115 id, +1116 error_code: 0u32.into(), +1117 final_offset: 4096u32.into(), +1118 }) +1119 .unwrap(), +1120 ShouldTransmit(false) +1121 ); +1122 assert_eq!(client.data_recvd, 4096); +1123 assert_eq!( +1124 client +1125 .received_reset(frame::ResetStream { +1126 id, +1127 error_code: 0u32.into(), +1128 final_offset: 4096u32.into(), +1129 }) +1130 .unwrap(), +1131 ShouldTransmit(false) +1132 ); +1133 assert_eq!(client.data_recvd, 4096); +1134 } +1135 +1136 #[test] +1137 fn recv_stopped() { +1138 let mut client = make(Side::Client); +1139 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1140 let initial_max = client.local_max_data; +1141 assert_eq!( +1142 client +1143 .received( +1144 frame::Stream { +1145 id, +1146 offset: 0, +1147 fin: false, +1148 data: Bytes::from_static(&[0; 32]), +1149 }, +1150 32 +1151 ) +1152 .unwrap(), +1153 ShouldTransmit(false) +1154 ); +1155 assert_eq!(client.local_max_data, initial_max); +1156 +1157 let mut pending = Retransmits::default(); +1158 let mut recv = RecvStream { +1159 id, +1160 state: &mut client, +1161 pending: &mut pending, +1162 }; +1163 +1164 recv.stop(0u32.into()).unwrap(); +1165 assert_eq!(recv.pending.stop_sending.len(), 1); +1166 assert!(!recv.pending.max_data); +1167 +1168 assert!(recv.stop(0u32.into()).is_err()); +1169 assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream)); +1170 assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream)); 1171 -1172 #[test] -1173 fn recv_stopped() { -1174 let mut client = make(Side::Client); -1175 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1176 let initial_max = client.local_max_data; -1177 assert_eq!( -1178 client -1179 .received( -1180 frame::Stream { -1181 id, -1182 offset: 0, -1183 fin: false, -1184 data: Bytes::from_static(&[0; 32]), -1185 }, -1186 32 -1187 ) -1188 .unwrap(), -1189 ShouldTransmit(false) -1190 ); -1191 assert_eq!(client.local_max_data, initial_max); -1192 -1193 let mut pending = Retransmits::default(); -1194 let mut recv = RecvStream { -1195 id, -1196 state: &mut client, -1197 pending: &mut pending, -1198 }; -1199 -1200 recv.stop(0u32.into()).unwrap(); -1201 assert_eq!(recv.pending.stop_sending.len(), 1); -1202 assert!(!recv.pending.max_data); -1203 -1204 assert!(recv.stop(0u32.into()).is_err()); -1205 assert_eq!(recv.read(true).err(), Some(ReadableError::ClosedStream)); -1206 assert_eq!(recv.read(false).err(), Some(ReadableError::ClosedStream)); -1207 -1208 assert_eq!(client.local_max_data - initial_max, 32); -1209 assert_eq!( -1210 client -1211 .received( -1212 frame::Stream { -1213 id, -1214 offset: 32, -1215 fin: true, -1216 data: Bytes::from_static(&[0; 16]), -1217 }, -1218 16 -1219 ) -1220 .unwrap(), -1221 ShouldTransmit(false) -1222 ); -1223 assert_eq!(client.local_max_data - initial_max, 48); -1224 assert!(!client.recv.contains_key(&id)); -1225 } -1226 -1227 #[test] -1228 fn stopped_reset() { -1229 let mut client = make(Side::Client); -1230 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1231 // Server opens stream -1232 assert_eq!( -1233 client -1234 .received( -1235 frame::Stream { -1236 id, -1237 offset: 0, -1238 fin: false, -1239 data: Bytes::from_static(&[0; 32]) -1240 }, -1241 32 -1242 ) -1243 .unwrap(), -1244 ShouldTransmit(false) -1245 ); -1246 -1247 let mut pending = Retransmits::default(); -1248 let mut recv = RecvStream { -1249 id, -1250 state: &mut client, -1251 pending: &mut pending, -1252 }; -1253 -1254 recv.stop(0u32.into()).unwrap(); -1255 assert_eq!(pending.stop_sending.len(), 1); -1256 assert!(!pending.max_data); -1257 -1258 // Server complies -1259 let prev_max = client.max_remote[Dir::Uni as usize]; -1260 assert_eq!( -1261 client -1262 .received_reset(frame::ResetStream { -1263 id, -1264 error_code: 0u32.into(), -1265 final_offset: 32u32.into(), -1266 }) -1267 .unwrap(), -1268 ShouldTransmit(false) -1269 ); -1270 assert!(!client.recv.contains_key(&id), "stream state is freed"); -1271 assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1); -1272 } -1273 -1274 #[test] -1275 fn send_stopped() { -1276 let mut server = make(Side::Server); -1277 server.set_params(&TransportParameters { -1278 initial_max_streams_uni: 1u32.into(), -1279 initial_max_data: 42u32.into(), -1280 initial_max_stream_data_uni: 42u32.into(), -1281 ..TransportParameters::default() -1282 }); -1283 -1284 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1285 let id = Streams { -1286 state: &mut server, -1287 conn_state: &state, -1288 } -1289 .open(Dir::Uni) -1290 .unwrap(); -1291 -1292 let mut stream = SendStream { -1293 id, -1294 state: &mut server, -1295 pending: &mut pending, -1296 conn_state: &state, -1297 }; +1172 assert_eq!(client.local_max_data - initial_max, 32); +1173 assert_eq!( +1174 client +1175 .received( +1176 frame::Stream { +1177 id, +1178 offset: 32, +1179 fin: true, +1180 data: Bytes::from_static(&[0; 16]), +1181 }, +1182 16 +1183 ) +1184 .unwrap(), +1185 ShouldTransmit(false) +1186 ); +1187 assert_eq!(client.local_max_data - initial_max, 48); +1188 assert!(!client.recv.contains_key(&id)); +1189 } +1190 +1191 #[test] +1192 fn stopped_reset() { +1193 let mut client = make(Side::Client); +1194 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1195 // Server opens stream +1196 assert_eq!( +1197 client +1198 .received( +1199 frame::Stream { +1200 id, +1201 offset: 0, +1202 fin: false, +1203 data: Bytes::from_static(&[0; 32]) +1204 }, +1205 32 +1206 ) +1207 .unwrap(), +1208 ShouldTransmit(false) +1209 ); +1210 +1211 let mut pending = Retransmits::default(); +1212 let mut recv = RecvStream { +1213 id, +1214 state: &mut client, +1215 pending: &mut pending, +1216 }; +1217 +1218 recv.stop(0u32.into()).unwrap(); +1219 assert_eq!(pending.stop_sending.len(), 1); +1220 assert!(!pending.max_data); +1221 +1222 // Server complies +1223 let prev_max = client.max_remote[Dir::Uni as usize]; +1224 assert_eq!( +1225 client +1226 .received_reset(frame::ResetStream { +1227 id, +1228 error_code: 0u32.into(), +1229 final_offset: 32u32.into(), +1230 }) +1231 .unwrap(), +1232 ShouldTransmit(false) +1233 ); +1234 assert!(!client.recv.contains_key(&id), "stream state is freed"); +1235 assert_eq!(client.max_remote[Dir::Uni as usize], prev_max + 1); +1236 } +1237 +1238 #[test] +1239 fn send_stopped() { +1240 let mut server = make(Side::Server); +1241 server.set_params(&TransportParameters { +1242 initial_max_streams_uni: 1u32.into(), +1243 initial_max_data: 42u32.into(), +1244 initial_max_stream_data_uni: 42u32.into(), +1245 ..TransportParameters::default() +1246 }); +1247 +1248 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1249 let id = Streams { +1250 state: &mut server, +1251 conn_state: &state, +1252 } +1253 .open(Dir::Uni) +1254 .unwrap(); +1255 +1256 let mut stream = SendStream { +1257 id, +1258 state: &mut server, +1259 pending: &mut pending, +1260 conn_state: &state, +1261 }; +1262 +1263 let error_code = 0u32.into(); +1264 stream.state.received_stop_sending(id, error_code); +1265 assert!( +1266 stream +1267 .state +1268 .events +1269 .contains(&StreamEvent::Stopped { id, error_code }) +1270 ); +1271 stream.state.events.clear(); +1272 +1273 assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code))); +1274 +1275 stream.reset(0u32.into()).unwrap(); +1276 assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream)); +1277 +1278 // A duplicate frame is a no-op +1279 stream.state.received_stop_sending(id, error_code); +1280 assert!(stream.state.events.is_empty()); +1281 } +1282 +1283 #[test] +1284 fn final_offset_flow_control() { +1285 let mut client = make(Side::Client); +1286 assert_eq!( +1287 client +1288 .received_reset(frame::ResetStream { +1289 id: StreamId::new(Side::Server, Dir::Uni, 0), +1290 error_code: 0u32.into(), +1291 final_offset: VarInt::MAX, +1292 }) +1293 .unwrap_err() +1294 .code, +1295 TransportErrorCode::FLOW_CONTROL_ERROR +1296 ); +1297 } 1298 -1299 let error_code = 0u32.into(); -1300 stream.state.received_stop_sending(id, error_code); -1301 assert!( -1302 stream -1303 .state -1304 .events -1305 .contains(&StreamEvent::Stopped { id, error_code }) -1306 ); -1307 stream.state.events.clear(); +1299 #[test] +1300 fn stream_priority() { +1301 let mut server = make(Side::Server); +1302 server.set_params(&TransportParameters { +1303 initial_max_streams_bidi: 3u32.into(), +1304 initial_max_data: 10u32.into(), +1305 initial_max_stream_data_bidi_remote: 10u32.into(), +1306 ..TransportParameters::default() +1307 }); 1308 -1309 assert_eq!(stream.write(&[]), Err(WriteError::Stopped(error_code))); -1310 -1311 stream.reset(0u32.into()).unwrap(); -1312 assert_eq!(stream.write(&[]), Err(WriteError::ClosedStream)); -1313 -1314 // A duplicate frame is a no-op -1315 stream.state.received_stop_sending(id, error_code); -1316 assert!(stream.state.events.is_empty()); -1317 } +1309 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1310 let mut streams = Streams { +1311 state: &mut server, +1312 conn_state: &state, +1313 }; +1314 +1315 let id_high = streams.open(Dir::Bi).unwrap(); +1316 let id_mid = streams.open(Dir::Bi).unwrap(); +1317 let id_low = streams.open(Dir::Bi).unwrap(); 1318 -1319 #[test] -1320 fn final_offset_flow_control() { -1321 let mut client = make(Side::Client); -1322 assert_eq!( -1323 client -1324 .received_reset(frame::ResetStream { -1325 id: StreamId::new(Side::Server, Dir::Uni, 0), -1326 error_code: 0u32.into(), -1327 final_offset: VarInt::MAX, -1328 }) -1329 .unwrap_err() -1330 .code, -1331 TransportErrorCode::FLOW_CONTROL_ERROR -1332 ); -1333 } -1334 -1335 #[test] -1336 fn stream_priority() { -1337 let mut server = make(Side::Server); -1338 server.set_params(&TransportParameters { -1339 initial_max_streams_bidi: 3u32.into(), -1340 initial_max_data: 10u32.into(), -1341 initial_max_stream_data_bidi_remote: 10u32.into(), -1342 ..TransportParameters::default() -1343 }); +1319 let mut mid = SendStream { +1320 id: id_mid, +1321 state: &mut server, +1322 pending: &mut pending, +1323 conn_state: &state, +1324 }; +1325 mid.write(b"mid").unwrap(); +1326 +1327 let mut low = SendStream { +1328 id: id_low, +1329 state: &mut server, +1330 pending: &mut pending, +1331 conn_state: &state, +1332 }; +1333 low.set_priority(-1).unwrap(); +1334 low.write(b"low").unwrap(); +1335 +1336 let mut high = SendStream { +1337 id: id_high, +1338 state: &mut server, +1339 pending: &mut pending, +1340 conn_state: &state, +1341 }; +1342 high.set_priority(1).unwrap(); +1343 high.write(b"high").unwrap(); 1344 -1345 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1346 let mut streams = Streams { -1347 state: &mut server, -1348 conn_state: &state, -1349 }; -1350 -1351 let id_high = streams.open(Dir::Bi).unwrap(); -1352 let id_mid = streams.open(Dir::Bi).unwrap(); -1353 let id_low = streams.open(Dir::Bi).unwrap(); -1354 -1355 let mut mid = SendStream { -1356 id: id_mid, -1357 state: &mut server, -1358 pending: &mut pending, -1359 conn_state: &state, -1360 }; -1361 mid.write(b"mid").unwrap(); -1362 -1363 let mut low = SendStream { -1364 id: id_low, -1365 state: &mut server, -1366 pending: &mut pending, +1345 let meta = server.write_frames_for_test(40, true); +1346 assert_eq!(meta[0].id, id_high); +1347 assert_eq!(meta[1].id, id_mid); +1348 assert_eq!(meta[2].id, id_low); +1349 +1350 assert!(!server.can_send_stream_data()); +1351 assert_eq!(server.pending.len(), 0); +1352 } +1353 +1354 #[test] +1355 fn requeue_stream_priority() { +1356 let mut server = make(Side::Server); +1357 server.set_params(&TransportParameters { +1358 initial_max_streams_bidi: 3u32.into(), +1359 initial_max_data: 1000u32.into(), +1360 initial_max_stream_data_bidi_remote: 1000u32.into(), +1361 ..TransportParameters::default() +1362 }); +1363 +1364 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1365 let mut streams = Streams { +1366 state: &mut server, 1367 conn_state: &state, 1368 }; -1369 low.set_priority(-1).unwrap(); -1370 low.write(b"low").unwrap(); -1371 -1372 let mut high = SendStream { -1373 id: id_high, -1374 state: &mut server, -1375 pending: &mut pending, -1376 conn_state: &state, -1377 }; -1378 high.set_priority(1).unwrap(); -1379 high.write(b"high").unwrap(); -1380 -1381 let mut buf = Vec::with_capacity(40); -1382 let meta = server.write_stream_frames(&mut buf, true, &mut Default::default()); -1383 assert_eq!(meta[0].id, id_high); -1384 assert_eq!(meta[1].id, id_mid); -1385 assert_eq!(meta[2].id, id_low); -1386 -1387 assert!(!server.can_send_stream_data()); -1388 assert_eq!(server.pending.len(), 0); -1389 } -1390 -1391 #[test] -1392 fn requeue_stream_priority() { -1393 let mut server = make(Side::Server); -1394 server.set_params(&TransportParameters { -1395 initial_max_streams_bidi: 3u32.into(), -1396 initial_max_data: 1000u32.into(), -1397 initial_max_stream_data_bidi_remote: 1000u32.into(), -1398 ..TransportParameters::default() -1399 }); -1400 -1401 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1402 let mut streams = Streams { -1403 state: &mut server, -1404 conn_state: &state, -1405 }; +1369 +1370 let id_high = streams.open(Dir::Bi).unwrap(); +1371 let id_mid = streams.open(Dir::Bi).unwrap(); +1372 +1373 let mut mid = SendStream { +1374 id: id_mid, +1375 state: &mut server, +1376 pending: &mut pending, +1377 conn_state: &state, +1378 }; +1379 assert_eq!(mid.write(b"mid").unwrap(), 3); +1380 assert_eq!(server.pending.len(), 1); +1381 +1382 let mut high = SendStream { +1383 id: id_high, +1384 state: &mut server, +1385 pending: &mut pending, +1386 conn_state: &state, +1387 }; +1388 high.set_priority(1).unwrap(); +1389 assert_eq!(high.write(&[0; 200]).unwrap(), 200); +1390 assert_eq!(server.pending.len(), 2); +1391 +1392 // Requeue the high priority stream to lowest priority. The initial send +1393 // still uses high priority since it's queued that way. After that it will +1394 // switch to low priority +1395 let mut high = SendStream { +1396 id: id_high, +1397 state: &mut server, +1398 pending: &mut pending, +1399 conn_state: &state, +1400 }; +1401 high.set_priority(-1).unwrap(); +1402 +1403 let meta = server.write_frames_for_test(40, true); +1404 assert_eq!(meta.len(), 1); +1405 assert_eq!(meta[0].id, id_high); 1406 -1407 let id_high = streams.open(Dir::Bi).unwrap(); -1408 let id_mid = streams.open(Dir::Bi).unwrap(); +1407 // After requeuing we should end up with 2 priorities - not 3 +1408 assert_eq!(server.pending.len(), 2); 1409 -1410 let mut mid = SendStream { -1411 id: id_mid, -1412 state: &mut server, -1413 pending: &mut pending, -1414 conn_state: &state, -1415 }; -1416 assert_eq!(mid.write(b"mid").unwrap(), 3); -1417 assert_eq!(server.pending.len(), 1); -1418 -1419 let mut high = SendStream { -1420 id: id_high, -1421 state: &mut server, -1422 pending: &mut pending, -1423 conn_state: &state, -1424 }; -1425 high.set_priority(1).unwrap(); -1426 assert_eq!(high.write(&[0; 200]).unwrap(), 200); -1427 assert_eq!(server.pending.len(), 2); -1428 -1429 // Requeue the high priority stream to lowest priority. The initial send -1430 // still uses high priority since it's queued that way. After that it will -1431 // switch to low priority -1432 let mut high = SendStream { -1433 id: id_high, -1434 state: &mut server, -1435 pending: &mut pending, -1436 conn_state: &state, -1437 }; -1438 high.set_priority(-1).unwrap(); -1439 -1440 let mut buf = Vec::with_capacity(1000).limit(40); -1441 let meta = server.write_stream_frames(&mut buf, true, &mut Default::default()); -1442 assert_eq!(meta.len(), 1); -1443 assert_eq!(meta[0].id, id_high); -1444 -1445 // After requeuing we should end up with 2 priorities - not 3 -1446 assert_eq!(server.pending.len(), 2); -1447 -1448 let mut buf = buf.into_inner(); +1410 // Send the remaining data. The initial mid priority one should go first now +1411 let meta = server.write_frames_for_test(1000 - 40, true); +1412 assert_eq!(meta.len(), 2); +1413 assert_eq!(meta[0].id, id_mid); +1414 assert_eq!(meta[1].id, id_high); +1415 +1416 assert!(!server.can_send_stream_data()); +1417 assert_eq!(server.pending.len(), 0); +1418 } +1419 +1420 #[test] +1421 fn same_stream_priority() { +1422 for fair in [true, false] { +1423 let mut server = make(Side::Server); +1424 server.set_params(&TransportParameters { +1425 initial_max_streams_bidi: 3u32.into(), +1426 initial_max_data: 300u32.into(), +1427 initial_max_stream_data_bidi_remote: 300u32.into(), +1428 ..TransportParameters::default() +1429 }); +1430 +1431 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1432 let mut streams = Streams { +1433 state: &mut server, +1434 conn_state: &state, +1435 }; +1436 +1437 // a, b and c all have the same priority +1438 let id_a = streams.open(Dir::Bi).unwrap(); +1439 let id_b = streams.open(Dir::Bi).unwrap(); +1440 let id_c = streams.open(Dir::Bi).unwrap(); +1441 +1442 let mut stream_a = SendStream { +1443 id: id_a, +1444 state: &mut server, +1445 pending: &mut pending, +1446 conn_state: &state, +1447 }; +1448 stream_a.write(&[b'a'; 100]).unwrap(); 1449 -1450 // Send the remaining data. The initial mid priority one should go first now -1451 let meta = server.write_stream_frames(&mut buf, true, &mut Default::default()); -1452 assert_eq!(meta.len(), 2); -1453 assert_eq!(meta[0].id, id_mid); -1454 assert_eq!(meta[1].id, id_high); -1455 -1456 assert!(!server.can_send_stream_data()); -1457 assert_eq!(server.pending.len(), 0); -1458 } -1459 -1460 #[test] -1461 fn same_stream_priority() { -1462 for fair in [true, false] { -1463 let mut server = make(Side::Server); -1464 server.set_params(&TransportParameters { -1465 initial_max_streams_bidi: 3u32.into(), -1466 initial_max_data: 300u32.into(), -1467 initial_max_stream_data_bidi_remote: 300u32.into(), -1468 ..TransportParameters::default() -1469 }); -1470 -1471 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1472 let mut streams = Streams { -1473 state: &mut server, -1474 conn_state: &state, -1475 }; +1450 let mut stream_b = SendStream { +1451 id: id_b, +1452 state: &mut server, +1453 pending: &mut pending, +1454 conn_state: &state, +1455 }; +1456 stream_b.write(&[b'b'; 100]).unwrap(); +1457 +1458 let mut stream_c = SendStream { +1459 id: id_c, +1460 state: &mut server, +1461 pending: &mut pending, +1462 conn_state: &state, +1463 }; +1464 stream_c.write(&[b'c'; 100]).unwrap(); +1465 +1466 let mut metas = vec![]; +1467 +1468 // loop until all the streams are written +1469 loop { +1470 let meta = server.write_frames_for_test(40, fair); +1471 if meta.is_empty() { +1472 break; +1473 } +1474 metas.extend(meta); +1475 } 1476 -1477 // a, b and c all have the same priority -1478 let id_a = streams.open(Dir::Bi).unwrap(); -1479 let id_b = streams.open(Dir::Bi).unwrap(); -1480 let id_c = streams.open(Dir::Bi).unwrap(); -1481 -1482 let mut stream_a = SendStream { -1483 id: id_a, -1484 state: &mut server, -1485 pending: &mut pending, -1486 conn_state: &state, -1487 }; -1488 stream_a.write(&[b'a'; 100]).unwrap(); -1489 -1490 let mut stream_b = SendStream { -1491 id: id_b, -1492 state: &mut server, -1493 pending: &mut pending, -1494 conn_state: &state, -1495 }; -1496 stream_b.write(&[b'b'; 100]).unwrap(); -1497 -1498 let mut stream_c = SendStream { -1499 id: id_c, -1500 state: &mut server, -1501 pending: &mut pending, -1502 conn_state: &state, -1503 }; -1504 stream_c.write(&[b'c'; 100]).unwrap(); -1505 -1506 let mut metas = vec![]; -1507 let mut buf = Vec::with_capacity(1024); +1477 assert!(!server.can_send_stream_data()); +1478 assert_eq!(server.pending.len(), 0); +1479 +1480 let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>(); +1481 if fair { +1482 // When fairness is enabled, if we run out of buffer space to write out a stream, +1483 // the stream is re-queued after all the streams with the same priority. +1484 assert_eq!( +1485 stream_ids, +1486 vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c] +1487 ); +1488 } else { +1489 // When fairness is disabled the stream is re-queued before all the other streams +1490 // with the same priority. +1491 assert_eq!( +1492 stream_ids, +1493 vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c] +1494 ); +1495 } +1496 } +1497 } +1498 +1499 #[test] +1500 fn unfair_priority_bump() { +1501 let mut server = make(Side::Server); +1502 server.set_params(&TransportParameters { +1503 initial_max_streams_bidi: 3u32.into(), +1504 initial_max_data: 300u32.into(), +1505 initial_max_stream_data_bidi_remote: 300u32.into(), +1506 ..TransportParameters::default() +1507 }); 1508 -1509 // loop until all the streams are written -1510 loop { -1511 let mut chunk_buf = buf.limit(40); -1512 let meta = -1513 server.write_stream_frames(&mut chunk_buf, fair, &mut Default::default()); -1514 if meta.is_empty() { -1515 break; -1516 } -1517 metas.extend(meta); -1518 buf = chunk_buf.into_inner(); -1519 } -1520 -1521 assert!(!server.can_send_stream_data()); -1522 assert_eq!(server.pending.len(), 0); -1523 -1524 let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>(); -1525 if fair { -1526 // When fairness is enabled, if we run out of buffer space to write out a stream, -1527 // the stream is re-queued after all the streams with the same priority. -1528 assert_eq!( -1529 stream_ids, -1530 vec![id_a, id_b, id_c, id_a, id_b, id_c, id_a, id_b, id_c] -1531 ); -1532 } else { -1533 // When fairness is disabled the stream is re-queued before all the other streams -1534 // with the same priority. -1535 assert_eq!( -1536 stream_ids, -1537 vec![id_a, id_a, id_a, id_b, id_b, id_b, id_c, id_c, id_c] -1538 ); -1539 } -1540 } -1541 } +1509 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1510 let mut streams = Streams { +1511 state: &mut server, +1512 conn_state: &state, +1513 }; +1514 +1515 // a, and b have the same priority, c has higher priority +1516 let id_a = streams.open(Dir::Bi).unwrap(); +1517 let id_b = streams.open(Dir::Bi).unwrap(); +1518 let id_c = streams.open(Dir::Bi).unwrap(); +1519 +1520 let mut stream_a = SendStream { +1521 id: id_a, +1522 state: &mut server, +1523 pending: &mut pending, +1524 conn_state: &state, +1525 }; +1526 stream_a.write(&[b'a'; 100]).unwrap(); +1527 +1528 let mut stream_b = SendStream { +1529 id: id_b, +1530 state: &mut server, +1531 pending: &mut pending, +1532 conn_state: &state, +1533 }; +1534 stream_b.write(&[b'b'; 100]).unwrap(); +1535 +1536 let mut metas = vec![]; +1537 +1538 // Write the first chunk of stream_a +1539 let meta = server.write_frames_for_test(40, false); +1540 assert!(!meta.is_empty()); +1541 metas.extend(meta); 1542 -1543 #[test] -1544 fn unfair_priority_bump() { -1545 let mut server = make(Side::Server); -1546 server.set_params(&TransportParameters { -1547 initial_max_streams_bidi: 3u32.into(), -1548 initial_max_data: 300u32.into(), -1549 initial_max_stream_data_bidi_remote: 300u32.into(), -1550 ..TransportParameters::default() -1551 }); +1543 // Queue stream_c which has higher priority +1544 let mut stream_c = SendStream { +1545 id: id_c, +1546 state: &mut server, +1547 pending: &mut pending, +1548 conn_state: &state, +1549 }; +1550 stream_c.set_priority(1).unwrap(); +1551 stream_c.write(&[b'b'; 100]).unwrap(); 1552 -1553 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1554 let mut streams = Streams { -1555 state: &mut server, -1556 conn_state: &state, -1557 }; -1558 -1559 // a, and b have the same priority, c has higher priority -1560 let id_a = streams.open(Dir::Bi).unwrap(); -1561 let id_b = streams.open(Dir::Bi).unwrap(); -1562 let id_c = streams.open(Dir::Bi).unwrap(); -1563 -1564 let mut stream_a = SendStream { -1565 id: id_a, -1566 state: &mut server, -1567 pending: &mut pending, -1568 conn_state: &state, -1569 }; -1570 stream_a.write(&[b'a'; 100]).unwrap(); -1571 -1572 let mut stream_b = SendStream { -1573 id: id_b, -1574 state: &mut server, -1575 pending: &mut pending, -1576 conn_state: &state, -1577 }; -1578 stream_b.write(&[b'b'; 100]).unwrap(); -1579 -1580 let mut metas = vec![]; -1581 let buf = Vec::with_capacity(1024); -1582 -1583 // Write the first chunk of stream_a -1584 let mut chunk_buf = buf.limit(40); -1585 let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default()); -1586 let mut buf = chunk_buf.into_inner(); -1587 assert!(!meta.is_empty()); -1588 metas.extend(meta); -1589 -1590 // Queue stream_c which has higher priority -1591 let mut stream_c = SendStream { -1592 id: id_c, -1593 state: &mut server, +1553 // loop until all the streams are written +1554 loop { +1555 let meta = server.write_frames_for_test(40, false); +1556 if meta.is_empty() { +1557 break; +1558 } +1559 metas.extend(meta); +1560 } +1561 +1562 assert!(!server.can_send_stream_data()); +1563 assert_eq!(server.pending.len(), 0); +1564 +1565 let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>(); +1566 assert_eq!( +1567 stream_ids, +1568 // stream_c bumps stream_b but doesn't bump stream_a which had already been partly +1569 // written out +1570 vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b] +1571 ); +1572 } +1573 +1574 #[test] +1575 fn stop_finished() { +1576 let mut client = make(Side::Client); +1577 let id = StreamId::new(Side::Server, Dir::Uni, 0); +1578 // Server finishes stream +1579 let _ = client +1580 .received( +1581 frame::Stream { +1582 id, +1583 offset: 0, +1584 fin: true, +1585 data: Bytes::from_static(&[0; 32]), +1586 }, +1587 32, +1588 ) +1589 .unwrap(); +1590 let mut pending = Retransmits::default(); +1591 let mut stream = RecvStream { +1592 id, +1593 state: &mut client, 1594 pending: &mut pending, -1595 conn_state: &state, -1596 }; -1597 stream_c.set_priority(1).unwrap(); -1598 stream_c.write(&[b'b'; 100]).unwrap(); +1595 }; +1596 stream.stop(0u32.into()).unwrap(); +1597 assert!(client.recv.get_mut(&id).is_none(), "stream is freed"); +1598 } 1599 -1600 // loop until all the streams are written -1601 loop { -1602 let mut chunk_buf = buf.limit(40); -1603 let meta = server.write_stream_frames(&mut chunk_buf, false, &mut Default::default()); -1604 buf = chunk_buf.into_inner(); -1605 if meta.is_empty() { -1606 break; -1607 } -1608 metas.extend(meta); -1609 } -1610 -1611 assert!(!server.can_send_stream_data()); -1612 assert_eq!(server.pending.len(), 0); -1613 -1614 let stream_ids = metas.iter().map(|m| m.id).collect::<Vec<_>>(); -1615 assert_eq!( -1616 stream_ids, -1617 // stream_c bumps stream_b but doesn't bump stream_a which had already been partly -1618 // written out -1619 vec![id_a, id_a, id_a, id_c, id_c, id_c, id_b, id_b, id_b] -1620 ); -1621 } -1622 -1623 #[test] -1624 fn stop_finished() { -1625 let mut client = make(Side::Client); -1626 let id = StreamId::new(Side::Server, Dir::Uni, 0); -1627 // Server finishes stream -1628 let _ = client -1629 .received( -1630 frame::Stream { -1631 id, -1632 offset: 0, -1633 fin: true, -1634 data: Bytes::from_static(&[0; 32]), -1635 }, -1636 32, -1637 ) -1638 .unwrap(); -1639 let mut pending = Retransmits::default(); -1640 let mut stream = RecvStream { -1641 id, -1642 state: &mut client, -1643 pending: &mut pending, -1644 }; -1645 stream.stop(0u32.into()).unwrap(); -1646 assert!(client.recv.get_mut(&id).is_none(), "stream is freed"); -1647 } -1648 -1649 // Verify that a stream that's been reset doesn't cause the appearance of pending data -1650 #[test] -1651 fn reset_stream_cannot_send() { -1652 let mut server = make(Side::Server); -1653 server.set_params(&TransportParameters { -1654 initial_max_streams_uni: 1u32.into(), -1655 initial_max_data: 42u32.into(), -1656 initial_max_stream_data_uni: 42u32.into(), -1657 ..TransportParameters::default() -1658 }); -1659 let (mut pending, state) = (Retransmits::default(), ConnState::established()); -1660 let mut streams = Streams { -1661 state: &mut server, -1662 conn_state: &state, -1663 }; -1664 -1665 let id = streams.open(Dir::Uni).unwrap(); -1666 let mut stream = SendStream { -1667 id, -1668 state: &mut server, -1669 pending: &mut pending, -1670 conn_state: &state, -1671 }; -1672 stream.write(b"hello").unwrap(); -1673 stream.reset(0u32.into()).unwrap(); -1674 -1675 assert_eq!(pending.reset_stream, &[(id, 0u32.into())]); -1676 assert!(!server.can_send_stream_data()); -1677 } -1678 -1679 #[test] -1680 fn stream_limit_fixed() { -1681 let mut client = make(Side::Client); -1682 // Open streams 0-127 -1683 assert_eq!( -1684 client.received( -1685 frame::Stream { -1686 id: StreamId::new(Side::Server, Dir::Uni, 127), -1687 offset: 0, -1688 fin: true, -1689 data: Bytes::from_static(&[]), -1690 }, -1691 0 -1692 ), -1693 Ok(ShouldTransmit(false)) -1694 ); -1695 // Try to open stream 128, exceeding limit -1696 assert_eq!( -1697 client -1698 .received( -1699 frame::Stream { -1700 id: StreamId::new(Side::Server, Dir::Uni, 128), -1701 offset: 0, -1702 fin: true, -1703 data: Bytes::from_static(&[]), -1704 }, -1705 0 -1706 ) -1707 .unwrap_err() -1708 .code, -1709 TransportErrorCode::STREAM_LIMIT_ERROR -1710 ); -1711 -1712 // Free stream 127 -1713 let mut pending = Retransmits::default(); -1714 let mut stream = RecvStream { -1715 id: StreamId::new(Side::Server, Dir::Uni, 127), -1716 state: &mut client, -1717 pending: &mut pending, -1718 }; -1719 stream.stop(0u32.into()).unwrap(); -1720 -1721 // Open stream 128 -1722 assert_eq!( -1723 client.received( -1724 frame::Stream { -1725 id: StreamId::new(Side::Server, Dir::Uni, 128), -1726 offset: 0, -1727 fin: true, -1728 data: Bytes::from_static(&[]), -1729 }, -1730 0 -1731 ), -1732 Ok(ShouldTransmit(false)) -1733 ); -1734 } -1735 -1736 #[test] -1737 fn stream_limit_grows() { -1738 let mut client = make(Side::Client); -1739 // Open streams 0-127 -1740 assert_eq!( -1741 client.received( -1742 frame::Stream { -1743 id: StreamId::new(Side::Server, Dir::Uni, 127), -1744 offset: 0, -1745 fin: true, -1746 data: Bytes::from_static(&[]), -1747 }, -1748 0 -1749 ), -1750 Ok(ShouldTransmit(false)) -1751 ); -1752 // Try to open stream 128, exceeding limit -1753 assert_eq!( -1754 client -1755 .received( -1756 frame::Stream { -1757 id: StreamId::new(Side::Server, Dir::Uni, 128), -1758 offset: 0, -1759 fin: true, -1760 data: Bytes::from_static(&[]), -1761 }, -1762 0 -1763 ) -1764 .unwrap_err() -1765 .code, -1766 TransportErrorCode::STREAM_LIMIT_ERROR -1767 ); -1768 -1769 // Relax limit by one -1770 client.set_max_concurrent(Dir::Uni, 129u32.into()); -1771 -1772 // Open stream 128 -1773 assert_eq!( -1774 client.received( -1775 frame::Stream { -1776 id: StreamId::new(Side::Server, Dir::Uni, 128), -1777 offset: 0, -1778 fin: true, -1779 data: Bytes::from_static(&[]), -1780 }, -1781 0 -1782 ), -1783 Ok(ShouldTransmit(false)) -1784 ); -1785 } -1786 -1787 #[test] -1788 fn stream_limit_shrinks() { -1789 let mut client = make(Side::Client); -1790 // Open streams 0-127 -1791 assert_eq!( -1792 client.received( -1793 frame::Stream { -1794 id: StreamId::new(Side::Server, Dir::Uni, 127), -1795 offset: 0, -1796 fin: true, -1797 data: Bytes::from_static(&[]), -1798 }, -1799 0 -1800 ), -1801 Ok(ShouldTransmit(false)) -1802 ); -1803 -1804 // Tighten limit by one -1805 client.set_max_concurrent(Dir::Uni, 127u32.into()); -1806 -1807 // Free stream 127 -1808 let mut pending = Retransmits::default(); -1809 let mut stream = RecvStream { -1810 id: StreamId::new(Side::Server, Dir::Uni, 127), -1811 state: &mut client, -1812 pending: &mut pending, -1813 }; -1814 stream.stop(0u32.into()).unwrap(); +1600 // Verify that a stream that's been reset doesn't cause the appearance of pending data +1601 #[test] +1602 fn reset_stream_cannot_send() { +1603 let mut server = make(Side::Server); +1604 server.set_params(&TransportParameters { +1605 initial_max_streams_uni: 1u32.into(), +1606 initial_max_data: 42u32.into(), +1607 initial_max_stream_data_uni: 42u32.into(), +1608 ..TransportParameters::default() +1609 }); +1610 let (mut pending, state) = (Retransmits::default(), ConnState::established()); +1611 let mut streams = Streams { +1612 state: &mut server, +1613 conn_state: &state, +1614 }; +1615 +1616 let id = streams.open(Dir::Uni).unwrap(); +1617 let mut stream = SendStream { +1618 id, +1619 state: &mut server, +1620 pending: &mut pending, +1621 conn_state: &state, +1622 }; +1623 stream.write(b"hello").unwrap(); +1624 stream.reset(0u32.into()).unwrap(); +1625 +1626 assert_eq!(pending.reset_stream, &[(id, 0u32.into())]); +1627 assert!(!server.can_send_stream_data()); +1628 } +1629 +1630 #[test] +1631 fn stream_limit_fixed() { +1632 let mut client = make(Side::Client); +1633 // Open streams 0-127 +1634 assert_eq!( +1635 client.received( +1636 frame::Stream { +1637 id: StreamId::new(Side::Server, Dir::Uni, 127), +1638 offset: 0, +1639 fin: true, +1640 data: Bytes::from_static(&[]), +1641 }, +1642 0 +1643 ), +1644 Ok(ShouldTransmit(false)) +1645 ); +1646 // Try to open stream 128, exceeding limit +1647 assert_eq!( +1648 client +1649 .received( +1650 frame::Stream { +1651 id: StreamId::new(Side::Server, Dir::Uni, 128), +1652 offset: 0, +1653 fin: true, +1654 data: Bytes::from_static(&[]), +1655 }, +1656 0 +1657 ) +1658 .unwrap_err() +1659 .code, +1660 TransportErrorCode::STREAM_LIMIT_ERROR +1661 ); +1662 +1663 // Free stream 127 +1664 let mut pending = Retransmits::default(); +1665 let mut stream = RecvStream { +1666 id: StreamId::new(Side::Server, Dir::Uni, 127), +1667 state: &mut client, +1668 pending: &mut pending, +1669 }; +1670 stream.stop(0u32.into()).unwrap(); +1671 +1672 // Open stream 128 +1673 assert_eq!( +1674 client.received( +1675 frame::Stream { +1676 id: StreamId::new(Side::Server, Dir::Uni, 128), +1677 offset: 0, +1678 fin: true, +1679 data: Bytes::from_static(&[]), +1680 }, +1681 0 +1682 ), +1683 Ok(ShouldTransmit(false)) +1684 ); +1685 } +1686 +1687 #[test] +1688 fn stream_limit_grows() { +1689 let mut client = make(Side::Client); +1690 // Open streams 0-127 +1691 assert_eq!( +1692 client.received( +1693 frame::Stream { +1694 id: StreamId::new(Side::Server, Dir::Uni, 127), +1695 offset: 0, +1696 fin: true, +1697 data: Bytes::from_static(&[]), +1698 }, +1699 0 +1700 ), +1701 Ok(ShouldTransmit(false)) +1702 ); +1703 // Try to open stream 128, exceeding limit +1704 assert_eq!( +1705 client +1706 .received( +1707 frame::Stream { +1708 id: StreamId::new(Side::Server, Dir::Uni, 128), +1709 offset: 0, +1710 fin: true, +1711 data: Bytes::from_static(&[]), +1712 }, +1713 0 +1714 ) +1715 .unwrap_err() +1716 .code, +1717 TransportErrorCode::STREAM_LIMIT_ERROR +1718 ); +1719 +1720 // Relax limit by one +1721 client.set_max_concurrent(Dir::Uni, 129u32.into()); +1722 +1723 // Open stream 128 +1724 assert_eq!( +1725 client.received( +1726 frame::Stream { +1727 id: StreamId::new(Side::Server, Dir::Uni, 128), +1728 offset: 0, +1729 fin: true, +1730 data: Bytes::from_static(&[]), +1731 }, +1732 0 +1733 ), +1734 Ok(ShouldTransmit(false)) +1735 ); +1736 } +1737 +1738 #[test] +1739 fn stream_limit_shrinks() { +1740 let mut client = make(Side::Client); +1741 // Open streams 0-127 +1742 assert_eq!( +1743 client.received( +1744 frame::Stream { +1745 id: StreamId::new(Side::Server, Dir::Uni, 127), +1746 offset: 0, +1747 fin: true, +1748 data: Bytes::from_static(&[]), +1749 }, +1750 0 +1751 ), +1752 Ok(ShouldTransmit(false)) +1753 ); +1754 +1755 // Tighten limit by one +1756 client.set_max_concurrent(Dir::Uni, 127u32.into()); +1757 +1758 // Free stream 127 +1759 let mut pending = Retransmits::default(); +1760 let mut stream = RecvStream { +1761 id: StreamId::new(Side::Server, Dir::Uni, 127), +1762 state: &mut client, +1763 pending: &mut pending, +1764 }; +1765 stream.stop(0u32.into()).unwrap(); +1766 +1767 // Try to open stream 128, still exceeding limit +1768 assert_eq!( +1769 client +1770 .received( +1771 frame::Stream { +1772 id: StreamId::new(Side::Server, Dir::Uni, 128), +1773 offset: 0, +1774 fin: true, +1775 data: Bytes::from_static(&[]), +1776 }, +1777 0 +1778 ) +1779 .unwrap_err() +1780 .code, +1781 TransportErrorCode::STREAM_LIMIT_ERROR +1782 ); +1783 +1784 // Free stream 126 +1785 assert_eq!( +1786 client.received_reset(frame::ResetStream { +1787 id: StreamId::new(Side::Server, Dir::Uni, 126), +1788 error_code: 0u32.into(), +1789 final_offset: 0u32.into(), +1790 }), +1791 Ok(ShouldTransmit(false)) +1792 ); +1793 let mut pending = Retransmits::default(); +1794 let mut stream = RecvStream { +1795 id: StreamId::new(Side::Server, Dir::Uni, 126), +1796 state: &mut client, +1797 pending: &mut pending, +1798 }; +1799 stream.stop(0u32.into()).unwrap(); +1800 +1801 // Open stream 128 +1802 assert_eq!( +1803 client.received( +1804 frame::Stream { +1805 id: StreamId::new(Side::Server, Dir::Uni, 128), +1806 offset: 0, +1807 fin: true, +1808 data: Bytes::from_static(&[]), +1809 }, +1810 0 +1811 ), +1812 Ok(ShouldTransmit(false)) +1813 ); +1814 } 1815 -1816 // Try to open stream 128, still exceeding limit -1817 assert_eq!( -1818 client -1819 .received( -1820 frame::Stream { -1821 id: StreamId::new(Side::Server, Dir::Uni, 128), -1822 offset: 0, -1823 fin: true, -1824 data: Bytes::from_static(&[]), -1825 }, -1826 0 -1827 ) -1828 .unwrap_err() -1829 .code, -1830 TransportErrorCode::STREAM_LIMIT_ERROR -1831 ); -1832 -1833 // Free stream 126 -1834 assert_eq!( -1835 client.received_reset(frame::ResetStream { -1836 id: StreamId::new(Side::Server, Dir::Uni, 126), -1837 error_code: 0u32.into(), -1838 final_offset: 0u32.into(), -1839 }), -1840 Ok(ShouldTransmit(false)) -1841 ); -1842 let mut pending = Retransmits::default(); -1843 let mut stream = RecvStream { -1844 id: StreamId::new(Side::Server, Dir::Uni, 126), -1845 state: &mut client, -1846 pending: &mut pending, -1847 }; -1848 stream.stop(0u32.into()).unwrap(); -1849 -1850 // Open stream 128 -1851 assert_eq!( -1852 client.received( -1853 frame::Stream { -1854 id: StreamId::new(Side::Server, Dir::Uni, 128), -1855 offset: 0, -1856 fin: true, -1857 data: Bytes::from_static(&[]), -1858 }, -1859 0 -1860 ), -1861 Ok(ShouldTransmit(false)) -1862 ); -1863 } -1864 -1865 #[test] -1866 fn remote_stream_capacity() { -1867 let mut client = make(Side::Client); -1868 for _ in 0..2 { -1869 client.set_max_concurrent(Dir::Uni, 200u32.into()); -1870 client.set_max_concurrent(Dir::Bi, 201u32.into()); -1871 assert_eq!(client.recv.len(), 200 + 201); -1872 assert_eq!(client.max_remote[Dir::Uni as usize], 200); -1873 assert_eq!(client.max_remote[Dir::Bi as usize], 201); -1874 } -1875 } -1876 -1877 #[test] -1878 fn expand_receive_window() { -1879 let mut server = make(Side::Server); -1880 let new_receive_window = 2 * server.receive_window as u32; -1881 let expanded = server.set_receive_window(new_receive_window.into()); -1882 assert!(expanded); -1883 assert_eq!(server.receive_window, new_receive_window as u64); -1884 assert_eq!(server.local_max_data, new_receive_window as u64); -1885 assert_eq!(server.receive_window_shrink_debt, 0); -1886 let prev_local_max_data = server.local_max_data; -1887 -1888 // credit, expecting all of them added to local_max_data -1889 let credits = 1024u64; -1890 let should_transmit = server.add_read_credits(credits); -1891 assert_eq!(server.receive_window_shrink_debt, 0); -1892 assert_eq!(server.local_max_data, prev_local_max_data + credits); -1893 assert!(should_transmit.should_transmit()); -1894 } -1895 -1896 #[test] -1897 fn shrink_receive_window() { -1898 let mut server = make(Side::Server); -1899 let new_receive_window = server.receive_window as u32 / 2; -1900 let prev_local_max_data = server.local_max_data; -1901 -1902 // shrink the receive_winbow, local_max_data is not expected to be changed -1903 let shrink_diff = server.receive_window - new_receive_window as u64; -1904 let expanded = server.set_receive_window(new_receive_window.into()); -1905 assert!(!expanded); -1906 assert_eq!(server.receive_window, new_receive_window as u64); -1907 assert_eq!(server.local_max_data, prev_local_max_data); -1908 assert_eq!(server.receive_window_shrink_debt, shrink_diff); -1909 let prev_local_max_data = server.local_max_data; -1910 -1911 // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt -1912 let credits = 1024u64; -1913 for _ in 0..2 { -1914 let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits; -1915 let should_transmit = server.add_read_credits(credits); -1916 assert_eq!( -1917 server.receive_window_shrink_debt, -1918 expected_receive_window_shrink_debt -1919 ); -1920 assert_eq!(server.local_max_data, prev_local_max_data); -1921 assert!(!should_transmit.should_transmit()); -1922 } -1923 -1924 // credit again which exceeds all remaining expected_receive_window_shrink_debt -1925 let credits = 1024 * 512; -1926 let prev_local_max_data = server.local_max_data; -1927 let expected_local_max_data = -1928 server.local_max_data + (credits - server.receive_window_shrink_debt); -1929 let _should_transmit = server.add_read_credits(credits); -1930 assert_eq!(server.receive_window_shrink_debt, 0); -1931 assert_eq!(server.local_max_data, expected_local_max_data); -1932 assert!(server.local_max_data > prev_local_max_data); -1933 -1934 // credit again, all should be added to local_max_data -1935 let credits = 1024 * 512; -1936 let expected_local_max_data = server.local_max_data + credits; -1937 let should_transmit = server.add_read_credits(credits); -1938 assert_eq!(server.receive_window_shrink_debt, 0); -1939 assert_eq!(server.local_max_data, expected_local_max_data); -1940 assert!(should_transmit.should_transmit()); -1941 } -1942 -1943 #[test] -1944 fn expand_send_window() { -1945 let mut server = make(Side::Server); -1946 -1947 let initial_send_window = server.send_window; -1948 let larger_send_window = initial_send_window * 2; -1949 -1950 // Set `initial_max_data` larger than `send_window` so we're limited by local flow control -1951 server.set_params(&TransportParameters { -1952 initial_max_data: VarInt::MAX, -1953 initial_max_stream_data_uni: VarInt::MAX, -1954 initial_max_streams_uni: VarInt::from_u32(100), -1955 ..TransportParameters::default() +1816 #[test] +1817 fn remote_stream_capacity() { +1818 let mut client = make(Side::Client); +1819 for _ in 0..2 { +1820 client.set_max_concurrent(Dir::Uni, 200u32.into()); +1821 client.set_max_concurrent(Dir::Bi, 201u32.into()); +1822 assert_eq!(client.recv.len(), 200 + 201); +1823 assert_eq!(client.max_remote[Dir::Uni as usize], 200); +1824 assert_eq!(client.max_remote[Dir::Bi as usize], 201); +1825 } +1826 } +1827 +1828 #[test] +1829 fn expand_receive_window() { +1830 let mut server = make(Side::Server); +1831 let new_receive_window = 2 * server.receive_window as u32; +1832 let expanded = server.set_receive_window(new_receive_window.into()); +1833 assert!(expanded); +1834 assert_eq!(server.receive_window, new_receive_window as u64); +1835 assert_eq!(server.local_max_data, new_receive_window as u64); +1836 assert_eq!(server.receive_window_shrink_debt, 0); +1837 let prev_local_max_data = server.local_max_data; +1838 +1839 // credit, expecting all of them added to local_max_data +1840 let credits = 1024u64; +1841 let should_transmit = server.add_read_credits(credits); +1842 assert_eq!(server.receive_window_shrink_debt, 0); +1843 assert_eq!(server.local_max_data, prev_local_max_data + credits); +1844 assert!(should_transmit.should_transmit()); +1845 } +1846 +1847 #[test] +1848 fn shrink_receive_window() { +1849 let mut server = make(Side::Server); +1850 let new_receive_window = server.receive_window as u32 / 2; +1851 let prev_local_max_data = server.local_max_data; +1852 +1853 // shrink the receive_winbow, local_max_data is not expected to be changed +1854 let shrink_diff = server.receive_window - new_receive_window as u64; +1855 let expanded = server.set_receive_window(new_receive_window.into()); +1856 assert!(!expanded); +1857 assert_eq!(server.receive_window, new_receive_window as u64); +1858 assert_eq!(server.local_max_data, prev_local_max_data); +1859 assert_eq!(server.receive_window_shrink_debt, shrink_diff); +1860 let prev_local_max_data = server.local_max_data; +1861 +1862 // credit twice, local_max_data does not change as it is absorbed by receive_window_shrink_debt +1863 let credits = 1024u64; +1864 for _ in 0..2 { +1865 let expected_receive_window_shrink_debt = server.receive_window_shrink_debt - credits; +1866 let should_transmit = server.add_read_credits(credits); +1867 assert_eq!( +1868 server.receive_window_shrink_debt, +1869 expected_receive_window_shrink_debt +1870 ); +1871 assert_eq!(server.local_max_data, prev_local_max_data); +1872 assert!(!should_transmit.should_transmit()); +1873 } +1874 +1875 // credit again which exceeds all remaining expected_receive_window_shrink_debt +1876 let credits = 1024 * 512; +1877 let prev_local_max_data = server.local_max_data; +1878 let expected_local_max_data = +1879 server.local_max_data + (credits - server.receive_window_shrink_debt); +1880 let _should_transmit = server.add_read_credits(credits); +1881 assert_eq!(server.receive_window_shrink_debt, 0); +1882 assert_eq!(server.local_max_data, expected_local_max_data); +1883 assert!(server.local_max_data > prev_local_max_data); +1884 +1885 // credit again, all should be added to local_max_data +1886 let credits = 1024 * 512; +1887 let expected_local_max_data = server.local_max_data + credits; +1888 let should_transmit = server.add_read_credits(credits); +1889 assert_eq!(server.receive_window_shrink_debt, 0); +1890 assert_eq!(server.local_max_data, expected_local_max_data); +1891 assert!(should_transmit.should_transmit()); +1892 } +1893 +1894 #[test] +1895 fn expand_send_window() { +1896 let mut server = make(Side::Server); +1897 +1898 let initial_send_window = server.send_window; +1899 let larger_send_window = initial_send_window * 2; +1900 +1901 // Set `initial_max_data` larger than `send_window` so we're limited by local flow control +1902 server.set_params(&TransportParameters { +1903 initial_max_data: VarInt::MAX, +1904 initial_max_stream_data_uni: VarInt::MAX, +1905 initial_max_streams_uni: VarInt::from_u32(100), +1906 ..TransportParameters::default() +1907 }); +1908 +1909 assert_eq!(server.write_limit(), initial_send_window); +1910 assert_eq!(server.poll(), None); +1911 +1912 let mut retransmits = Retransmits::default(); +1913 let conn_state = ConnState::established(); +1914 +1915 let stream_id = Streams { +1916 state: &mut server, +1917 conn_state: &conn_state, +1918 } +1919 .open(Dir::Uni) +1920 .expect("should be able to open a stream"); +1921 +1922 let mut stream = SendStream { +1923 id: stream_id, +1924 state: &mut server, +1925 pending: &mut retransmits, +1926 conn_state: &conn_state, +1927 }; +1928 +1929 // Check that the stream accepts `initial_send_window` bytes +1930 let initial_send_len = initial_send_window as usize; +1931 let data = vec![0xFFu8; initial_send_len]; +1932 +1933 assert_eq!(stream.write(&data), Ok(initial_send_len)); +1934 +1935 // Try to write the same data again, observe that it's blocked +1936 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +1937 +1938 // Check that we get a `Writable` event after increasing the send window +1939 stream.state.set_send_window(larger_send_window); +1940 assert_eq!( +1941 stream.state.poll(), +1942 Some(StreamEvent::Writable { id: stream_id }) +1943 ); +1944 +1945 // Check that the stream accepts the exact same amount of data again +1946 assert_eq!(stream.write(&data), Ok(initial_send_len)); +1947 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +1948 +1949 assert_eq!(stream.state.poll(), None); +1950 +1951 // Ack the data +1952 stream.state.received_ack_of(frame::StreamMeta { +1953 id: stream_id, +1954 offsets: 0..larger_send_window, +1955 fin: false, 1956 }); 1957 -1958 assert_eq!(server.write_limit(), initial_send_window); -1959 assert_eq!(server.poll(), None); -1960 -1961 let mut retransmits = Retransmits::default(); -1962 let conn_state = ConnState::established(); -1963 -1964 let stream_id = Streams { -1965 state: &mut server, -1966 conn_state: &conn_state, -1967 } -1968 .open(Dir::Uni) -1969 .expect("should be able to open a stream"); -1970 -1971 let mut stream = SendStream { -1972 id: stream_id, -1973 state: &mut server, -1974 pending: &mut retransmits, -1975 conn_state: &conn_state, -1976 }; -1977 -1978 // Check that the stream accepts `initial_send_window` bytes -1979 let initial_send_len = initial_send_window as usize; -1980 let data = vec![0xFFu8; initial_send_len]; -1981 -1982 assert_eq!(stream.write(&data), Ok(initial_send_len)); +1958 assert_eq!( +1959 stream.state.poll(), +1960 Some(StreamEvent::Writable { id: stream_id }) +1961 ); +1962 +1963 // Check that our full send window is available again +1964 assert_eq!(stream.write(&data), Ok(initial_send_len)); +1965 assert_eq!(stream.write(&data), Ok(initial_send_len)); +1966 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +1967 } +1968 +1969 #[test] +1970 fn shrink_send_window() { +1971 let mut server = make(Side::Server); +1972 +1973 let initial_send_window = server.send_window; +1974 let smaller_send_window = server.send_window / 2; +1975 +1976 // Set `initial_max_data` larger than `send_window` so we're limited by local flow control +1977 server.set_params(&TransportParameters { +1978 initial_max_data: VarInt::MAX, +1979 initial_max_stream_data_uni: VarInt::MAX, +1980 initial_max_streams_uni: VarInt::from_u32(100), +1981 ..TransportParameters::default() +1982 }); 1983 -1984 // Try to write the same data again, observe that it's blocked -1985 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +1984 assert_eq!(server.write_limit(), initial_send_window); +1985 assert_eq!(server.poll(), None); 1986 -1987 // Check that we get a `Writable` event after increasing the send window -1988 stream.state.set_send_window(larger_send_window); -1989 assert_eq!( -1990 stream.state.poll(), -1991 Some(StreamEvent::Writable { id: stream_id }) -1992 ); -1993 -1994 // Check that the stream accepts the exact same amount of data again -1995 assert_eq!(stream.write(&data), Ok(initial_send_len)); -1996 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -1997 -1998 assert_eq!(stream.state.poll(), None); -1999 -2000 // Ack the data -2001 stream.state.received_ack_of(frame::StreamMeta { -2002 id: stream_id, -2003 offsets: 0..larger_send_window, -2004 fin: false, -2005 }); -2006 -2007 assert_eq!( -2008 stream.state.poll(), -2009 Some(StreamEvent::Writable { id: stream_id }) -2010 ); +1987 let mut retransmits = Retransmits::default(); +1988 let conn_state = ConnState::established(); +1989 +1990 let stream_id = Streams { +1991 state: &mut server, +1992 conn_state: &conn_state, +1993 } +1994 .open(Dir::Uni) +1995 .expect("should be able to open a stream"); +1996 +1997 let mut stream = SendStream { +1998 id: stream_id, +1999 state: &mut server, +2000 pending: &mut retransmits, +2001 conn_state: &conn_state, +2002 }; +2003 +2004 let initial_send_len = initial_send_window as usize; +2005 +2006 let data = vec![0xFFu8; initial_send_len]; +2007 +2008 // Assert that the full send window is accepted +2009 assert_eq!(stream.write(&data), Ok(initial_send_len)); +2010 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); 2011 -2012 // Check that our full send window is available again -2013 assert_eq!(stream.write(&data), Ok(initial_send_len)); -2014 assert_eq!(stream.write(&data), Ok(initial_send_len)); -2015 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -2016 } -2017 -2018 #[test] -2019 fn shrink_send_window() { -2020 let mut server = make(Side::Server); -2021 -2022 let initial_send_window = server.send_window; -2023 let smaller_send_window = server.send_window / 2; -2024 -2025 // Set `initial_max_data` larger than `send_window` so we're limited by local flow control -2026 server.set_params(&TransportParameters { -2027 initial_max_data: VarInt::MAX, -2028 initial_max_stream_data_uni: VarInt::MAX, -2029 initial_max_streams_uni: VarInt::from_u32(100), -2030 ..TransportParameters::default() -2031 }); -2032 -2033 assert_eq!(server.write_limit(), initial_send_window); -2034 assert_eq!(server.poll(), None); -2035 -2036 let mut retransmits = Retransmits::default(); -2037 let conn_state = ConnState::established(); +2012 assert_eq!(stream.state.write_limit(), 0); +2013 assert_eq!(stream.state.poll(), None); +2014 +2015 // Shrink our send window, assert that it's still not writable +2016 stream.state.set_send_window(smaller_send_window); +2017 assert_eq!(stream.state.write_limit(), 0); +2018 assert_eq!(stream.state.poll(), None); +2019 +2020 // Assert that data is still not accepted +2021 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +2022 +2023 // Ack some data, assert that writes are still not accepted due to outstanding sends +2024 stream.state.received_ack_of(frame::StreamMeta { +2025 id: stream_id, +2026 offsets: 0..smaller_send_window, +2027 fin: false, +2028 }); +2029 +2030 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +2031 +2032 // Ack the rest of the data +2033 stream.state.received_ack_of(frame::StreamMeta { +2034 id: stream_id, +2035 offsets: smaller_send_window..initial_send_window, +2036 fin: false, +2037 }); 2038 -2039 let stream_id = Streams { -2040 state: &mut server, -2041 conn_state: &conn_state, -2042 } -2043 .open(Dir::Uni) -2044 .expect("should be able to open a stream"); +2039 // This should generate a `Writable` event +2040 assert_eq!( +2041 stream.state.poll(), +2042 Some(StreamEvent::Writable { id: stream_id }) +2043 ); +2044 assert_eq!(stream.state.write_limit(), smaller_send_window); 2045 -2046 let mut stream = SendStream { -2047 id: stream_id, -2048 state: &mut server, -2049 pending: &mut retransmits, -2050 conn_state: &conn_state, -2051 }; -2052 -2053 let initial_send_len = initial_send_window as usize; -2054 -2055 let data = vec![0xFFu8; initial_send_len]; -2056 -2057 // Assert that the full send window is accepted -2058 assert_eq!(stream.write(&data), Ok(initial_send_len)); -2059 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -2060 -2061 assert_eq!(stream.state.write_limit(), 0); -2062 assert_eq!(stream.state.poll(), None); -2063 -2064 // Shrink our send window, assert that it's still not writable -2065 stream.state.set_send_window(smaller_send_window); -2066 assert_eq!(stream.state.write_limit(), 0); -2067 assert_eq!(stream.state.poll(), None); -2068 -2069 // Assert that data is still not accepted -2070 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -2071 -2072 // Ack some data, assert that writes are still not accepted due to outstanding sends -2073 stream.state.received_ack_of(frame::StreamMeta { -2074 id: stream_id, -2075 offsets: 0..smaller_send_window, -2076 fin: false, -2077 }); -2078 -2079 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -2080 -2081 // Ack the rest of the data -2082 stream.state.received_ack_of(frame::StreamMeta { -2083 id: stream_id, -2084 offsets: smaller_send_window..initial_send_window, -2085 fin: false, -2086 }); -2087 -2088 // This should generate a `Writable` event -2089 assert_eq!( -2090 stream.state.poll(), -2091 Some(StreamEvent::Writable { id: stream_id }) -2092 ); -2093 assert_eq!(stream.state.write_limit(), smaller_send_window); -2094 -2095 // Assert that only `smaller_send_window` bytes are accepted -2096 assert_eq!(stream.write(&data), Ok(smaller_send_window as usize)); -2097 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); -2098 } -2099} \ No newline at end of file +2046 // Assert that only `smaller_send_window` bytes are accepted +2047 assert_eq!(stream.write(&data), Ok(smaller_send_window as usize)); +2048 assert_eq!(stream.write(&data), Err(WriteError::Blocked)); +2049 } +2050} \ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/frame.rs.html b/pr/323/docs/src/iroh_quinn_proto/frame.rs.html index bb19305c3..477b7a316 100644 --- a/pr/323/docs/src/iroh_quinn_proto/frame.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/frame.rs.html @@ -1,5 +1,5 @@ frame.rs - source

iroh_quinn_proto/
frame.rs

1use std::{
-2    fmt::{self, Write},
+2    fmt::{self, Display, Write},
 3    mem,
 4    net::{IpAddr, SocketAddr},
 5    ops::{Range, RangeInclusive},
@@ -20,2277 +20,2621 @@
 20#[cfg(feature = "arbitrary")]
 21use arbitrary::Arbitrary;
 22
-23#[derive(
-24    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
-25)]
-26#[display(rename_all = "SCREAMING_SNAKE_CASE")]
-27#[allow(missing_docs)]
-28#[func(
-29    pub(crate) const fn to_u64(self) -> u64,
-30    const fn from_u64(rev: u64) -> Option<Self>,
-31)]
-32pub enum FrameType {
-33    #[assoc(to_u64 = 0x00)]
-34    Padding,
-35    #[assoc(to_u64 = 0x01)]
-36    Ping,
-37    #[assoc(to_u64 = 0x02)]
-38    Ack,
-39    #[assoc(to_u64 = 0x03)]
-40    AckEcn,
-41    #[assoc(to_u64 = 0x04)]
-42    ResetStream,
-43    #[assoc(to_u64 = 0x05)]
-44    StopSending,
-45    #[assoc(to_u64 = 0x06)]
-46    Crypto,
-47    #[assoc(to_u64 = 0x07)]
-48    NewToken,
-49    // STREAM
-50    #[assoc(to_u64 = _0.to_u64())]
-51    Stream(StreamInfo),
-52    #[assoc(to_u64 = 0x10)]
-53    MaxData,
-54    #[assoc(to_u64 = 0x11)]
-55    MaxStreamData,
-56    #[assoc(to_u64 = 0x12)]
-57    MaxStreamsBidi,
-58    #[assoc(to_u64 = 0x13)]
-59    MaxStreamsUni,
-60    #[assoc(to_u64 = 0x14)]
-61    DataBlocked,
-62    #[assoc(to_u64 = 0x15)]
-63    StreamDataBlocked,
-64    #[assoc(to_u64 = 0x16)]
-65    StreamsBlockedBidi,
-66    #[assoc(to_u64 = 0x17)]
-67    StreamsBlockedUni,
-68    #[assoc(to_u64 = 0x18)]
-69    NewConnectionId,
-70    #[assoc(to_u64 = 0x19)]
-71    RetireConnectionId,
-72    #[assoc(to_u64 = 0x1a)]
-73    PathChallenge,
-74    #[assoc(to_u64 = 0x1b)]
-75    PathResponse,
-76    #[assoc(to_u64 = 0x1c)]
-77    ConnectionClose,
-78    #[assoc(to_u64 = 0x1d)]
-79    ApplicationClose,
-80    #[assoc(to_u64 = 0x1e)]
-81    HandshakeDone,
-82    // ACK Frequency
-83    #[assoc(to_u64 = 0xaf)]
-84    AckFrequency,
-85    #[assoc(to_u64 = 0x1f)]
-86    ImmediateAck,
-87    // DATAGRAM
-88    #[assoc(to_u64 = _0.to_u64())]
-89    Datagram(DatagramInfo),
-90    // ADDRESS DISCOVERY REPORT
-91    #[assoc(to_u64 = 0x9f81a6)]
-92    ObservedIpv4Addr,
-93    #[assoc(to_u64 = 0x9f81a7)]
-94    ObservedIpv6Addr,
-95    // Multipath
-96    #[assoc(to_u64 = 0x3e)]
-97    PathAck,
-98    #[assoc(to_u64 = 0x3f)]
-99    PathAckEcn,
-100    #[assoc(to_u64 = 0x3e75)]
-101    PathAbandon,
-102    #[assoc(to_u64 = 0x3e76)]
-103    PathStatusBackup,
-104    #[assoc(to_u64 = 0x3e77)]
-105    PathStatusAvailable,
-106    #[assoc(to_u64 = 0x3e78)]
-107    PathNewConnectionId,
-108    #[assoc(to_u64 = 0x3e79)]
-109    PathRetireConnectionId,
-110    #[assoc(to_u64 = 0x3e7a)]
-111    MaxPathId,
-112    #[assoc(to_u64 = 0x3e7b)]
-113    PathsBlocked,
-114    #[assoc(to_u64 = 0x3e7c)]
-115    PathCidsBlocked,
-116    // IROH'S NAT TRAVERSAL
-117    #[assoc(to_u64 = 0x3d7f90)]
-118    AddIpv4Address,
-119    #[assoc(to_u64 = 0x3d7f91)]
-120    AddIpv6Address,
-121    #[assoc(to_u64 = 0x3d7f92)]
-122    ReachOutAtIpv4,
-123    #[assoc(to_u64 = 0x3d7f93)]
-124    ReachOutAtIpv6,
-125    #[assoc(to_u64 = 0x3d7f94)]
-126    RemoveAddress,
-127}
-128
-129/// Encounter a frame ID that was not valid.
-130#[derive(Debug, PartialEq, Eq)]
-131pub struct InvalidFrameId(u64);
-132
-133impl TryFrom<u64> for FrameType {
-134    type Error = InvalidFrameId;
-135    fn try_from(value: u64) -> Result<Self, Self::Error> {
-136        match Self::from_u64(value) {
-137            Some(t) => Ok(t),
-138            None => {
-139                if DatagramInfo::VALUES.contains(&value) {
-140                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
-141                }
-142                if StreamInfo::VALUES.contains(&value) {
-143                    return Ok(Self::Stream(StreamInfo(value as u8)));
+23#[cfg(feature = "qlog")]
+24use super::connection::qlog::ToQlog;
+25
+26#[derive(
+27    Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display, enum_assoc::Assoc,
+28)]
+29#[display(rename_all = "SCREAMING_SNAKE_CASE")]
+30#[allow(missing_docs)]
+31#[func(
+32    pub(crate) const fn to_u64(self) -> u64,
+33    const fn from_u64(rev: u64) -> Option<Self>,
+34)]
+35pub enum FrameType {
+36    #[assoc(to_u64 = 0x00)]
+37    Padding,
+38    #[assoc(to_u64 = 0x01)]
+39    Ping,
+40    #[assoc(to_u64 = 0x02)]
+41    Ack,
+42    #[assoc(to_u64 = 0x03)]
+43    AckEcn,
+44    #[assoc(to_u64 = 0x04)]
+45    ResetStream,
+46    #[assoc(to_u64 = 0x05)]
+47    StopSending,
+48    #[assoc(to_u64 = 0x06)]
+49    Crypto,
+50    #[assoc(to_u64 = 0x07)]
+51    NewToken,
+52    // STREAM
+53    #[assoc(to_u64 = _0.to_u64())]
+54    Stream(StreamInfo),
+55    #[assoc(to_u64 = 0x10)]
+56    MaxData,
+57    #[assoc(to_u64 = 0x11)]
+58    MaxStreamData,
+59    #[assoc(to_u64 = 0x12)]
+60    MaxStreamsBidi,
+61    #[assoc(to_u64 = 0x13)]
+62    MaxStreamsUni,
+63    #[assoc(to_u64 = 0x14)]
+64    DataBlocked,
+65    #[assoc(to_u64 = 0x15)]
+66    StreamDataBlocked,
+67    #[assoc(to_u64 = 0x16)]
+68    StreamsBlockedBidi,
+69    #[assoc(to_u64 = 0x17)]
+70    StreamsBlockedUni,
+71    #[assoc(to_u64 = 0x18)]
+72    NewConnectionId,
+73    #[assoc(to_u64 = 0x19)]
+74    RetireConnectionId,
+75    #[assoc(to_u64 = 0x1a)]
+76    PathChallenge,
+77    #[assoc(to_u64 = 0x1b)]
+78    PathResponse,
+79    #[assoc(to_u64 = 0x1c)]
+80    ConnectionClose,
+81    #[assoc(to_u64 = 0x1d)]
+82    ApplicationClose,
+83    #[assoc(to_u64 = 0x1e)]
+84    HandshakeDone,
+85    // ACK Frequency
+86    #[assoc(to_u64 = 0xaf)]
+87    AckFrequency,
+88    #[assoc(to_u64 = 0x1f)]
+89    ImmediateAck,
+90    // DATAGRAM
+91    #[assoc(to_u64 = _0.to_u64())]
+92    Datagram(DatagramInfo),
+93    // ADDRESS DISCOVERY REPORT
+94    #[assoc(to_u64 = 0x9f81a6)]
+95    ObservedIpv4Addr,
+96    #[assoc(to_u64 = 0x9f81a7)]
+97    ObservedIpv6Addr,
+98    // Multipath
+99    #[assoc(to_u64 = 0x3e)]
+100    PathAck,
+101    #[assoc(to_u64 = 0x3f)]
+102    PathAckEcn,
+103    #[assoc(to_u64 = 0x3e75)]
+104    PathAbandon,
+105    #[assoc(to_u64 = 0x3e76)]
+106    PathStatusBackup,
+107    #[assoc(to_u64 = 0x3e77)]
+108    PathStatusAvailable,
+109    #[assoc(to_u64 = 0x3e78)]
+110    PathNewConnectionId,
+111    #[assoc(to_u64 = 0x3e79)]
+112    PathRetireConnectionId,
+113    #[assoc(to_u64 = 0x3e7a)]
+114    MaxPathId,
+115    #[assoc(to_u64 = 0x3e7b)]
+116    PathsBlocked,
+117    #[assoc(to_u64 = 0x3e7c)]
+118    PathCidsBlocked,
+119    // IROH'S NAT TRAVERSAL
+120    #[assoc(to_u64 = 0x3d7f90)]
+121    AddIpv4Address,
+122    #[assoc(to_u64 = 0x3d7f91)]
+123    AddIpv6Address,
+124    #[assoc(to_u64 = 0x3d7f92)]
+125    ReachOutAtIpv4,
+126    #[assoc(to_u64 = 0x3d7f93)]
+127    ReachOutAtIpv6,
+128    #[assoc(to_u64 = 0x3d7f94)]
+129    RemoveAddress,
+130}
+131
+132/// Encounter a frame ID that was not valid.
+133#[derive(Debug, PartialEq, Eq)]
+134pub struct InvalidFrameId(u64);
+135
+136impl TryFrom<u64> for FrameType {
+137    type Error = InvalidFrameId;
+138    fn try_from(value: u64) -> Result<Self, Self::Error> {
+139        match Self::from_u64(value) {
+140            Some(t) => Ok(t),
+141            None => {
+142                if DatagramInfo::VALUES.contains(&value) {
+143                    return Ok(Self::Datagram(DatagramInfo(value as u8)));
 144                }
-145                Err(InvalidFrameId(value))
-146            }
-147        }
-148    }
-149}
-150
-151impl FrameType {
-152    /// The encoded size of this [`FrameType`].
-153    const fn size(&self) -> usize {
-154        VarInt(self.to_u64()).size()
-155    }
-156}
-157
-158impl Decodable for FrameType {
-159    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-160        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
-161    }
-162}
-163
-164impl Encodable for FrameType {
-165    fn encode<B: BufMut>(&self, buf: &mut B) {
-166        buf.write_var(self.to_u64());
-167    }
-168}
-169
-170pub(crate) trait FrameStruct {
-171    /// Smallest number of bytes this type of frame is guaranteed to fit within.
-172    const SIZE_BOUND: usize;
-173}
-174
-175/// The type used to refer to [`FrameType`]s in closing and transport errors.
-176#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
-177pub enum MaybeFrame {
-178    /// Not attributed to any particular [`FrameType`].
-179    None,
-180    /// Attributed to some frame type this implementation does not recognize.
-181    #[display("UNKNOWN{:02x}", _0)]
-182    #[debug("Unknown{:02x}", _0)]
-183    Unknown(u64),
-184    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
-185    Known(FrameType),
-186}
-187
-188impl MaybeFrame {
-189    /// Encoded size of this [`MaybeFrame`].
-190    const fn size(&self) -> usize {
-191        match self {
-192            Self::None => VarInt(0).size(),
-193            Self::Unknown(other) => VarInt(*other).size(),
-194            Self::Known(frame_type) => frame_type.size(),
-195        }
-196    }
-197}
-198
-199impl Decodable for MaybeFrame {
-200    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-201        match FrameType::try_from(buf.get_var()?) {
-202            Ok(FrameType::Padding) => Ok(Self::None),
-203            Ok(other_frame) => Ok(Self::Known(other_frame)),
-204            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
-205        }
-206    }
-207}
-208
-209impl Encodable for MaybeFrame {
-210    fn encode<B: BufMut>(&self, buf: &mut B) {
-211        match self {
-212            Self::None => buf.write_var(0u64),
-213            Self::Unknown(frame_id) => buf.write_var(*frame_id),
-214            Self::Known(frame_type) => buf.write(*frame_type),
-215        }
-216    }
-217}
-218
-219#[allow(missing_docs)]
-220#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
-221#[display("STREAM")]
-222pub struct StreamInfo(u8);
-223
-224impl StreamInfo {
-225    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
-226    fn fin(self) -> bool {
-227        self.0 & 0x01 != 0
-228    }
-229    fn len(self) -> bool {
-230        self.0 & 0x02 != 0
-231    }
-232    fn off(self) -> bool {
-233        self.0 & 0x04 != 0
-234    }
-235
-236    const fn to_u64(self) -> u64 {
-237        self.0 as u64
-238    }
-239}
-240
-241#[allow(missing_docs)]
-242#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
-243#[display("DATAGRAM")]
-244pub struct DatagramInfo(u8);
-245
-246impl DatagramInfo {
-247    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+145                if StreamInfo::VALUES.contains(&value) {
+146                    return Ok(Self::Stream(StreamInfo(value as u8)));
+147                }
+148                Err(InvalidFrameId(value))
+149            }
+150        }
+151    }
+152}
+153
+154impl FrameType {
+155    /// The encoded size of this [`FrameType`].
+156    const fn size(&self) -> usize {
+157        VarInt(self.to_u64()).size()
+158    }
+159}
+160
+161impl Decodable for FrameType {
+162    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+163        Self::try_from(buf.get_var()?).map_err(|_| coding::UnexpectedEnd)
+164    }
+165}
+166
+167impl Encodable for FrameType {
+168    fn encode<B: BufMut>(&self, buf: &mut B) {
+169        buf.write_var(self.to_u64());
+170    }
+171}
+172
+173/// Wrapper type for the encodable frames.
+174///
+175/// This includes some "encoder" types instead of the actual read frame, when writing directly to
+176/// a buffer is more efficient than building the Frame itself.
+177#[derive(derive_more::From, enum_assoc::Assoc, derive_more::Display)]
+178#[func(fn encode_inner<B: BufMut>(&self, buf: &mut B) {_0.encode(buf)})]
+179#[func(pub(crate) const fn get_type(&self) -> FrameType {_0.get_type()})]
+180#[cfg_attr(feature = "qlog", func(pub(crate) fn to_qlog(&self) -> qlog::events::quic::QuicFrame {_0.to_qlog()}))]
+181pub(super) enum EncodableFrame<'a> {
+182    PathAck(PathAckEncoder<'a>),
+183    Ack(AckEncoder<'a>),
+184    Close(CloseEncoder<'a>),
+185    PathResponse(PathResponse),
+186    HandshakeDone(HandshakeDone),
+187    ReachOut(ReachOut),
+188    ObservedAddr(ObservedAddr),
+189    Ping(Ping),
+190    ImmediateAck(ImmediateAck),
+191    AckFrequency(AckFrequency),
+192    PathChallenge(PathChallenge),
+193    Crypto(Crypto),
+194    PathAbandon(PathAbandon),
+195    PathStatusAvailable(PathStatusAvailable),
+196    PathStatusBackup(PathStatusBackup),
+197    MaxPathId(MaxPathId),
+198    PathsBlocked(PathsBlocked),
+199    PathCidsBlocked(PathCidsBlocked),
+200    ResetStream(ResetStream),
+201    StopSending(StopSending),
+202    NewConnectionId(NewConnectionId),
+203    RetireConnectionId(RetireConnectionId),
+204    Datagram(Datagram),
+205    NewToken(NewToken),
+206    AddAddress(AddAddress),
+207    RemoveAddress(RemoveAddress),
+208    StreamMeta(StreamMetaEncoder),
+209    MaxData(MaxData),
+210    MaxStreamData(MaxStreamData),
+211    MaxStreams(MaxStreams),
+212}
+213
+214impl<'a> Encodable for EncodableFrame<'a> {
+215    fn encode<B: BufMut>(&self, buf: &mut B) {
+216        self.encode_inner(buf)
+217    }
+218}
+219
+220pub(crate) trait FrameStruct {
+221    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+222    const SIZE_BOUND: usize;
+223}
+224
+225/// The type used to refer to [`FrameType`]s in closing and transport errors.
+226#[derive(Copy, Clone, Eq, PartialEq, derive_more::Debug, derive_more::Display)]
+227pub enum MaybeFrame {
+228    /// Not attributed to any particular [`FrameType`].
+229    None,
+230    /// Attributed to some frame type this implementation does not recognize.
+231    #[display("UNKNOWN{:02x}", _0)]
+232    #[debug("Unknown{:02x}", _0)]
+233    Unknown(u64),
+234    /// Attributed to a specific [`FrameType`], never [`FrameType::Padding`].
+235    Known(FrameType),
+236}
+237
+238impl MaybeFrame {
+239    /// Encoded size of this [`MaybeFrame`].
+240    const fn size(&self) -> usize {
+241        match self {
+242            Self::None => VarInt(0).size(),
+243            Self::Unknown(other) => VarInt(*other).size(),
+244            Self::Known(frame_type) => frame_type.size(),
+245        }
+246    }
+247}
 248
-249    fn len(self) -> bool {
-250        self.0 & 0x01 != 0
-251    }
-252
-253    const fn to_u64(self) -> u64 {
-254        self.0 as u64
-255    }
-256}
-257
-258#[derive(Debug)]
-259pub(crate) enum Frame {
-260    Padding,
-261    Ping,
-262    Ack(Ack),
-263    PathAck(PathAck),
-264    ResetStream(ResetStream),
-265    StopSending(StopSending),
-266    Crypto(Crypto),
-267    NewToken(NewToken),
-268    Stream(Stream),
-269    MaxData(VarInt),
-270    MaxStreamData { id: StreamId, offset: u64 },
-271    MaxStreams { dir: Dir, count: u64 },
-272    DataBlocked { offset: u64 },
-273    StreamDataBlocked { id: StreamId, offset: u64 },
-274    StreamsBlocked { dir: Dir, limit: u64 },
-275    NewConnectionId(NewConnectionId),
-276    RetireConnectionId(RetireConnectionId),
-277    PathChallenge(PathChallenge),
-278    PathResponse(PathResponse),
-279    Close(Close),
-280    Datagram(Datagram),
-281    AckFrequency(AckFrequency),
-282    ImmediateAck,
-283    HandshakeDone,
-284    ObservedAddr(ObservedAddr),
-285    PathAbandon(PathAbandon),
-286    PathStatusAvailable(PathStatusAvailable),
-287    PathStatusBackup(PathStatusBackup),
-288    MaxPathId(MaxPathId),
-289    PathsBlocked(PathsBlocked),
-290    PathCidsBlocked(PathCidsBlocked),
-291    AddAddress(AddAddress),
-292    ReachOut(ReachOut),
-293    RemoveAddress(RemoveAddress),
-294}
-295
-296impl fmt::Display for Frame {
-297    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-298        // Eventually all our frames will support fmt::Display and be able to be used to log
-299        // consistently. For now we fall back to fmt::Debug.
-300        match self {
-301            Self::Padding => write!(f, "PADDING"),
-302            Self::Ping => write!(f, "PING"),
-303            Self::PathChallenge(frame) => write!(f, "{frame}"),
-304            Self::PathResponse(frame) => write!(f, "{frame}"),
-305            Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"),
-306            Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"),
-307            _ => write!(f, "{self:?}"),
-308        }
-309    }
-310}
-311
-312impl Frame {
-313    pub(crate) fn ty(&self) -> FrameType {
-314        use Frame::*;
-315        match *self {
-316            Padding => FrameType::Padding,
-317            ResetStream(_) => FrameType::ResetStream,
-318            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
-319            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
-320            MaxData(_) => FrameType::MaxData,
-321            MaxStreamData { .. } => FrameType::MaxStreamData,
-322            MaxStreams { dir: Dir::Bi, .. } => FrameType::MaxStreamsBidi,
-323            MaxStreams { dir: Dir::Uni, .. } => FrameType::MaxStreamsUni,
-324            Ping => FrameType::Ping,
-325            DataBlocked { .. } => FrameType::DataBlocked,
-326            StreamDataBlocked { .. } => FrameType::StreamDataBlocked,
-327            StreamsBlocked { dir: Dir::Bi, .. } => FrameType::StreamsBlockedBidi,
-328            StreamsBlocked { dir: Dir::Uni, .. } => FrameType::StreamsBlockedUni,
-329            StopSending { .. } => FrameType::StopSending,
-330            RetireConnectionId { .. } => FrameType::RetireConnectionId,
-331            Ack(_) => FrameType::Ack,
-332            PathAck(_) => FrameType::PathAck,
-333            Stream(ref x) => {
-334                let mut ty = *StreamInfo::VALUES.start() as u8;
-335                if x.fin {
-336                    ty |= 0x01;
-337                }
-338                if x.offset != 0 {
-339                    ty |= 0x04;
-340                }
-341                // TODO(@divma): move all this to getframetype for Stream
-342                FrameType::Stream(StreamInfo(ty))
-343            }
-344            PathChallenge(_) => FrameType::PathChallenge,
-345            PathResponse(_) => FrameType::PathResponse,
-346            NewConnectionId(cid) => cid.get_type(),
-347            Crypto(_) => FrameType::Crypto,
-348            NewToken(_) => FrameType::NewToken,
-349            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
-350            AckFrequency(_) => FrameType::AckFrequency,
-351            ImmediateAck => FrameType::ImmediateAck,
-352            HandshakeDone => FrameType::HandshakeDone,
-353            ObservedAddr(ref observed) => observed.get_type(),
-354            PathAbandon(_) => FrameType::PathAbandon,
-355            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
-356            PathStatusBackup(_) => FrameType::PathStatusBackup,
-357            MaxPathId(_) => FrameType::MaxPathId,
-358            PathsBlocked(_) => FrameType::PathsBlocked,
-359            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
-360            AddAddress(ref frame) => frame.get_type(),
-361            ReachOut(ref frame) => frame.get_type(),
-362            RemoveAddress(_) => self::RemoveAddress::TYPE,
-363        }
-364    }
-365
-366    pub(crate) fn is_ack_eliciting(&self) -> bool {
-367        !matches!(
-368            *self,
-369            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
-370        )
-371    }
-372
-373    /// Returns `true` if this frame MUST be sent in 1-RTT space
-374    pub(crate) fn is_1rtt(&self) -> bool {
-375        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
-376        // > All frames defined in this document MUST only be sent in 1-RTT packets.
-377        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
-378        // > connection with an error of type PROTOCOL_VIOLATION.
-379
-380        self.is_multipath_frame() || self.is_qad_frame()
-381    }
-382
-383    fn is_qad_frame(&self) -> bool {
-384        matches!(*self, Self::ObservedAddr(_))
-385    }
-386
-387    fn is_multipath_frame(&self) -> bool {
-388        matches!(
-389            *self,
-390            Self::PathAck(_)
-391                | Self::PathAbandon(_)
-392                | Self::PathStatusBackup(_)
-393                | Self::PathStatusAvailable(_)
-394                | Self::MaxPathId(_)
-395                | Self::PathsBlocked(_)
-396                | Self::PathCidsBlocked(_)
-397                | Self::NewConnectionId(NewConnectionId {
-398                    path_id: Some(_),
-399                    ..
-400                })
-401                | Self::RetireConnectionId(RetireConnectionId {
-402                    path_id: Some(_),
-403                    ..
-404                })
-405        )
-406    }
-407}
-408
-409#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
-410#[display("PATH_CHALLENGE({_0:08x})")]
-411pub(crate) struct PathChallenge(pub(crate) u64);
-412
-413impl PathChallenge {
-414    pub(crate) const SIZE_BOUND: usize = 9;
-415}
-416impl Decodable for PathChallenge {
-417    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-418        Ok(Self(buf.get()?))
-419    }
-420}
-421impl Encodable for PathChallenge {
-422    fn encode<B: BufMut>(&self, buf: &mut B) {
-423        buf.write(FrameType::PathChallenge);
-424        buf.write(self.0);
-425    }
-426}
-427
-428#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
-429#[display("PATH_RESPONSE({_0:08x})")]
-430pub(crate) struct PathResponse(pub(crate) u64);
-431
-432impl PathResponse {
-433    pub(crate) const SIZE_BOUND: usize = 9;
-434}
-435
-436impl Decodable for PathResponse {
-437    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-438        Ok(Self(buf.get()?))
-439    }
-440}
-441impl Encodable for PathResponse {
-442    fn encode<B: BufMut>(&self, buf: &mut B) {
-443        buf.write(FrameType::PathResponse);
-444        buf.write(self.0);
-445    }
-446}
-447
-448#[derive(Debug, PartialEq, Eq)]
-449pub(crate) struct RetireConnectionId {
-450    pub(crate) path_id: Option<PathId>,
-451    pub(crate) sequence: u64,
-452}
-453
-454impl RetireConnectionId {
-455    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
-456    pub(crate) const SIZE_BOUND: usize = {
-457        let type_len = FrameType::RetireConnectionId.size();
-458        let seq_max_len = 8usize;
-459        type_len + seq_max_len
-460    };
-461
-462    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
-463    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
-464        let type_len = FrameType::PathRetireConnectionId.size();
-465        let path_id_len = VarInt::from_u32(u32::MAX).size();
-466        let seq_max_len = 8usize;
-467        type_len + path_id_len + seq_max_len
-468    };
+249impl Decodable for MaybeFrame {
+250    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+251        match FrameType::try_from(buf.get_var()?) {
+252            Ok(FrameType::Padding) => Ok(Self::None),
+253            Ok(other_frame) => Ok(Self::Known(other_frame)),
+254            Err(InvalidFrameId(other)) => Ok(Self::Unknown(other)),
+255        }
+256    }
+257}
+258
+259impl Encodable for MaybeFrame {
+260    fn encode<B: BufMut>(&self, buf: &mut B) {
+261        match self {
+262            Self::None => buf.write_var(0u64),
+263            Self::Unknown(frame_id) => buf.write_var(*frame_id),
+264            Self::Known(frame_type) => buf.write(*frame_type),
+265        }
+266    }
+267}
+268
+269#[derive(derive_more::Display)]
+270#[display("HANDSHAKE_DONE")]
+271pub(crate) struct HandshakeDone;
+272
+273impl HandshakeDone {
+274    const fn get_type(&self) -> FrameType {
+275        FrameType::HandshakeDone
+276    }
+277}
+278
+279impl Encodable for HandshakeDone {
+280    fn encode<B: BufMut>(&self, buf: &mut B) {
+281        FrameType::HandshakeDone.encode(buf);
+282    }
+283}
+284
+285#[derive(derive_more::Display)]
+286#[display("PING")]
+287pub(crate) struct Ping;
+288
+289impl Ping {
+290    const fn get_type(&self) -> FrameType {
+291        FrameType::Ping
+292    }
+293}
+294
+295impl Encodable for Ping {
+296    fn encode<B: BufMut>(&self, buf: &mut B) {
+297        FrameType::Ping.encode(buf);
+298    }
+299}
+300
+301#[derive(derive_more::Display)]
+302#[display("IMMEDIATE_ACK")]
+303pub(crate) struct ImmediateAck;
+304
+305impl ImmediateAck {
+306    const fn get_type(&self) -> FrameType {
+307        FrameType::ImmediateAck
+308    }
+309}
+310
+311impl Encodable for ImmediateAck {
+312    fn encode<B: BufMut>(&self, buf: &mut B) {
+313        FrameType::ImmediateAck.encode(buf);
+314    }
+315}
+316
+317#[allow(missing_docs)]
+318#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+319#[display("STREAM")]
+320pub struct StreamInfo(u8);
+321
+322impl StreamInfo {
+323    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x08, 0x0f);
+324    fn fin(self) -> bool {
+325        self.0 & 0x01 != 0
+326    }
+327    fn len(self) -> bool {
+328        self.0 & 0x02 != 0
+329    }
+330    fn off(self) -> bool {
+331        self.0 & 0x04 != 0
+332    }
+333
+334    const fn to_u64(self) -> u64 {
+335        self.0 as u64
+336    }
+337}
+338
+339#[allow(missing_docs)]
+340#[derive(Debug, Copy, Clone, Eq, PartialEq, derive_more::Display)]
+341#[display("DATAGRAM")]
+342pub struct DatagramInfo(u8);
+343
+344impl DatagramInfo {
+345    const VALUES: RangeInclusive<u64> = RangeInclusive::new(0x30, 0x31);
+346
+347    fn len(self) -> bool {
+348        self.0 & 0x01 != 0
+349    }
+350
+351    const fn to_u64(self) -> u64 {
+352        self.0 as u64
+353    }
+354}
+355
+356#[derive(Debug)]
+357pub(crate) enum Frame {
+358    Padding,
+359    Ping,
+360    Ack(Ack),
+361    PathAck(PathAck),
+362    ResetStream(ResetStream),
+363    StopSending(StopSending),
+364    Crypto(Crypto),
+365    NewToken(NewToken),
+366    Stream(Stream),
+367    MaxData(MaxData),
+368    MaxStreamData(MaxStreamData),
+369    MaxStreams(MaxStreams),
+370    DataBlocked { offset: u64 },
+371    StreamDataBlocked { id: StreamId, offset: u64 },
+372    StreamsBlocked { dir: Dir, limit: u64 },
+373    NewConnectionId(NewConnectionId),
+374    RetireConnectionId(RetireConnectionId),
+375    PathChallenge(PathChallenge),
+376    PathResponse(PathResponse),
+377    Close(Close),
+378    Datagram(Datagram),
+379    AckFrequency(AckFrequency),
+380    ImmediateAck,
+381    HandshakeDone,
+382    ObservedAddr(ObservedAddr),
+383    PathAbandon(PathAbandon),
+384    PathStatusAvailable(PathStatusAvailable),
+385    PathStatusBackup(PathStatusBackup),
+386    MaxPathId(MaxPathId),
+387    PathsBlocked(PathsBlocked),
+388    PathCidsBlocked(PathCidsBlocked),
+389    AddAddress(AddAddress),
+390    ReachOut(ReachOut),
+391    RemoveAddress(RemoveAddress),
+392}
+393
+394impl fmt::Display for Frame {
+395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+396        // Eventually all our frames will support fmt::Display and be able to be used to log
+397        // consistently. For now we fall back to fmt::Debug.
+398        match self {
+399            Self::Padding => write!(f, "PADDING"),
+400            Self::Ping => write!(f, "PING"),
+401            Self::PathChallenge(frame) => write!(f, "{frame}"),
+402            Self::PathResponse(frame) => write!(f, "{frame}"),
+403            Self::ImmediateAck => write!(f, "IMMEDIATE_ACK"),
+404            Self::HandshakeDone => write!(f, "HANDSHAKE_DONE"),
+405            _ => write!(f, "{self:?}"),
+406        }
+407    }
+408}
+409
+410impl Frame {
+411    pub(crate) fn ty(&self) -> FrameType {
+412        use Frame::*;
+413        match &self {
+414            Padding => FrameType::Padding,
+415            ResetStream(_) => FrameType::ResetStream,
+416            Close(self::Close::Connection(_)) => FrameType::ConnectionClose,
+417            Close(self::Close::Application(_)) => FrameType::ConnectionClose,
+418            MaxData(_) => FrameType::MaxData,
+419            MaxStreamData(_) => FrameType::MaxStreamData,
+420            MaxStreams(max_streams) => max_streams.get_type(),
+421            Ping => FrameType::Ping,
+422            DataBlocked { .. } => FrameType::DataBlocked,
+423            StreamDataBlocked { .. } => FrameType::StreamDataBlocked,
+424            StreamsBlocked { dir: Dir::Bi, .. } => FrameType::StreamsBlockedBidi,
+425            StreamsBlocked { dir: Dir::Uni, .. } => FrameType::StreamsBlockedUni,
+426            StopSending { .. } => FrameType::StopSending,
+427            RetireConnectionId(retire_frame) => retire_frame.get_type(),
+428            Ack(ack) => ack.get_type(),
+429            PathAck(path_ack) => path_ack.get_type(),
+430            Stream(ref x) => {
+431                let mut ty = *StreamInfo::VALUES.start() as u8;
+432                if x.fin {
+433                    ty |= 0x01;
+434                }
+435                if x.offset != 0 {
+436                    ty |= 0x04;
+437                }
+438                // TODO(@divma): move all this to getframetype for Stream
+439                FrameType::Stream(StreamInfo(ty))
+440            }
+441            PathChallenge(_) => FrameType::PathChallenge,
+442            PathResponse(_) => FrameType::PathResponse,
+443            NewConnectionId(cid) => cid.get_type(),
+444            Crypto(_) => FrameType::Crypto,
+445            NewToken(_) => FrameType::NewToken,
+446            Datagram(_) => FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8)),
+447            AckFrequency(_) => FrameType::AckFrequency,
+448            ImmediateAck => FrameType::ImmediateAck,
+449            HandshakeDone => FrameType::HandshakeDone,
+450            ObservedAddr(ref observed) => observed.get_type(),
+451            PathAbandon(_) => FrameType::PathAbandon,
+452            PathStatusAvailable(_) => FrameType::PathStatusAvailable,
+453            PathStatusBackup(_) => FrameType::PathStatusBackup,
+454            MaxPathId(_) => FrameType::MaxPathId,
+455            PathsBlocked(_) => FrameType::PathsBlocked,
+456            PathCidsBlocked(_) => FrameType::PathCidsBlocked,
+457            AddAddress(ref frame) => frame.get_type(),
+458            ReachOut(ref frame) => frame.get_type(),
+459            RemoveAddress(_) => self::RemoveAddress::TYPE,
+460        }
+461    }
+462
+463    pub(crate) fn is_ack_eliciting(&self) -> bool {
+464        !matches!(
+465            *self,
+466            Self::Ack(_) | Self::PathAck(_) | Self::Padding | Self::Close(_)
+467        )
+468    }
 469
-470    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
-471    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
-472    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
-473        Ok(Self {
-474            path_id: if read_path { Some(bytes.get()?) } else { None },
-475            sequence: bytes.get_var()?,
-476        })
-477    }
-478
-479    /// Get the [`FrameType`] for this [`RetireConnectionId`]
-480    pub(crate) fn get_type(&self) -> FrameType {
-481        if self.path_id.is_some() {
-482            FrameType::PathRetireConnectionId
-483        } else {
-484            FrameType::RetireConnectionId
-485        }
-486    }
-487
-488    /// Returns the maximum encoded size on the wire
-489    ///
-490    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
-491    /// estimate, does not squeeze every last byte out.
-492    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
-493        match path_retire_cid {
-494            true => Self::SIZE_BOUND_MULTIPATH,
-495            false => Self::SIZE_BOUND,
-496        }
-497    }
-498}
-499
-500impl Encodable for RetireConnectionId {
-501    fn encode<W: BufMut>(&self, buf: &mut W) {
-502        buf.write(self.get_type());
-503        if let Some(id) = self.path_id {
-504            buf.write(id);
-505        }
-506        buf.write_var(self.sequence);
-507    }
-508}
+470    /// Returns `true` if this frame MUST be sent in 1-RTT space
+471    pub(crate) fn is_1rtt(&self) -> bool {
+472        // See also https://www.ietf.org/archive/id/draft-ietf-quic-multipath-17.html#section-4-1:
+473        // > All frames defined in this document MUST only be sent in 1-RTT packets.
+474        // > If an endpoint receives a multipath-specific frame in a different packet type, it MUST close the
+475        // > connection with an error of type PROTOCOL_VIOLATION.
+476
+477        self.is_multipath_frame() || self.is_qad_frame()
+478    }
+479
+480    fn is_qad_frame(&self) -> bool {
+481        matches!(*self, Self::ObservedAddr(_))
+482    }
+483
+484    fn is_multipath_frame(&self) -> bool {
+485        matches!(
+486            *self,
+487            Self::PathAck(_)
+488                | Self::PathAbandon(_)
+489                | Self::PathStatusBackup(_)
+490                | Self::PathStatusAvailable(_)
+491                | Self::MaxPathId(_)
+492                | Self::PathsBlocked(_)
+493                | Self::PathCidsBlocked(_)
+494                | Self::NewConnectionId(NewConnectionId {
+495                    path_id: Some(_),
+496                    ..
+497                })
+498                | Self::RetireConnectionId(RetireConnectionId {
+499                    path_id: Some(_),
+500                    ..
+501                })
+502        )
+503    }
+504}
+505
+506#[derive(Debug, Clone, Copy, PartialEq, Eq, derive_more::Display)]
+507#[display("PATH_CHALLENGE({_0:08x})")]
+508pub(crate) struct PathChallenge(pub(crate) u64);
 509
-510#[derive(Clone, Debug)]
-511pub(crate) enum Close {
-512    Connection(ConnectionClose),
-513    Application(ApplicationClose),
-514}
-515
-516impl Close {
-517    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
-518        CloseEncoder {
-519            close: self,
-520            max_len,
-521        }
-522    }
+510impl PathChallenge {
+511    pub(crate) const SIZE_BOUND: usize = 9;
+512
+513    const fn get_type(&self) -> FrameType {
+514        FrameType::PathChallenge
+515    }
+516}
+517
+518impl Decodable for PathChallenge {
+519    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+520        Ok(Self(buf.get()?))
+521    }
+522}
 523
-524    pub(crate) fn is_transport_layer(&self) -> bool {
-525        matches!(*self, Self::Connection(_))
-526    }
-527}
-528
-529pub(crate) struct CloseEncoder<'a> {
-530    close: &'a Close,
-531    max_len: usize,
-532}
-533
-534impl<'a> Encodable for CloseEncoder<'a> {
-535    fn encode<W: BufMut>(&self, out: &mut W) {
-536        match self.close {
-537            Close::Connection(x) => x.encode(out, self.max_len),
-538            Close::Application(x) => x.encode(out, self.max_len),
-539        }
+524impl Encodable for PathChallenge {
+525    fn encode<B: BufMut>(&self, buf: &mut B) {
+526        buf.write(FrameType::PathChallenge);
+527        buf.write(self.0);
+528    }
+529}
+530
+531#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Display)]
+532#[display("PATH_RESPONSE({_0:08x})")]
+533pub(crate) struct PathResponse(pub(crate) u64);
+534
+535impl PathResponse {
+536    pub(crate) const SIZE_BOUND: usize = 9;
+537
+538    const fn get_type(&self) -> FrameType {
+539        FrameType::PathResponse
 540    }
 541}
 542
-543impl From<TransportError> for Close {
-544    fn from(x: TransportError) -> Self {
-545        Self::Connection(x.into())
+543impl Decodable for PathResponse {
+544    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+545        Ok(Self(buf.get()?))
 546    }
 547}
-548impl From<ConnectionClose> for Close {
-549    fn from(x: ConnectionClose) -> Self {
-550        Self::Connection(x)
-551    }
-552}
-553impl From<ApplicationClose> for Close {
-554    fn from(x: ApplicationClose) -> Self {
-555        Self::Application(x)
-556    }
-557}
+548impl Encodable for PathResponse {
+549    fn encode<B: BufMut>(&self, buf: &mut B) {
+550        buf.write(FrameType::PathResponse);
+551        buf.write(self.0);
+552    }
+553}
+554
+555#[derive(Debug, Clone, Copy, derive_more::Display)]
+556#[display("MAX_DATA({_0})")]
+557pub(crate) struct MaxData(pub(crate) VarInt);
 558
-559/// Reason given by the transport for closing the connection
-560#[derive(Debug, Clone, PartialEq, Eq)]
-561pub struct ConnectionClose {
-562    /// Class of error as encoded in the specification
-563    pub error_code: TransportErrorCode,
-564    /// Type of frame that caused the close
-565    pub frame_type: MaybeFrame,
-566    /// Human-readable reason for the close
-567    pub reason: Bytes,
-568}
-569
-570impl fmt::Display for ConnectionClose {
-571    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-572        self.error_code.fmt(f)?;
-573        if !self.reason.as_ref().is_empty() {
-574            f.write_str(": ")?;
-575            f.write_str(&String::from_utf8_lossy(&self.reason))?;
-576        }
-577        Ok(())
-578    }
-579}
-580
-581impl From<TransportError> for ConnectionClose {
-582    fn from(x: TransportError) -> Self {
-583        Self {
-584            error_code: x.code,
-585            frame_type: x.frame,
-586            reason: x.reason.into(),
-587        }
-588    }
-589}
-590
-591impl FrameStruct for ConnectionClose {
-592    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
-593}
-594
-595impl ConnectionClose {
-596    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
-597        out.write(FrameType::ConnectionClose); // 1 byte
-598        out.write(self.error_code); // <= 8 bytes
-599        out.write(self.frame_type); // <= 8 bytes
-600        let max_len = max_len
-601            - 3
-602            - self.frame_type.size()
-603            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
-604        let actual_len = self.reason.len().min(max_len);
-605        out.write_var(actual_len as u64); // <= 8 bytes
-606        out.put_slice(&self.reason[0..actual_len]); // whatever's left
-607    }
-608}
-609
-610/// Reason given by an application for closing the connection
-611#[derive(Debug, Clone, PartialEq, Eq)]
-612pub struct ApplicationClose {
-613    /// Application-specific reason code
-614    pub error_code: VarInt,
-615    /// Human-readable reason for the close
-616    pub reason: Bytes,
-617}
-618
-619impl fmt::Display for ApplicationClose {
-620    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-621        if !self.reason.as_ref().is_empty() {
-622            f.write_str(&String::from_utf8_lossy(&self.reason))?;
-623            f.write_str(" (code ")?;
-624            self.error_code.fmt(f)?;
-625            f.write_str(")")?;
-626        } else {
-627            self.error_code.fmt(f)?;
-628        }
-629        Ok(())
-630    }
-631}
-632
-633impl FrameStruct for ApplicationClose {
-634    const SIZE_BOUND: usize = 1 + 8 + 8;
+559impl MaxData {
+560    const fn get_type(&self) -> FrameType {
+561        FrameType::MaxData
+562    }
+563}
+564
+565impl Decodable for MaxData {
+566    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+567        Ok(Self(buf.get()?))
+568    }
+569}
+570impl Encodable for MaxData {
+571    fn encode<B: BufMut>(&self, buf: &mut B) {
+572        buf.write(FrameType::MaxData);
+573        buf.write(self.0);
+574    }
+575}
+576
+577#[derive(Debug, Clone, Copy, derive_more::Display)]
+578#[display("MAX_STREAM_DATA id: {id} max: {offset}")]
+579pub(crate) struct MaxStreamData {
+580    pub(crate) id: StreamId,
+581    pub(crate) offset: u64,
+582}
+583
+584impl MaxStreamData {
+585    const fn get_type(&self) -> FrameType {
+586        FrameType::MaxStreamData
+587    }
+588}
+589
+590impl Decodable for MaxStreamData {
+591    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+592        Ok(Self {
+593            id: buf.get()?,
+594            offset: buf.get_var()?,
+595        })
+596    }
+597}
+598
+599impl Encodable for MaxStreamData {
+600    fn encode<B: BufMut>(&self, buf: &mut B) {
+601        buf.write(FrameType::MaxStreamData);
+602        buf.write(self.id);
+603        buf.write_var(self.offset);
+604    }
+605}
+606
+607#[derive(Debug, Clone, Copy, derive_more::Display)]
+608#[display("{} count: {count}", self.get_type())]
+609pub(crate) struct MaxStreams {
+610    pub(crate) dir: Dir,
+611    pub(crate) count: u64,
+612}
+613
+614impl MaxStreams {
+615    const fn get_type(&self) -> FrameType {
+616        match self.dir {
+617            Dir::Bi => FrameType::MaxStreamsBidi,
+618            Dir::Uni => FrameType::MaxStreamsUni,
+619        }
+620    }
+621}
+622
+623impl Encodable for MaxStreams {
+624    fn encode<B: BufMut>(&self, buf: &mut B) {
+625        buf.write(self.get_type());
+626        buf.write_var(self.count);
+627    }
+628}
+629
+630#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+631#[display("{} {} seq: {sequence}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+632pub(crate) struct RetireConnectionId {
+633    pub(crate) path_id: Option<PathId>,
+634    pub(crate) sequence: u64,
 635}
 636
-637impl ApplicationClose {
-638    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
-639        out.write(FrameType::ApplicationClose); // 1 byte
-640        out.write(self.error_code); // <= 8 bytes
-641        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
-642        let actual_len = self.reason.len().min(max_len);
-643        out.write_var(actual_len as u64); // <= 8 bytes
-644        out.put_slice(&self.reason[0..actual_len]); // whatever's left
-645    }
-646}
-647
-648#[derive(Clone, Eq, PartialEq)]
-649pub(crate) struct PathAck {
-650    pub path_id: PathId,
-651    pub largest: u64,
-652    pub delay: u64,
-653    pub additional: Bytes,
-654    pub ecn: Option<EcnCounts>,
-655}
-656
-657impl fmt::Debug for PathAck {
-658    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-659        let mut ranges = "[".to_string();
-660        let mut first = true;
-661        for range in self.into_iter() {
-662            if !first {
-663                ranges.push(',');
-664            }
-665            write!(ranges, "{range:?}")?;
-666            first = false;
-667        }
-668        ranges.push(']');
-669
-670        f.debug_struct("PathAck")
-671            .field("path_id", &self.path_id)
-672            .field("largest", &self.largest)
-673            .field("delay", &self.delay)
-674            .field("ecn", &self.ecn)
-675            .field("ranges", &ranges)
-676            .finish()
-677    }
-678}
-679
-680impl<'a> IntoIterator for &'a PathAck {
-681    type Item = RangeInclusive<u64>;
-682    #[allow(unnameable_types)]
-683    type IntoIter = AckIter<'a>;
-684
-685    fn into_iter(self) -> Self::IntoIter {
-686        AckIter::new(self.largest, &self.additional[..])
-687    }
-688}
-689
-690impl PathAck {
-691    pub(crate) fn into_ack(self) -> (Ack, PathId) {
-692        let ack = Ack {
-693            largest: self.largest,
-694            delay: self.delay,
-695            additional: self.additional,
-696            ecn: self.ecn,
-697        };
+637impl RetireConnectionId {
+638    /// Maximum size of this frame when the frame type is [`FrameType::RetireConnectionId`]
+639    pub(crate) const SIZE_BOUND: usize = {
+640        let type_len = FrameType::RetireConnectionId.size();
+641        let seq_max_len = 8usize;
+642        type_len + seq_max_len
+643    };
+644
+645    /// Maximum size of this frame when the frame type is [`FrameType::PathRetireConnectionId`]
+646    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+647        let type_len = FrameType::PathRetireConnectionId.size();
+648        let path_id_len = VarInt::from_u32(u32::MAX).size();
+649        let seq_max_len = 8usize;
+650        type_len + path_id_len + seq_max_len
+651    };
+652
+653    /// Decode [`Self`] from the buffer, provided that the frame type has been verified (either
+654    /// [`FrameType::PathRetireConnectionId`], or [`FrameType::RetireConnectionId`])
+655    pub(crate) fn decode<R: Buf>(bytes: &mut R, read_path: bool) -> coding::Result<Self> {
+656        Ok(Self {
+657            path_id: if read_path { Some(bytes.get()?) } else { None },
+658            sequence: bytes.get_var()?,
+659        })
+660    }
+661
+662    /// Get the [`FrameType`] for this [`RetireConnectionId`]
+663    const fn get_type(&self) -> FrameType {
+664        if self.path_id.is_some() {
+665            FrameType::PathRetireConnectionId
+666        } else {
+667            FrameType::RetireConnectionId
+668        }
+669    }
+670
+671    /// Returns the maximum encoded size on the wire
+672    ///
+673    /// `path_retire_cid` determines whether this frame is a multipath frame. This is a rough upper
+674    /// estimate, does not squeeze every last byte out.
+675    pub(crate) const fn size_bound(path_retire_cid: bool) -> usize {
+676        match path_retire_cid {
+677            true => Self::SIZE_BOUND_MULTIPATH,
+678            false => Self::SIZE_BOUND,
+679        }
+680    }
+681}
+682
+683impl Encodable for RetireConnectionId {
+684    fn encode<W: BufMut>(&self, buf: &mut W) {
+685        buf.write(self.get_type());
+686        if let Some(id) = self.path_id {
+687            buf.write(id);
+688        }
+689        buf.write_var(self.sequence);
+690    }
+691}
+692
+693#[derive(Clone, Debug, derive_more::Display)]
+694pub(crate) enum Close {
+695    Connection(ConnectionClose),
+696    Application(ApplicationClose),
+697}
 698
-699        (ack, self.path_id)
-700    }
-701
-702    pub(crate) fn encoder<'a>(
-703        path_id: PathId,
-704        delay: u64,
-705        ranges: &'a ArrayRangeSet,
-706        ecn: Option<&'a EcnCounts>,
-707    ) -> PathAckEncoder<'a> {
-708        PathAckEncoder {
-709            path_id,
-710            delay,
-711            ranges,
-712            ecn,
-713        }
-714    }
-715}
-716
-717pub(crate) struct PathAckEncoder<'a> {
-718    path_id: PathId,
-719    delay: u64,
-720    ranges: &'a ArrayRangeSet,
-721    ecn: Option<&'a EcnCounts>,
-722}
-723
-724impl<'a> Encodable for PathAckEncoder<'a> {
-725    /// Encode [`Self`] into the given buffer
-726    ///
-727    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
-728    /// depending on whether [`EcnCounts`] are provided.
-729    ///
-730    /// PANICS: if `ranges` is empty.
-731    fn encode<W: BufMut>(&self, buf: &mut W) {
-732        let PathAckEncoder {
-733            path_id,
-734            delay,
-735            ranges,
-736            ecn,
-737        } = self;
-738        let mut rest = ranges.iter().rev();
-739        let first = rest
-740            .next()
-741            .expect("Caller has verified ranges is non empty");
-742        let largest = first.end - 1;
-743        let first_size = first.end - first.start;
-744        let kind = match ecn.is_some() {
-745            true => FrameType::PathAckEcn,
-746            false => FrameType::PathAck,
-747        };
-748        buf.write(kind);
-749        buf.write(*path_id);
-750        buf.write_var(largest);
-751        buf.write_var(*delay);
-752        buf.write_var(ranges.len() as u64 - 1);
-753        buf.write_var(first_size - 1);
-754        let mut prev = first.start;
-755        for block in rest {
-756            let size = block.end - block.start;
-757            buf.write_var(prev - block.end - 1);
-758            buf.write_var(size - 1);
-759            prev = block.start;
-760        }
-761        if let Some(x) = ecn {
-762            x.encode(buf)
-763        }
-764    }
-765}
-766
-767#[derive(Clone, Eq, PartialEq)]
-768pub(crate) struct Ack {
-769    pub largest: u64,
-770    pub delay: u64,
-771    pub additional: Bytes,
-772    pub ecn: Option<EcnCounts>,
+699impl Close {
+700    pub(crate) fn encoder(&self, max_len: usize) -> CloseEncoder<'_> {
+701        CloseEncoder {
+702            close: self,
+703            max_len,
+704        }
+705    }
+706
+707    pub(crate) fn is_transport_layer(&self) -> bool {
+708        matches!(*self, Self::Connection(_))
+709    }
+710}
+711
+712#[derive(derive_more::Display)]
+713#[display("{close}")]
+714pub(crate) struct CloseEncoder<'a> {
+715    pub(crate) close: &'a Close,
+716    max_len: usize,
+717}
+718
+719impl<'a> CloseEncoder<'a> {
+720    const fn get_type(&self) -> FrameType {
+721        match self.close {
+722            Close::Connection(_) => FrameType::ConnectionClose,
+723            Close::Application(_) => FrameType::ApplicationClose,
+724        }
+725    }
+726}
+727
+728impl<'a> Encodable for CloseEncoder<'a> {
+729    fn encode<W: BufMut>(&self, out: &mut W) {
+730        match self.close {
+731            Close::Connection(x) => x.encode(out, self.max_len),
+732            Close::Application(x) => x.encode(out, self.max_len),
+733        }
+734    }
+735}
+736
+737impl From<TransportError> for Close {
+738    fn from(x: TransportError) -> Self {
+739        Self::Connection(x.into())
+740    }
+741}
+742impl From<ConnectionClose> for Close {
+743    fn from(x: ConnectionClose) -> Self {
+744        Self::Connection(x)
+745    }
+746}
+747impl From<ApplicationClose> for Close {
+748    fn from(x: ApplicationClose) -> Self {
+749        Self::Application(x)
+750    }
+751}
+752
+753/// Reason given by the transport for closing the connection
+754#[derive(Debug, Clone, PartialEq, Eq)]
+755pub struct ConnectionClose {
+756    /// Class of error as encoded in the specification
+757    pub error_code: TransportErrorCode,
+758    /// Type of frame that caused the close
+759    pub frame_type: MaybeFrame,
+760    /// Human-readable reason for the close
+761    pub reason: Bytes,
+762}
+763
+764impl fmt::Display for ConnectionClose {
+765    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+766        self.error_code.fmt(f)?;
+767        if !self.reason.as_ref().is_empty() {
+768            f.write_str(": ")?;
+769            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+770        }
+771        Ok(())
+772    }
 773}
 774
-775impl fmt::Debug for Ack {
-776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-777        let mut ranges = "[".to_string();
-778        let mut first = true;
-779        for range in self.iter() {
-780            if !first {
-781                ranges.push(',');
-782            }
-783            write!(ranges, "{range:?}").unwrap();
-784            first = false;
-785        }
-786        ranges.push(']');
-787
-788        f.debug_struct("Ack")
-789            .field("largest", &self.largest)
-790            .field("delay", &self.delay)
-791            .field("ecn", &self.ecn)
-792            .field("ranges", &ranges)
-793            .finish()
-794    }
-795}
-796
-797#[allow(unnameable_types)]
-798impl<'a> IntoIterator for &'a Ack {
-799    type Item = RangeInclusive<u64>;
-800    type IntoIter = AckIter<'a>;
-801
-802    fn into_iter(self) -> AckIter<'a> {
-803        AckIter::new(self.largest, &self.additional[..])
-804    }
-805}
-806
-807impl Ack {
-808    pub(crate) fn encoder<'a>(
-809        delay: u64,
-810        ranges: &'a ArrayRangeSet,
-811        ecn: Option<&'a EcnCounts>,
-812    ) -> AckEncoder<'a> {
-813        AckEncoder { delay, ranges, ecn }
-814    }
-815
-816    pub(crate) fn iter(&self) -> AckIter<'_> {
-817        self.into_iter()
-818    }
-819}
-820
-821pub(crate) struct AckEncoder<'a> {
-822    delay: u64,
-823    ranges: &'a ArrayRangeSet,
-824    ecn: Option<&'a EcnCounts>,
+775impl From<TransportError> for ConnectionClose {
+776    fn from(x: TransportError) -> Self {
+777        Self {
+778            error_code: x.code,
+779            frame_type: x.frame,
+780            reason: x.reason.into(),
+781        }
+782    }
+783}
+784
+785impl FrameStruct for ConnectionClose {
+786    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+787}
+788
+789impl ConnectionClose {
+790    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+791        out.write(FrameType::ConnectionClose); // 1 byte
+792        out.write(self.error_code); // <= 8 bytes
+793        out.write(self.frame_type); // <= 8 bytes
+794        let max_len = max_len
+795            - 3
+796            - self.frame_type.size()
+797            - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+798        let actual_len = self.reason.len().min(max_len);
+799        out.write_var(actual_len as u64); // <= 8 bytes
+800        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+801    }
+802}
+803
+804/// Reason given by an application for closing the connection
+805#[derive(Debug, Clone, PartialEq, Eq)]
+806pub struct ApplicationClose {
+807    /// Application-specific reason code
+808    pub error_code: VarInt,
+809    /// Human-readable reason for the close
+810    pub reason: Bytes,
+811}
+812
+813impl fmt::Display for ApplicationClose {
+814    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+815        if !self.reason.as_ref().is_empty() {
+816            f.write_str(&String::from_utf8_lossy(&self.reason))?;
+817            f.write_str(" (code ")?;
+818            self.error_code.fmt(f)?;
+819            f.write_str(")")?;
+820        } else {
+821            self.error_code.fmt(f)?;
+822        }
+823        Ok(())
+824    }
 825}
 826
-827impl<'a> Encodable for AckEncoder<'a> {
-828    fn encode<W: BufMut>(&self, buf: &mut W) {
-829        let AckEncoder { delay, ranges, ecn } = self;
-830        let mut rest = ranges.iter().rev();
-831        let first = rest.next().unwrap();
-832        let largest = first.end - 1;
-833        let first_size = first.end - first.start;
-834        let kind = match ecn.is_some() {
-835            true => FrameType::AckEcn,
-836            false => FrameType::Ack,
-837        };
-838        buf.write(kind);
-839        buf.write_var(largest);
-840        buf.write_var(*delay);
-841        buf.write_var(ranges.len() as u64 - 1);
-842        buf.write_var(first_size - 1);
-843        let mut prev = first.start;
-844        for block in rest {
-845            let size = block.end - block.start;
-846            buf.write_var(prev - block.end - 1);
-847            buf.write_var(size - 1);
-848            prev = block.start;
-849        }
-850        if let Some(x) = ecn {
-851            x.encode(buf)
-852        }
-853    }
-854}
-855
-856#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-857pub(crate) struct EcnCounts {
-858    pub ect0: u64,
-859    pub ect1: u64,
-860    pub ce: u64,
-861}
-862
-863impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
-864    fn add_assign(&mut self, rhs: EcnCodepoint) {
-865        match rhs {
-866            EcnCodepoint::Ect0 => {
-867                self.ect0 += 1;
-868            }
-869            EcnCodepoint::Ect1 => {
-870                self.ect1 += 1;
-871            }
-872            EcnCodepoint::Ce => {
-873                self.ce += 1;
-874            }
-875        }
-876    }
-877}
+827impl FrameStruct for ApplicationClose {
+828    const SIZE_BOUND: usize = 1 + 8 + 8;
+829}
+830
+831impl ApplicationClose {
+832    pub(crate) fn encode<W: BufMut>(&self, out: &mut W, max_len: usize) {
+833        out.write(FrameType::ApplicationClose); // 1 byte
+834        out.write(self.error_code); // <= 8 bytes
+835        let max_len = max_len - 3 - VarInt::from_u64(self.reason.len() as u64).unwrap().size();
+836        let actual_len = self.reason.len().min(max_len);
+837        out.write_var(actual_len as u64); // <= 8 bytes
+838        out.put_slice(&self.reason[0..actual_len]); // whatever's left
+839    }
+840}
+841
+842#[derive(Clone, Eq, PartialEq)]
+843pub(crate) struct PathAck {
+844    pub path_id: PathId,
+845    pub largest: u64,
+846    pub delay: u64,
+847    pub additional: Bytes,
+848    pub ecn: Option<EcnCounts>,
+849}
+850
+851impl fmt::Debug for PathAck {
+852    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+853        let mut ranges = "[".to_string();
+854        let mut first = true;
+855        for range in self.into_iter() {
+856            if !first {
+857                ranges.push(',');
+858            }
+859            write!(ranges, "{range:?}")?;
+860            first = false;
+861        }
+862        ranges.push(']');
+863
+864        f.debug_struct("PathAck")
+865            .field("path_id", &self.path_id)
+866            .field("largest", &self.largest)
+867            .field("delay", &self.delay)
+868            .field("ecn", &self.ecn)
+869            .field("ranges", &ranges)
+870            .finish()
+871    }
+872}
+873
+874impl<'a> IntoIterator for &'a PathAck {
+875    type Item = RangeInclusive<u64>;
+876    #[allow(unnameable_types)]
+877    type IntoIter = AckIter<'a>;
 878
-879impl EcnCounts {
-880    pub(crate) const ZERO: Self = Self {
-881        ect0: 0,
-882        ect1: 0,
-883        ce: 0,
-884    };
-885}
-886
-887impl Encodable for EcnCounts {
-888    fn encode<W: BufMut>(&self, out: &mut W) {
-889        out.write_var(self.ect0);
-890        out.write_var(self.ect1);
-891        out.write_var(self.ce);
-892    }
-893}
-894
-895#[derive(Debug, Clone)]
-896pub(crate) struct Stream {
-897    pub(crate) id: StreamId,
-898    pub(crate) offset: u64,
-899    pub(crate) fin: bool,
-900    pub(crate) data: Bytes,
-901}
-902
-903impl FrameStruct for Stream {
-904    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
-905}
-906
-907/// Metadata from a stream frame
-908#[derive(Debug, Clone)]
-909pub(crate) struct StreamMeta {
-910    pub(crate) id: StreamId,
-911    pub(crate) offsets: Range<u64>,
-912    pub(crate) fin: bool,
-913}
-914
-915// This manual implementation exists because `Default` is not implemented for `StreamId`
-916impl Default for StreamMeta {
-917    fn default() -> Self {
-918        Self {
-919            id: StreamId(0),
-920            offsets: 0..0,
-921            fin: false,
-922        }
-923    }
-924}
-925
-926impl StreamMeta {
-927    pub(crate) fn encoder(&self, encode_length: bool) -> StreamMetaEncoder<'_> {
-928        StreamMetaEncoder {
-929            meta: self,
-930            encode_length,
-931        }
-932    }
-933}
-934
-935pub(crate) struct StreamMetaEncoder<'a> {
-936    meta: &'a StreamMeta,
-937    encode_length: bool,
-938}
-939
-940impl<'a> Encodable for StreamMetaEncoder<'a> {
-941    fn encode<W: BufMut>(&self, out: &mut W) {
-942        let StreamMetaEncoder {
-943            meta,
-944            encode_length: length,
-945        } = self;
-946        let mut ty = *StreamInfo::VALUES.start();
-947        if meta.offsets.start != 0 {
-948            ty |= 0x04;
-949        }
-950        if *length {
-951            ty |= 0x02;
-952        }
-953        if meta.fin {
-954            ty |= 0x01;
-955        }
-956        out.write_var(ty); // 1 byte
-957        out.write(meta.id); // <=8 bytes
-958        if meta.offsets.start != 0 {
-959            out.write_var(meta.offsets.start); // <=8 bytes
-960        }
-961        if *length {
-962            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
-963        }
-964    }
-965}
-966
-967/// A vector of [`StreamMeta`] with optimization for the single element case
-968pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
-969
-970#[derive(Debug, Clone)]
-971pub(crate) struct Crypto {
-972    pub(crate) offset: u64,
-973    pub(crate) data: Bytes,
-974}
-975
-976impl Crypto {
-977    pub(crate) const SIZE_BOUND: usize = 17;
+879    fn into_iter(self) -> Self::IntoIter {
+880        AckIter::new(self.largest, &self.additional[..])
+881    }
+882}
+883
+884impl PathAck {
+885    pub(crate) fn into_ack(self) -> (Ack, PathId) {
+886        let ack = Ack {
+887            largest: self.largest,
+888            delay: self.delay,
+889            additional: self.additional,
+890            ecn: self.ecn,
+891        };
+892
+893        (ack, self.path_id)
+894    }
+895
+896    fn get_type(&self) -> FrameType {
+897        if self.ecn.is_some() {
+898            FrameType::PathAckEcn
+899        } else {
+900            FrameType::PathAck
+901        }
+902    }
+903
+904    pub(crate) fn encoder<'a>(
+905        path_id: PathId,
+906        delay: u64,
+907        ranges: &'a ArrayRangeSet,
+908        ecn: Option<&'a EcnCounts>,
+909    ) -> PathAckEncoder<'a> {
+910        PathAckEncoder {
+911            path_id,
+912            delay,
+913            ranges,
+914            ecn,
+915        }
+916    }
+917}
+918
+919#[derive(derive_more::Display)]
+920#[display("{} path_id: {path_id} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+921pub(crate) struct PathAckEncoder<'a> {
+922    pub(super) path_id: PathId,
+923    pub(super) delay: u64,
+924    pub(super) ranges: &'a ArrayRangeSet,
+925    pub(super) ecn: Option<&'a EcnCounts>,
+926}
+927
+928impl<'a> PathAckEncoder<'a> {
+929    const fn get_type(&self) -> FrameType {
+930        match self.ecn.is_some() {
+931            true => FrameType::PathAckEcn,
+932            false => FrameType::PathAck,
+933        }
+934    }
+935}
+936
+937impl<'a> Encodable for PathAckEncoder<'a> {
+938    /// Encode [`Self`] into the given buffer
+939    ///
+940    /// The [`FrameType`] will be either [`FrameType::PathAckEcn`] or [`FrameType::PathAck`]
+941    /// depending on whether [`EcnCounts`] are provided.
+942    ///
+943    /// PANICS: if `ranges` is empty.
+944    fn encode<W: BufMut>(&self, buf: &mut W) {
+945        let PathAckEncoder {
+946            path_id,
+947            delay,
+948            ranges,
+949            ecn,
+950        } = self;
+951        let mut rest = ranges.iter().rev();
+952        let first = rest
+953            .next()
+954            .expect("Caller has verified ranges is non empty");
+955        let largest = first.end - 1;
+956        let first_size = first.end - first.start;
+957        let kind = match ecn.is_some() {
+958            true => FrameType::PathAckEcn,
+959            false => FrameType::PathAck,
+960        };
+961        buf.write(kind);
+962        buf.write(*path_id);
+963        buf.write_var(largest);
+964        buf.write_var(*delay);
+965        buf.write_var(ranges.len() as u64 - 1);
+966        buf.write_var(first_size - 1);
+967        let mut prev = first.start;
+968        for block in rest {
+969            let size = block.end - block.start;
+970            buf.write_var(prev - block.end - 1);
+971            buf.write_var(size - 1);
+972            prev = block.start;
+973        }
+974        if let Some(x) = ecn {
+975            x.encode(buf)
+976        }
+977    }
 978}
 979
-980impl Encodable for Crypto {
-981    fn encode<W: BufMut>(&self, out: &mut W) {
-982        out.write(FrameType::Crypto);
-983        out.write_var(self.offset);
-984        out.write_var(self.data.len() as u64);
-985        out.put_slice(&self.data);
-986    }
-987}
-988
-989#[derive(Debug, Clone)]
-990pub(crate) struct NewToken {
-991    pub(crate) token: Bytes,
-992}
-993
-994impl Encodable for NewToken {
-995    fn encode<W: BufMut>(&self, out: &mut W) {
-996        out.write(FrameType::NewToken);
-997        out.write_var(self.token.len() as u64);
-998        out.put_slice(&self.token);
-999    }
-1000}
-1001
-1002impl NewToken {
-1003    pub(crate) fn size(&self) -> usize {
-1004        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
-1005    }
-1006}
-1007
-1008#[derive(Debug, Clone)]
-1009pub(crate) struct MaxPathId(pub(crate) PathId);
-1010
-1011impl MaxPathId {
-1012    pub(crate) const SIZE_BOUND: usize =
-1013        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
-1014}
-1015
-1016impl Decodable for MaxPathId {
-1017    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-1018        Ok(Self(buf.get()?))
-1019    }
-1020}
-1021
-1022impl Encodable for MaxPathId {
-1023    fn encode<B: BufMut>(&self, buf: &mut B) {
-1024        buf.write(FrameType::MaxPathId);
-1025        buf.write(self.0);
-1026    }
-1027}
+980#[derive(Clone, Eq, PartialEq)]
+981pub(crate) struct Ack {
+982    pub largest: u64,
+983    pub delay: u64,
+984    pub additional: Bytes,
+985    pub ecn: Option<EcnCounts>,
+986}
+987
+988impl fmt::Debug for Ack {
+989    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+990        let mut ranges = "[".to_string();
+991        let mut first = true;
+992        for range in self.iter() {
+993            if !first {
+994                ranges.push(',');
+995            }
+996            write!(ranges, "{range:?}").unwrap();
+997            first = false;
+998        }
+999        ranges.push(']');
+1000
+1001        f.debug_struct("Ack")
+1002            .field("largest", &self.largest)
+1003            .field("delay", &self.delay)
+1004            .field("ecn", &self.ecn)
+1005            .field("ranges", &ranges)
+1006            .finish()
+1007    }
+1008}
+1009
+1010#[allow(unnameable_types)]
+1011impl<'a> IntoIterator for &'a Ack {
+1012    type Item = RangeInclusive<u64>;
+1013    type IntoIter = AckIter<'a>;
+1014
+1015    fn into_iter(self) -> AckIter<'a> {
+1016        AckIter::new(self.largest, &self.additional[..])
+1017    }
+1018}
+1019
+1020impl Ack {
+1021    pub(crate) fn encoder<'a>(
+1022        delay: u64,
+1023        ranges: &'a ArrayRangeSet,
+1024        ecn: Option<&'a EcnCounts>,
+1025    ) -> AckEncoder<'a> {
+1026        AckEncoder { delay, ranges, ecn }
+1027    }
 1028
-1029#[derive(Debug, Clone, PartialEq, Eq)]
-1030pub(crate) struct PathsBlocked(pub(crate) PathId);
-1031
-1032impl PathsBlocked {
-1033    pub(crate) const SIZE_BOUND: usize =
-1034        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
-1035}
-1036
-1037impl Encodable for PathsBlocked {
-1038    fn encode<B: BufMut>(&self, buf: &mut B) {
-1039        buf.write(FrameType::PathsBlocked);
-1040        buf.write(self.0);
-1041    }
-1042}
-1043
-1044impl Decodable for PathsBlocked {
-1045    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
-1046    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
-1047        Ok(Self(buf.get()?))
-1048    }
-1049}
-1050
-1051#[derive(Debug, Clone, PartialEq, Eq)]
-1052pub(crate) struct PathCidsBlocked {
-1053    pub(crate) path_id: PathId,
-1054    pub(crate) next_seq: VarInt,
-1055}
-1056
-1057impl PathCidsBlocked {
-1058    pub(crate) const SIZE_BOUND: usize =
-1059        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
-1060}
-1061
-1062impl Decodable for PathCidsBlocked {
-1063    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
-1064        Ok(Self {
-1065            path_id: buf.get()?,
-1066            next_seq: buf.get()?,
-1067        })
-1068    }
-1069}
-1070
-1071impl Encodable for PathCidsBlocked {
-1072    fn encode<W: BufMut>(&self, buf: &mut W) {
-1073        buf.write(FrameType::PathCidsBlocked);
-1074        buf.write(self.path_id);
-1075        buf.write(self.next_seq);
-1076    }
-1077}
-1078
-1079pub(crate) struct Iter {
-1080    bytes: Bytes,
-1081    last_ty: MaybeFrame,
-1082}
-1083
-1084impl Iter {
-1085    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
-1086        if payload.is_empty() {
-1087            // "An endpoint MUST treat receipt of a packet containing no frames as a
-1088            // connection error of type PROTOCOL_VIOLATION."
-1089            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
-1090            return Err(TransportError::PROTOCOL_VIOLATION(
-1091                "packet payload is empty",
-1092            ));
-1093        }
+1029    pub(crate) fn iter(&self) -> AckIter<'_> {
+1030        self.into_iter()
+1031    }
+1032
+1033    pub(crate) const fn get_type(&self) -> FrameType {
+1034        if self.ecn.is_some() {
+1035            FrameType::AckEcn
+1036        } else {
+1037            FrameType::Ack
+1038        }
+1039    }
+1040}
+1041
+1042#[derive(derive_more::Display)]
+1043#[display("{} ranges: {ranges:?} delay: {delay}µs", self.get_type())]
+1044pub(crate) struct AckEncoder<'a> {
+1045    pub(crate) delay: u64,
+1046    pub(crate) ranges: &'a ArrayRangeSet,
+1047    pub(crate) ecn: Option<&'a EcnCounts>,
+1048}
+1049
+1050impl<'a> AckEncoder<'a> {
+1051    const fn get_type(&self) -> FrameType {
+1052        match self.ecn.is_some() {
+1053            true => FrameType::AckEcn,
+1054            false => FrameType::Ack,
+1055        }
+1056    }
+1057}
+1058
+1059impl<'a> Encodable for AckEncoder<'a> {
+1060    fn encode<W: BufMut>(&self, buf: &mut W) {
+1061        let AckEncoder { delay, ranges, ecn } = self;
+1062        let mut rest = ranges.iter().rev();
+1063        let first = rest.next().unwrap();
+1064        let largest = first.end - 1;
+1065        let first_size = first.end - first.start;
+1066        let kind = match ecn.is_some() {
+1067            true => FrameType::AckEcn,
+1068            false => FrameType::Ack,
+1069        };
+1070        buf.write(kind);
+1071        buf.write_var(largest);
+1072        buf.write_var(*delay);
+1073        buf.write_var(ranges.len() as u64 - 1);
+1074        buf.write_var(first_size - 1);
+1075        let mut prev = first.start;
+1076        for block in rest {
+1077            let size = block.end - block.start;
+1078            buf.write_var(prev - block.end - 1);
+1079            buf.write_var(size - 1);
+1080            prev = block.start;
+1081        }
+1082        if let Some(x) = ecn {
+1083            x.encode(buf)
+1084        }
+1085    }
+1086}
+1087
+1088#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+1089pub(crate) struct EcnCounts {
+1090    pub ect0: u64,
+1091    pub ect1: u64,
+1092    pub ce: u64,
+1093}
 1094
-1095        Ok(Self {
-1096            bytes: payload,
-1097            last_ty: MaybeFrame::None,
-1098        })
-1099    }
-1100
-1101    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
-1102        let len = self.bytes.get_var()?;
-1103        if len > self.bytes.remaining() as u64 {
-1104            return Err(UnexpectedEnd);
-1105        }
-1106        Ok(self.bytes.split_to(len as usize))
-1107    }
-1108
-1109    #[track_caller]
-1110    fn try_next(&mut self) -> Result<Frame, IterErr> {
-1111        self.last_ty = self.bytes.get()?;
-1112
-1113        let ty = match self.last_ty {
-1114            MaybeFrame::None => FrameType::Padding,
-1115            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
-1116            MaybeFrame::Known(frame_type) => frame_type,
-1117        };
-1118        Ok(match ty {
-1119            FrameType::Padding => Frame::Padding,
-1120            FrameType::ResetStream => Frame::ResetStream(ResetStream {
-1121                id: self.bytes.get()?,
-1122                error_code: self.bytes.get()?,
-1123                final_offset: self.bytes.get()?,
-1124            }),
-1125            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
-1126                error_code: self.bytes.get()?,
-1127                frame_type: self.bytes.get()?,
-1128                reason: self.take_len()?,
-1129            })),
-1130            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
-1131                error_code: self.bytes.get()?,
-1132                reason: self.take_len()?,
-1133            })),
-1134            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
-1135            FrameType::MaxStreamData => Frame::MaxStreamData {
-1136                id: self.bytes.get()?,
-1137                offset: self.bytes.get_var()?,
-1138            },
-1139            FrameType::MaxStreamsBidi => Frame::MaxStreams {
-1140                dir: Dir::Bi,
-1141                count: self.bytes.get_var()?,
-1142            },
-1143            FrameType::MaxStreamsUni => Frame::MaxStreams {
-1144                dir: Dir::Uni,
-1145                count: self.bytes.get_var()?,
-1146            },
-1147            FrameType::Ping => Frame::Ping,
-1148            FrameType::DataBlocked => Frame::DataBlocked {
-1149                offset: self.bytes.get_var()?,
-1150            },
-1151            FrameType::StreamDataBlocked => Frame::StreamDataBlocked {
-1152                id: self.bytes.get()?,
-1153                offset: self.bytes.get_var()?,
-1154            },
-1155            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked {
-1156                dir: Dir::Bi,
-1157                limit: self.bytes.get_var()?,
-1158            },
-1159            FrameType::StreamsBlockedUni => Frame::StreamsBlocked {
-1160                dir: Dir::Uni,
-1161                limit: self.bytes.get_var()?,
-1162            },
-1163            FrameType::StopSending => Frame::StopSending(StopSending {
-1164                id: self.bytes.get()?,
-1165                error_code: self.bytes.get()?,
-1166            }),
-1167            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
-1168                Frame::RetireConnectionId(RetireConnectionId::decode(
-1169                    &mut self.bytes,
-1170                    ty == FrameType::PathRetireConnectionId,
-1171                )?)
-1172            }
-1173            FrameType::Ack | FrameType::AckEcn => {
-1174                let largest = self.bytes.get_var()?;
-1175                let delay = self.bytes.get_var()?;
-1176                let extra_blocks = self.bytes.get_var()? as usize;
-1177                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
-1178                Frame::Ack(Ack {
-1179                    delay,
-1180                    largest,
-1181                    additional: self.bytes.split_to(n),
-1182                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
-1183                        None
-1184                    } else {
-1185                        Some(EcnCounts {
-1186                            ect0: self.bytes.get_var()?,
-1187                            ect1: self.bytes.get_var()?,
-1188                            ce: self.bytes.get_var()?,
-1189                        })
-1190                    },
-1191                })
-1192            }
-1193            FrameType::PathAck | FrameType::PathAckEcn => {
-1194                let path_id = self.bytes.get()?;
-1195                let largest = self.bytes.get_var()?;
-1196                let delay = self.bytes.get_var()?;
-1197                let extra_blocks = self.bytes.get_var()? as usize;
-1198                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
-1199                Frame::PathAck(PathAck {
-1200                    path_id,
-1201                    delay,
-1202                    largest,
-1203                    additional: self.bytes.split_to(n),
-1204                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
-1205                        None
-1206                    } else {
-1207                        Some(EcnCounts {
-1208                            ect0: self.bytes.get_var()?,
-1209                            ect1: self.bytes.get_var()?,
-1210                            ce: self.bytes.get_var()?,
-1211                        })
-1212                    },
-1213                })
-1214            }
-1215            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
-1216            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
-1217            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
-1218                let read_path = ty == FrameType::PathNewConnectionId;
-1219                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
-1220            }
-1221            FrameType::Crypto => Frame::Crypto(Crypto {
-1222                offset: self.bytes.get_var()?,
-1223                data: self.take_len()?,
-1224            }),
-1225            FrameType::NewToken => Frame::NewToken(NewToken {
-1226                token: self.take_len()?,
-1227            }),
-1228            FrameType::HandshakeDone => Frame::HandshakeDone,
-1229            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
-1230                sequence: self.bytes.get()?,
-1231                ack_eliciting_threshold: self.bytes.get()?,
-1232                request_max_ack_delay: self.bytes.get()?,
-1233                reordering_threshold: self.bytes.get()?,
-1234            }),
-1235            FrameType::ImmediateAck => Frame::ImmediateAck,
-1236            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
-1237                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
-1238                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
-1239                Frame::ObservedAddr(observed)
-1240            }
-1241            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
-1242            FrameType::PathStatusAvailable => {
-1243                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
-1244            }
-1245            FrameType::PathStatusBackup => {
-1246                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
-1247            }
-1248            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
-1249            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
-1250            FrameType::PathCidsBlocked => {
-1251                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
-1252            }
-1253            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
-1254                let is_ipv6 = ty == FrameType::AddIpv6Address;
-1255                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
-1256                Frame::AddAddress(add_address)
-1257            }
-1258            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
-1259                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
-1260                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
-1261                Frame::ReachOut(reach_out)
-1262            }
-1263            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
-1264            FrameType::Stream(s) => Frame::Stream(Stream {
-1265                id: self.bytes.get()?,
-1266                offset: if s.off() { self.bytes.get_var()? } else { 0 },
-1267                fin: s.fin(),
-1268                data: if s.len() {
-1269                    self.take_len()?
-1270                } else {
-1271                    self.take_remaining()
-1272                },
-1273            }),
-1274            FrameType::Datagram(d) => Frame::Datagram(Datagram {
-1275                data: if d.len() {
-1276                    self.take_len()?
-1277                } else {
-1278                    self.take_remaining()
-1279                },
-1280            }),
-1281        })
-1282    }
-1283
-1284    fn take_remaining(&mut self) -> Bytes {
-1285        mem::take(&mut self.bytes)
+1095impl std::ops::AddAssign<EcnCodepoint> for EcnCounts {
+1096    fn add_assign(&mut self, rhs: EcnCodepoint) {
+1097        match rhs {
+1098            EcnCodepoint::Ect0 => {
+1099                self.ect0 += 1;
+1100            }
+1101            EcnCodepoint::Ect1 => {
+1102                self.ect1 += 1;
+1103            }
+1104            EcnCodepoint::Ce => {
+1105                self.ce += 1;
+1106            }
+1107        }
+1108    }
+1109}
+1110
+1111impl EcnCounts {
+1112    pub(crate) const ZERO: Self = Self {
+1113        ect0: 0,
+1114        ect1: 0,
+1115        ce: 0,
+1116    };
+1117}
+1118
+1119impl Encodable for EcnCounts {
+1120    fn encode<W: BufMut>(&self, out: &mut W) {
+1121        out.write_var(self.ect0);
+1122        out.write_var(self.ect1);
+1123        out.write_var(self.ce);
+1124    }
+1125}
+1126
+1127#[derive(Debug, Clone)]
+1128pub(crate) struct Stream {
+1129    pub(crate) id: StreamId,
+1130    pub(crate) offset: u64,
+1131    pub(crate) fin: bool,
+1132    pub(crate) data: Bytes,
+1133}
+1134
+1135impl FrameStruct for Stream {
+1136    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
+1137}
+1138
+1139/// Metadata from a stream frame
+1140#[derive(Debug, Clone, derive_more::Display)]
+1141#[display("STREAM id: {id} off: {} len: {} fin: {fin}", offsets.start, offsets.end - offsets.start)]
+1142pub(crate) struct StreamMeta {
+1143    pub(crate) id: StreamId,
+1144    pub(crate) offsets: Range<u64>,
+1145    pub(crate) fin: bool,
+1146}
+1147
+1148// This manual implementation exists because `Default` is not implemented for `StreamId`
+1149impl Default for StreamMeta {
+1150    fn default() -> Self {
+1151        Self {
+1152            id: StreamId(0),
+1153            offsets: 0..0,
+1154            fin: false,
+1155        }
+1156    }
+1157}
+1158
+1159impl StreamMeta {
+1160    pub(crate) fn encoder(self, encode_length: bool) -> StreamMetaEncoder {
+1161        StreamMetaEncoder {
+1162            meta: self,
+1163            encode_length,
+1164        }
+1165    }
+1166
+1167    const fn get_type(&self, encode_length: bool) -> StreamInfo {
+1168        let mut ty = *StreamInfo::VALUES.start();
+1169        if self.offsets.start != 0 {
+1170            ty |= 0x04;
+1171        }
+1172        if encode_length {
+1173            ty |= 0x02;
+1174        }
+1175        if self.fin {
+1176            ty |= 0x01;
+1177        }
+1178        StreamInfo(ty as u8)
+1179    }
+1180}
+1181
+1182#[derive(derive_more::Display)]
+1183#[display("{meta}")]
+1184pub(crate) struct StreamMetaEncoder {
+1185    pub(crate) meta: StreamMeta,
+1186    encode_length: bool,
+1187}
+1188
+1189impl StreamMetaEncoder {
+1190    const fn get_type(&self) -> FrameType {
+1191        FrameType::Stream(self.meta.get_type(self.encode_length))
+1192    }
+1193}
+1194
+1195impl Encodable for StreamMetaEncoder {
+1196    fn encode<W: BufMut>(&self, out: &mut W) {
+1197        let Self {
+1198            meta,
+1199            encode_length,
+1200        } = self;
+1201        out.write_var(meta.get_type(*encode_length).0 as u64); // 1 byte
+1202        out.write(meta.id); // <=8 bytes
+1203        if meta.offsets.start != 0 {
+1204            out.write_var(meta.offsets.start); // <=8 bytes
+1205        }
+1206        if *encode_length {
+1207            out.write_var(meta.offsets.end - meta.offsets.start); // <=8 bytes
+1208        }
+1209    }
+1210}
+1211
+1212/// A vector of [`StreamMeta`] with optimization for the single element case
+1213pub(crate) type StreamMetaVec = TinyVec<[StreamMeta; 1]>;
+1214
+1215#[derive(Debug, Clone, derive_more::Display)]
+1216#[display("CRYPTO off: {offset} len = {}", data.len())]
+1217pub(crate) struct Crypto {
+1218    pub(crate) offset: u64,
+1219    pub(crate) data: Bytes,
+1220}
+1221
+1222impl Crypto {
+1223    pub(crate) const SIZE_BOUND: usize = 17;
+1224
+1225    const fn get_type(&self) -> FrameType {
+1226        FrameType::Crypto
+1227    }
+1228}
+1229
+1230impl Encodable for Crypto {
+1231    fn encode<W: BufMut>(&self, out: &mut W) {
+1232        out.write(FrameType::Crypto);
+1233        out.write_var(self.offset);
+1234        out.write_var(self.data.len() as u64);
+1235        out.put_slice(&self.data);
+1236    }
+1237}
+1238
+1239#[derive(Debug, Clone, derive_more::Display)]
+1240#[display("NEW_TOKEN")]
+1241pub(crate) struct NewToken {
+1242    pub(crate) token: Bytes,
+1243}
+1244
+1245impl Encodable for NewToken {
+1246    fn encode<W: BufMut>(&self, out: &mut W) {
+1247        out.write(FrameType::NewToken);
+1248        out.write_var(self.token.len() as u64);
+1249        out.put_slice(&self.token);
+1250    }
+1251}
+1252
+1253impl NewToken {
+1254    pub(crate) fn size(&self) -> usize {
+1255        1 + VarInt::from_u64(self.token.len() as u64).unwrap().size() + self.token.len()
+1256    }
+1257
+1258    const fn get_type(&self) -> FrameType {
+1259        FrameType::NewToken
+1260    }
+1261}
+1262
+1263#[derive(Debug, Clone, derive_more::Display)]
+1264#[display("MAX_PATH_ID path_id: {_0}")]
+1265pub(crate) struct MaxPathId(pub(crate) PathId);
+1266
+1267impl MaxPathId {
+1268    pub(crate) const SIZE_BOUND: usize =
+1269        FrameType::MaxPathId.size() + VarInt(u32::MAX as u64).size();
+1270
+1271    const fn get_type(&self) -> FrameType {
+1272        FrameType::MaxPathId
+1273    }
+1274}
+1275
+1276impl Decodable for MaxPathId {
+1277    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1278        Ok(Self(buf.get()?))
+1279    }
+1280}
+1281
+1282impl Encodable for MaxPathId {
+1283    fn encode<B: BufMut>(&self, buf: &mut B) {
+1284        buf.write(FrameType::MaxPathId);
+1285        buf.write(self.0);
 1286    }
 1287}
 1288
-1289impl Iterator for Iter {
-1290    type Item = Result<Frame, InvalidFrame>;
-1291    fn next(&mut self) -> Option<Self::Item> {
-1292        if !self.bytes.has_remaining() {
-1293            return None;
-1294        }
-1295        match self.try_next() {
-1296            Ok(x) => Some(Ok(x)),
-1297            Err(e) => {
-1298                // Corrupt frame, skip it and everything that follows
-1299                self.bytes.clear();
-1300                Some(Err(InvalidFrame {
-1301                    ty: self.last_ty,
-1302                    reason: e.reason(),
-1303                }))
-1304            }
-1305        }
+1289#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1290#[display("PATHS_BLOCKED remote_max_path_id: {_0}")]
+1291pub(crate) struct PathsBlocked(pub(crate) PathId);
+1292
+1293impl PathsBlocked {
+1294    pub(crate) const SIZE_BOUND: usize =
+1295        FrameType::PathsBlocked.size() + VarInt(u32::MAX as u64).size();
+1296
+1297    const fn get_type(&self) -> FrameType {
+1298        FrameType::PathsBlocked
+1299    }
+1300}
+1301
+1302impl Encodable for PathsBlocked {
+1303    fn encode<B: BufMut>(&self, buf: &mut B) {
+1304        buf.write(FrameType::PathsBlocked);
+1305        buf.write(self.0);
 1306    }
 1307}
 1308
-1309#[derive(Debug)]
-1310pub(crate) struct InvalidFrame {
-1311    pub(crate) ty: MaybeFrame,
-1312    pub(crate) reason: &'static str,
-1313}
-1314
-1315impl From<InvalidFrame> for TransportError {
-1316    fn from(err: InvalidFrame) -> Self {
-1317        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
-1318        te.frame = err.ty;
-1319        te
-1320    }
+1309impl Decodable for PathsBlocked {
+1310    /// Decode [`Self`] from the buffer, provided that the frame type has been verified
+1311    fn decode<B: Buf>(buf: &mut B) -> coding::Result<Self> {
+1312        Ok(Self(buf.get()?))
+1313    }
+1314}
+1315
+1316#[derive(Debug, Clone, PartialEq, Eq, derive_more::Display)]
+1317#[display("PATH_CIDS_BLOCKED path_id: {path_id} next_seq: {next_seq}")]
+1318pub(crate) struct PathCidsBlocked {
+1319    pub(crate) path_id: PathId,
+1320    pub(crate) next_seq: VarInt,
 1321}
 1322
-1323/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover
-1324fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> {
-1325    let total_len = buf.remaining();
-1326    let first_block = buf.get_var()?;
-1327    let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?;
-1328    for _ in 0..n {
-1329        let gap = buf.get_var()?;
-1330        smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?;
-1331        let block = buf.get_var()?;
-1332        smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?;
-1333    }
-1334    Ok(total_len - buf.remaining())
-1335}
-1336
-1337#[derive(Debug)]
-1338enum IterErr {
-1339    UnexpectedEnd,
-1340    InvalidFrameId,
-1341    Malformed,
-1342}
-1343
-1344impl IterErr {
-1345    fn reason(&self) -> &'static str {
-1346        use IterErr::*;
-1347        match *self {
-1348            UnexpectedEnd => "unexpected end",
-1349            InvalidFrameId => "invalid frame ID",
-1350            Malformed => "malformed",
-1351        }
-1352    }
-1353}
-1354
-1355impl From<UnexpectedEnd> for IterErr {
-1356    fn from(_: UnexpectedEnd) -> Self {
-1357        Self::UnexpectedEnd
-1358    }
-1359}
-1360
-1361#[derive(Debug, Clone)]
-1362pub(crate) struct AckIter<'a> {
-1363    largest: u64,
-1364    data: &'a [u8],
-1365}
-1366
-1367impl<'a> AckIter<'a> {
-1368    fn new(largest: u64, data: &'a [u8]) -> Self {
-1369        Self { largest, data }
-1370    }
-1371}
-1372
-1373impl Iterator for AckIter<'_> {
-1374    type Item = RangeInclusive<u64>;
-1375    fn next(&mut self) -> Option<RangeInclusive<u64>> {
-1376        if !self.data.has_remaining() {
-1377            return None;
-1378        }
-1379        let block = self.data.get_var().unwrap();
-1380        let largest = self.largest;
-1381        if let Ok(gap) = self.data.get_var() {
-1382            self.largest -= block + gap + 2;
-1383        }
-1384        Some(largest - block..=largest)
-1385    }
-1386}
-1387
-1388#[allow(unreachable_pub)] // fuzzing only
-1389#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
-1390#[derive(Debug, Copy, Clone)]
-1391pub struct ResetStream {
-1392    pub(crate) id: StreamId,
-1393    pub(crate) error_code: VarInt,
-1394    pub(crate) final_offset: VarInt,
-1395}
-1396
-1397impl FrameStruct for ResetStream {
-1398    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
-1399}
-1400
-1401impl Encodable for ResetStream {
-1402    fn encode<W: BufMut>(&self, out: &mut W) {
-1403        out.write(FrameType::ResetStream); // 1 byte
-1404        out.write(self.id); // <= 8 bytes
-1405        out.write(self.error_code); // <= 8 bytes
-1406        out.write(self.final_offset); // <= 8 bytes
-1407    }
-1408}
-1409
-1410#[derive(Debug, Copy, Clone)]
-1411pub(crate) struct StopSending {
-1412    pub(crate) id: StreamId,
-1413    pub(crate) error_code: VarInt,
-1414}
-1415
-1416impl FrameStruct for StopSending {
-1417    const SIZE_BOUND: usize = 1 + 8 + 8;
-1418}
-1419
-1420impl Encodable for StopSending {
-1421    fn encode<W: BufMut>(&self, out: &mut W) {
-1422        out.write(FrameType::StopSending); // 1 byte
-1423        out.write(self.id); // <= 8 bytes
-1424        out.write(self.error_code) // <= 8 bytes
-1425    }
-1426}
-1427
-1428#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-1429pub(crate) struct NewConnectionId {
-1430    pub(crate) path_id: Option<PathId>,
-1431    pub(crate) sequence: u64,
-1432    pub(crate) retire_prior_to: u64,
-1433    pub(crate) id: ConnectionId,
-1434    pub(crate) reset_token: ResetToken,
-1435}
-1436
-1437impl NewConnectionId {
-1438    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
-1439    pub(crate) const SIZE_BOUND: usize = {
-1440        let type_len = FrameType::NewConnectionId.size();
-1441        let seq_max_len = 8usize;
-1442        let retire_prior_to_max_len = 8usize;
-1443        let cid_len_len = 1;
-1444        let cid_len = 160;
-1445        let reset_token_len = 16;
-1446        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
-1447    };
-1448
-1449    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
-1450    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
-1451        let type_len = FrameType::PathNewConnectionId.size();
-1452        let path_id_len = VarInt::from_u32(u32::MAX).size();
-1453        let seq_max_len = 8usize;
-1454        let retire_prior_to_max_len = 8usize;
-1455        let cid_len_len = 1;
-1456        let cid_len = 160;
-1457        let reset_token_len = 16;
-1458        type_len
-1459            + path_id_len
-1460            + seq_max_len
-1461            + retire_prior_to_max_len
-1462            + cid_len_len
-1463            + cid_len
-1464            + reset_token_len
-1465    };
-1466
-1467    pub(crate) fn get_type(&self) -> FrameType {
-1468        if self.path_id.is_some() {
-1469            FrameType::PathNewConnectionId
-1470        } else {
-1471            FrameType::NewConnectionId
-1472        }
-1473    }
-1474
-1475    /// Returns the maximum encoded size on the wire.
-1476    ///
-1477    /// This is a rough upper estimate, does not squeeze every last byte out.
-1478    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
-1479        let upper_bound = match path_new_cid {
-1480            true => Self::SIZE_BOUND_MULTIPATH,
-1481            false => Self::SIZE_BOUND,
-1482        };
-1483        // instead of using the maximum cid len, use the provided one
-1484        upper_bound - 160 + cid_len
-1485    }
-1486
-1487    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
-1488        let path_id = if read_path { Some(bytes.get()?) } else { None };
-1489        let sequence = bytes.get_var()?;
-1490        let retire_prior_to = bytes.get_var()?;
-1491        if retire_prior_to > sequence {
-1492            return Err(IterErr::Malformed);
-1493        }
-1494        let length = bytes.get::<u8>()? as usize;
-1495        if length > MAX_CID_SIZE || length == 0 {
-1496            return Err(IterErr::Malformed);
-1497        }
-1498        if length > bytes.remaining() {
-1499            return Err(IterErr::UnexpectedEnd);
-1500        }
-1501        let mut stage = [0; MAX_CID_SIZE];
-1502        bytes.copy_to_slice(&mut stage[0..length]);
-1503        let id = ConnectionId::new(&stage[..length]);
-1504        if bytes.remaining() < 16 {
-1505            return Err(IterErr::UnexpectedEnd);
-1506        }
-1507        let mut reset_token = [0; RESET_TOKEN_SIZE];
-1508        bytes.copy_to_slice(&mut reset_token);
-1509        Ok(Self {
-1510            path_id,
-1511            sequence,
-1512            retire_prior_to,
-1513            id,
-1514            reset_token: reset_token.into(),
-1515        })
-1516    }
-1517}
-1518
-1519impl Encodable for NewConnectionId {
-1520    fn encode<W: BufMut>(&self, out: &mut W) {
-1521        out.write(self.get_type());
-1522        if let Some(id) = self.path_id {
-1523            out.write(id);
-1524        }
-1525        out.write_var(self.sequence);
-1526        out.write_var(self.retire_prior_to);
-1527        out.write(self.id.len() as u8);
-1528        out.put_slice(&self.id);
-1529        out.put_slice(&self.reset_token);
-1530    }
-1531}
-1532
-1533impl FrameStruct for NewConnectionId {
-1534    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
-1535}
-1536
-1537/// An unreliable datagram
-1538#[derive(Debug, Clone)]
-1539pub struct Datagram {
-1540    /// Payload
-1541    pub data: Bytes,
-1542}
-1543
-1544impl FrameStruct for Datagram {
-1545    const SIZE_BOUND: usize = 1 + 8;
-1546}
-1547
-1548impl Datagram {
-1549    pub(crate) fn size(&self, length: bool) -> usize {
-1550        1 + if length {
-1551            VarInt::from_u64(self.data.len() as u64).unwrap().size()
-1552        } else {
-1553            0
-1554        } + self.data.len()
-1555    }
-1556}
-1557
-1558impl Encodable for Datagram {
-1559    fn encode<B: BufMut>(&self, out: &mut B) {
-1560        // A datagram is encoded only after this is verified.
-1561        const ENCODE_LEN: bool = true;
-1562        out.write(FrameType::Datagram(DatagramInfo(
-1563            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
-1564        ))); // 1 byte
-1565        // Safe to unwrap because we check length sanity before queueing datagrams
-1566        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
-1567        out.put_slice(&self.data);
-1568    }
-1569}
-1570
-1571#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-1572pub(crate) struct AckFrequency {
-1573    pub(crate) sequence: VarInt,
-1574    pub(crate) ack_eliciting_threshold: VarInt,
-1575    pub(crate) request_max_ack_delay: VarInt,
-1576    pub(crate) reordering_threshold: VarInt,
-1577}
-1578
-1579impl Encodable for AckFrequency {
-1580    fn encode<W: BufMut>(&self, buf: &mut W) {
-1581        buf.write(FrameType::AckFrequency);
-1582        buf.write(self.sequence);
-1583        buf.write(self.ack_eliciting_threshold);
-1584        buf.write(self.request_max_ack_delay);
-1585        buf.write(self.reordering_threshold);
-1586    }
-1587}
-1588
-1589/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
-1590
-1591/// Conjunction of the information contained in the address discovery frames
-1592/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
-1593#[derive(Debug, PartialEq, Eq, Clone)]
-1594pub(crate) struct ObservedAddr {
-1595    /// Monotonically increasing integer within the same connection.
-1596    pub(crate) seq_no: VarInt,
-1597    /// Reported observed address.
-1598    pub(crate) ip: IpAddr,
-1599    /// Reported observed port.
-1600    pub(crate) port: u16,
-1601}
-1602
-1603impl ObservedAddr {
-1604    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
-1605        Self {
-1606            ip: remote.ip(),
-1607            port: remote.port(),
-1608            seq_no: seq_no.into(),
-1609        }
-1610    }
-1611
-1612    /// Get the [`FrameType`] for this frame.
-1613    pub(crate) fn get_type(&self) -> FrameType {
-1614        if self.ip.is_ipv6() {
-1615            FrameType::ObservedIpv6Addr
-1616        } else {
-1617            FrameType::ObservedIpv4Addr
+1323impl PathCidsBlocked {
+1324    pub(crate) const SIZE_BOUND: usize =
+1325        FrameType::PathCidsBlocked.size() + VarInt(u32::MAX as u64).size() + VarInt::MAX.size();
+1326
+1327    const fn get_type(&self) -> FrameType {
+1328        FrameType::PathCidsBlocked
+1329    }
+1330}
+1331
+1332impl Decodable for PathCidsBlocked {
+1333    fn decode<R: Buf>(buf: &mut R) -> coding::Result<Self> {
+1334        Ok(Self {
+1335            path_id: buf.get()?,
+1336            next_seq: buf.get()?,
+1337        })
+1338    }
+1339}
+1340
+1341impl Encodable for PathCidsBlocked {
+1342    fn encode<W: BufMut>(&self, buf: &mut W) {
+1343        buf.write(FrameType::PathCidsBlocked);
+1344        buf.write(self.path_id);
+1345        buf.write(self.next_seq);
+1346    }
+1347}
+1348
+1349pub(crate) struct Iter {
+1350    bytes: Bytes,
+1351    last_ty: MaybeFrame,
+1352}
+1353
+1354impl Iter {
+1355    pub(crate) fn new(payload: Bytes) -> Result<Self, TransportError> {
+1356        if payload.is_empty() {
+1357            // "An endpoint MUST treat receipt of a packet containing no frames as a
+1358            // connection error of type PROTOCOL_VIOLATION."
+1359            // https://www.rfc-editor.org/rfc/rfc9000.html#name-frames-and-frame-types
+1360            return Err(TransportError::PROTOCOL_VIOLATION(
+1361                "packet payload is empty",
+1362            ));
+1363        }
+1364
+1365        Ok(Self {
+1366            bytes: payload,
+1367            last_ty: MaybeFrame::None,
+1368        })
+1369    }
+1370
+1371    fn take_len(&mut self) -> Result<Bytes, UnexpectedEnd> {
+1372        let len = self.bytes.get_var()?;
+1373        if len > self.bytes.remaining() as u64 {
+1374            return Err(UnexpectedEnd);
+1375        }
+1376        Ok(self.bytes.split_to(len as usize))
+1377    }
+1378
+1379    #[track_caller]
+1380    fn try_next(&mut self) -> Result<Frame, IterErr> {
+1381        self.last_ty = self.bytes.get()?;
+1382
+1383        let ty = match self.last_ty {
+1384            MaybeFrame::None => FrameType::Padding,
+1385            MaybeFrame::Unknown(_other) => return Err(IterErr::InvalidFrameId),
+1386            MaybeFrame::Known(frame_type) => frame_type,
+1387        };
+1388        Ok(match ty {
+1389            FrameType::Padding => Frame::Padding,
+1390            FrameType::ResetStream => Frame::ResetStream(ResetStream {
+1391                id: self.bytes.get()?,
+1392                error_code: self.bytes.get()?,
+1393                final_offset: self.bytes.get()?,
+1394            }),
+1395            FrameType::ConnectionClose => Frame::Close(Close::Connection(ConnectionClose {
+1396                error_code: self.bytes.get()?,
+1397                frame_type: self.bytes.get()?,
+1398                reason: self.take_len()?,
+1399            })),
+1400            FrameType::ApplicationClose => Frame::Close(Close::Application(ApplicationClose {
+1401                error_code: self.bytes.get()?,
+1402                reason: self.take_len()?,
+1403            })),
+1404            FrameType::MaxData => Frame::MaxData(self.bytes.get()?),
+1405            FrameType::MaxStreamData => Frame::MaxStreamData(self.bytes.get()?),
+1406            FrameType::MaxStreamsBidi => Frame::MaxStreams(MaxStreams {
+1407                dir: Dir::Bi,
+1408                count: self.bytes.get_var()?,
+1409            }),
+1410            FrameType::MaxStreamsUni => Frame::MaxStreams(MaxStreams {
+1411                dir: Dir::Uni,
+1412                count: self.bytes.get_var()?,
+1413            }),
+1414            FrameType::Ping => Frame::Ping,
+1415            FrameType::DataBlocked => Frame::DataBlocked {
+1416                offset: self.bytes.get_var()?,
+1417            },
+1418            FrameType::StreamDataBlocked => Frame::StreamDataBlocked {
+1419                id: self.bytes.get()?,
+1420                offset: self.bytes.get_var()?,
+1421            },
+1422            FrameType::StreamsBlockedBidi => Frame::StreamsBlocked {
+1423                dir: Dir::Bi,
+1424                limit: self.bytes.get_var()?,
+1425            },
+1426            FrameType::StreamsBlockedUni => Frame::StreamsBlocked {
+1427                dir: Dir::Uni,
+1428                limit: self.bytes.get_var()?,
+1429            },
+1430            FrameType::StopSending => Frame::StopSending(StopSending {
+1431                id: self.bytes.get()?,
+1432                error_code: self.bytes.get()?,
+1433            }),
+1434            FrameType::RetireConnectionId | FrameType::PathRetireConnectionId => {
+1435                Frame::RetireConnectionId(RetireConnectionId::decode(
+1436                    &mut self.bytes,
+1437                    ty == FrameType::PathRetireConnectionId,
+1438                )?)
+1439            }
+1440            FrameType::Ack | FrameType::AckEcn => {
+1441                let largest = self.bytes.get_var()?;
+1442                let delay = self.bytes.get_var()?;
+1443                let extra_blocks = self.bytes.get_var()? as usize;
+1444                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+1445                Frame::Ack(Ack {
+1446                    delay,
+1447                    largest,
+1448                    additional: self.bytes.split_to(n),
+1449                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
+1450                        None
+1451                    } else {
+1452                        Some(EcnCounts {
+1453                            ect0: self.bytes.get_var()?,
+1454                            ect1: self.bytes.get_var()?,
+1455                            ce: self.bytes.get_var()?,
+1456                        })
+1457                    },
+1458                })
+1459            }
+1460            FrameType::PathAck | FrameType::PathAckEcn => {
+1461                let path_id = self.bytes.get()?;
+1462                let largest = self.bytes.get_var()?;
+1463                let delay = self.bytes.get_var()?;
+1464                let extra_blocks = self.bytes.get_var()? as usize;
+1465                let n = scan_ack_blocks(&self.bytes, largest, extra_blocks)?;
+1466                Frame::PathAck(PathAck {
+1467                    path_id,
+1468                    delay,
+1469                    largest,
+1470                    additional: self.bytes.split_to(n),
+1471                    ecn: if ty != FrameType::AckEcn && ty != FrameType::PathAckEcn {
+1472                        None
+1473                    } else {
+1474                        Some(EcnCounts {
+1475                            ect0: self.bytes.get_var()?,
+1476                            ect1: self.bytes.get_var()?,
+1477                            ce: self.bytes.get_var()?,
+1478                        })
+1479                    },
+1480                })
+1481            }
+1482            FrameType::PathChallenge => Frame::PathChallenge(self.bytes.get()?),
+1483            FrameType::PathResponse => Frame::PathResponse(self.bytes.get()?),
+1484            FrameType::NewConnectionId | FrameType::PathNewConnectionId => {
+1485                let read_path = ty == FrameType::PathNewConnectionId;
+1486                Frame::NewConnectionId(NewConnectionId::read(&mut self.bytes, read_path)?)
+1487            }
+1488            FrameType::Crypto => Frame::Crypto(Crypto {
+1489                offset: self.bytes.get_var()?,
+1490                data: self.take_len()?,
+1491            }),
+1492            FrameType::NewToken => Frame::NewToken(NewToken {
+1493                token: self.take_len()?,
+1494            }),
+1495            FrameType::HandshakeDone => Frame::HandshakeDone,
+1496            FrameType::AckFrequency => Frame::AckFrequency(AckFrequency {
+1497                sequence: self.bytes.get()?,
+1498                ack_eliciting_threshold: self.bytes.get()?,
+1499                request_max_ack_delay: self.bytes.get()?,
+1500                reordering_threshold: self.bytes.get()?,
+1501            }),
+1502            FrameType::ImmediateAck => Frame::ImmediateAck,
+1503            FrameType::ObservedIpv4Addr | FrameType::ObservedIpv6Addr => {
+1504                let is_ipv6 = ty == FrameType::ObservedIpv6Addr;
+1505                let observed = ObservedAddr::read(&mut self.bytes, is_ipv6)?;
+1506                Frame::ObservedAddr(observed)
+1507            }
+1508            FrameType::PathAbandon => Frame::PathAbandon(PathAbandon::decode(&mut self.bytes)?),
+1509            FrameType::PathStatusAvailable => {
+1510                Frame::PathStatusAvailable(PathStatusAvailable::decode(&mut self.bytes)?)
+1511            }
+1512            FrameType::PathStatusBackup => {
+1513                Frame::PathStatusBackup(PathStatusBackup::decode(&mut self.bytes)?)
+1514            }
+1515            FrameType::MaxPathId => Frame::MaxPathId(MaxPathId::decode(&mut self.bytes)?),
+1516            FrameType::PathsBlocked => Frame::PathsBlocked(PathsBlocked::decode(&mut self.bytes)?),
+1517            FrameType::PathCidsBlocked => {
+1518                Frame::PathCidsBlocked(PathCidsBlocked::decode(&mut self.bytes)?)
+1519            }
+1520            FrameType::AddIpv4Address | FrameType::AddIpv6Address => {
+1521                let is_ipv6 = ty == FrameType::AddIpv6Address;
+1522                let add_address = AddAddress::read(&mut self.bytes, is_ipv6)?;
+1523                Frame::AddAddress(add_address)
+1524            }
+1525            FrameType::ReachOutAtIpv4 | FrameType::ReachOutAtIpv6 => {
+1526                let is_ipv6 = ty == FrameType::ReachOutAtIpv6;
+1527                let reach_out = ReachOut::read(&mut self.bytes, is_ipv6)?;
+1528                Frame::ReachOut(reach_out)
+1529            }
+1530            FrameType::RemoveAddress => Frame::RemoveAddress(RemoveAddress::read(&mut self.bytes)?),
+1531            FrameType::Stream(s) => Frame::Stream(Stream {
+1532                id: self.bytes.get()?,
+1533                offset: if s.off() { self.bytes.get_var()? } else { 0 },
+1534                fin: s.fin(),
+1535                data: if s.len() {
+1536                    self.take_len()?
+1537                } else {
+1538                    self.take_remaining()
+1539                },
+1540            }),
+1541            FrameType::Datagram(d) => Frame::Datagram(Datagram {
+1542                data: if d.len() {
+1543                    self.take_len()?
+1544                } else {
+1545                    self.take_remaining()
+1546                },
+1547            }),
+1548        })
+1549    }
+1550
+1551    fn take_remaining(&mut self) -> Bytes {
+1552        mem::take(&mut self.bytes)
+1553    }
+1554}
+1555
+1556impl Iterator for Iter {
+1557    type Item = Result<Frame, InvalidFrame>;
+1558    fn next(&mut self) -> Option<Self::Item> {
+1559        if !self.bytes.has_remaining() {
+1560            return None;
+1561        }
+1562        match self.try_next() {
+1563            Ok(x) => Some(Ok(x)),
+1564            Err(e) => {
+1565                // Corrupt frame, skip it and everything that follows
+1566                self.bytes.clear();
+1567                Some(Err(InvalidFrame {
+1568                    ty: self.last_ty,
+1569                    reason: e.reason(),
+1570                }))
+1571            }
+1572        }
+1573    }
+1574}
+1575
+1576#[derive(Debug)]
+1577pub(crate) struct InvalidFrame {
+1578    pub(crate) ty: MaybeFrame,
+1579    pub(crate) reason: &'static str,
+1580}
+1581
+1582impl From<InvalidFrame> for TransportError {
+1583    fn from(err: InvalidFrame) -> Self {
+1584        let mut te = Self::FRAME_ENCODING_ERROR(err.reason);
+1585        te.frame = err.ty;
+1586        te
+1587    }
+1588}
+1589
+1590/// Validate exactly `n` ACK ranges in `buf` and return the number of bytes they cover
+1591fn scan_ack_blocks(mut buf: &[u8], largest: u64, n: usize) -> Result<usize, IterErr> {
+1592    let total_len = buf.remaining();
+1593    let first_block = buf.get_var()?;
+1594    let mut smallest = largest.checked_sub(first_block).ok_or(IterErr::Malformed)?;
+1595    for _ in 0..n {
+1596        let gap = buf.get_var()?;
+1597        smallest = smallest.checked_sub(gap + 2).ok_or(IterErr::Malformed)?;
+1598        let block = buf.get_var()?;
+1599        smallest = smallest.checked_sub(block).ok_or(IterErr::Malformed)?;
+1600    }
+1601    Ok(total_len - buf.remaining())
+1602}
+1603
+1604#[derive(Debug)]
+1605enum IterErr {
+1606    UnexpectedEnd,
+1607    InvalidFrameId,
+1608    Malformed,
+1609}
+1610
+1611impl IterErr {
+1612    fn reason(&self) -> &'static str {
+1613        use IterErr::*;
+1614        match *self {
+1615            UnexpectedEnd => "unexpected end",
+1616            InvalidFrameId => "invalid frame ID",
+1617            Malformed => "malformed",
 1618        }
 1619    }
-1620
-1621    /// Compute the number of bytes needed to encode the frame.
-1622    pub(crate) fn size(&self) -> usize {
-1623        let type_size = self.get_type().size();
-1624        let req_id_bytes = self.seq_no.size();
-1625        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
-1626        let port_bytes = 2;
-1627        type_size + req_id_bytes + ip_bytes + port_bytes
-1628    }
-1629
-1630    /// Reads the frame contents from the buffer.
-1631    ///
-1632    /// Should only be called when the frame type has been identified as
-1633    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
-1634    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
-1635        let seq_no = bytes.get()?;
-1636        let ip = if is_ipv6 {
-1637            IpAddr::V6(bytes.get()?)
-1638        } else {
-1639            IpAddr::V4(bytes.get()?)
-1640        };
-1641        let port = bytes.get()?;
-1642        Ok(Self { seq_no, ip, port })
-1643    }
-1644
-1645    /// Gives the [`SocketAddr`] reported in the frame.
-1646    pub(crate) fn socket_addr(&self) -> SocketAddr {
-1647        (self.ip, self.port).into()
-1648    }
-1649}
-1650
-1651impl Encodable for ObservedAddr {
-1652    fn encode<W: BufMut>(&self, buf: &mut W) {
-1653        buf.write(self.get_type());
-1654        buf.write(self.seq_no);
-1655        match self.ip {
-1656            IpAddr::V4(ipv4_addr) => {
-1657                buf.write(ipv4_addr);
-1658            }
-1659            IpAddr::V6(ipv6_addr) => {
-1660                buf.write(ipv6_addr);
-1661            }
-1662        }
-1663        buf.write::<u16>(self.port);
-1664    }
-1665}
-1666
-1667/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
-1668
-1669#[derive(Debug, PartialEq, Eq)]
-1670pub(crate) struct PathAbandon {
-1671    pub(crate) path_id: PathId,
-1672    pub(crate) error_code: TransportErrorCode,
+1620}
+1621
+1622impl From<UnexpectedEnd> for IterErr {
+1623    fn from(_: UnexpectedEnd) -> Self {
+1624        Self::UnexpectedEnd
+1625    }
+1626}
+1627
+1628#[derive(Debug, Clone)]
+1629pub(crate) struct AckIter<'a> {
+1630    largest: u64,
+1631    data: &'a [u8],
+1632}
+1633
+1634impl<'a> AckIter<'a> {
+1635    fn new(largest: u64, data: &'a [u8]) -> Self {
+1636        Self { largest, data }
+1637    }
+1638}
+1639
+1640impl Iterator for AckIter<'_> {
+1641    type Item = RangeInclusive<u64>;
+1642    fn next(&mut self) -> Option<RangeInclusive<u64>> {
+1643        if !self.data.has_remaining() {
+1644            return None;
+1645        }
+1646        let block = self.data.get_var().unwrap();
+1647        let largest = self.largest;
+1648        if let Ok(gap) = self.data.get_var() {
+1649            self.largest -= block + gap + 2;
+1650        }
+1651        Some(largest - block..=largest)
+1652    }
+1653}
+1654
+1655#[allow(unreachable_pub)] // fuzzing only
+1656#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
+1657#[derive(Debug, Copy, Clone, derive_more::Display)]
+1658#[display("RESET_STREAM id: {id}")]
+1659pub struct ResetStream {
+1660    pub(crate) id: StreamId,
+1661    pub(crate) error_code: VarInt,
+1662    pub(crate) final_offset: VarInt,
+1663}
+1664
+1665impl ResetStream {
+1666    const fn get_type(&self) -> FrameType {
+1667        FrameType::ResetStream
+1668    }
+1669}
+1670
+1671impl FrameStruct for ResetStream {
+1672    const SIZE_BOUND: usize = 1 + 8 + 8 + 8;
 1673}
 1674
-1675impl PathAbandon {
-1676    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
-1677}
-1678
-1679impl Encodable for PathAbandon {
-1680    fn encode<W: BufMut>(&self, buf: &mut W) {
-1681        buf.write(FrameType::PathAbandon);
-1682        buf.write(self.path_id);
-1683        buf.write(self.error_code);
-1684    }
-1685}
-1686
-1687impl Decodable for PathAbandon {
-1688    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
-1689        Ok(Self {
-1690            path_id: bytes.get()?,
-1691            error_code: bytes.get()?,
-1692        })
-1693    }
-1694}
-1695
-1696#[derive(Debug, PartialEq, Eq)]
-1697pub(crate) struct PathStatusAvailable {
-1698    pub(crate) path_id: PathId,
-1699    pub(crate) status_seq_no: VarInt,
-1700}
-1701
-1702impl PathStatusAvailable {
-1703    const TYPE: FrameType = FrameType::PathStatusAvailable;
-1704    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
-1705}
-1706
-1707impl Encodable for PathStatusAvailable {
-1708    fn encode<W: BufMut>(&self, buf: &mut W) {
-1709        buf.write(Self::TYPE);
-1710        buf.write(self.path_id);
-1711        buf.write(self.status_seq_no);
-1712    }
-1713}
-1714
-1715impl Decodable for PathStatusAvailable {
-1716    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
-1717        Ok(Self {
-1718            path_id: bytes.get()?,
-1719            status_seq_no: bytes.get()?,
-1720        })
-1721    }
-1722}
-1723
-1724#[derive(Debug, PartialEq, Eq)]
-1725pub(crate) struct PathStatusBackup {
-1726    pub(crate) path_id: PathId,
-1727    pub(crate) status_seq_no: VarInt,
-1728}
-1729
-1730impl PathStatusBackup {
-1731    const TYPE: FrameType = FrameType::PathStatusBackup;
-1732}
-1733
-1734impl Encodable for PathStatusBackup {
-1735    fn encode<W: BufMut>(&self, buf: &mut W) {
-1736        buf.write(Self::TYPE);
-1737        buf.write(self.path_id);
-1738        buf.write(self.status_seq_no);
-1739    }
-1740}
-1741
-1742impl Decodable for PathStatusBackup {
-1743    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
-1744        Ok(Self {
-1745            path_id: bytes.get()?,
-1746            status_seq_no: bytes.get()?,
-1747        })
-1748    }
-1749}
-1750
-1751/* Nat traversal frames */
-1752
-1753/// Conjunction of the information contained in the add address frames
-1754/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
-1755#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
-1756// TODO(@divma): remove
-1757#[allow(dead_code)]
-1758pub(crate) struct AddAddress {
-1759    /// Monotonically increasing integer within the same connection
-1760    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
-1761    pub(crate) seq_no: VarInt,
-1762    /// Address to include in the known set
-1763    pub(crate) ip: IpAddr,
-1764    /// Port to use with this address
-1765    pub(crate) port: u16,
-1766}
-1767
-1768// TODO(@divma): remove
-1769#[allow(dead_code)]
-1770impl AddAddress {
-1771    /// Smallest number of bytes this type of frame is guaranteed to fit within.
-1772    pub(crate) const SIZE_BOUND: usize = Self {
-1773        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
-1774        port: u16::MAX,
-1775        seq_no: VarInt::MAX,
-1776    }
-1777    .size();
-1778
-1779    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
-1780        Self { ip, port, seq_no }
-1781    }
-1782
-1783    /// Get the [`FrameType`] for this frame.
-1784    pub(crate) const fn get_type(&self) -> FrameType {
-1785        if self.ip.is_ipv6() {
-1786            FrameType::AddIpv6Address
-1787        } else {
-1788            FrameType::AddIpv4Address
-1789        }
-1790    }
-1791
-1792    /// Compute the number of bytes needed to encode the frame
-1793    pub(crate) const fn size(&self) -> usize {
-1794        let type_size = self.get_type().size();
-1795        let seq_no_bytes = self.seq_no.size();
-1796        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
-1797        let port_bytes = 2;
-1798        type_size + seq_no_bytes + ip_bytes + port_bytes
-1799    }
-1800
-1801    /// Read the frame contents from the buffer
-1802    ///
-1803    /// Should only be called when the frame type has been identified as
-1804    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
-1805    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
-1806        let seq_no = bytes.get()?;
-1807        let ip = if is_ipv6 {
-1808            IpAddr::V6(bytes.get()?)
-1809        } else {
-1810            IpAddr::V4(bytes.get()?)
-1811        };
-1812        let port = bytes.get()?;
-1813        Ok(Self { seq_no, ip, port })
-1814    }
-1815
-1816    /// Give the [`SocketAddr`] encoded in the frame
-1817    pub(crate) fn socket_addr(&self) -> SocketAddr {
-1818        self.ip_port().into()
-1819    }
-1820
-1821    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
-1822        (self.ip, self.port)
-1823    }
-1824}
-1825
-1826impl Encodable for AddAddress {
-1827    fn encode<W: BufMut>(&self, buf: &mut W) {
-1828        buf.write(self.get_type());
-1829        buf.write(self.seq_no);
-1830        match self.ip {
-1831            IpAddr::V4(ipv4_addr) => {
-1832                buf.write(ipv4_addr);
-1833            }
-1834            IpAddr::V6(ipv6_addr) => {
-1835                buf.write(ipv6_addr);
-1836            }
-1837        }
-1838        buf.write::<u16>(self.port);
-1839    }
-1840}
-1841
-1842/// Conjunction of the information contained in the reach out frames
-1843/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
-1844#[derive(Debug, PartialEq, Eq, Clone)]
-1845// TODO(@divma): remove
-1846#[allow(dead_code)]
-1847pub(crate) struct ReachOut {
-1848    /// The sequence number of the NAT Traversal attempts
-1849    pub(crate) round: VarInt,
-1850    /// Address to use
-1851    pub(crate) ip: IpAddr,
-1852    /// Port to use with this address
-1853    pub(crate) port: u16,
-1854}
-1855
-1856// TODO(@divma): remove
-1857#[allow(dead_code)]
-1858impl ReachOut {
-1859    /// Smallest number of bytes this type of frame is guaranteed to fit within
-1860    pub(crate) const SIZE_BOUND: usize = Self {
-1861        round: VarInt::MAX,
-1862        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
-1863        port: u16::MAX,
+1675impl Encodable for ResetStream {
+1676    fn encode<W: BufMut>(&self, out: &mut W) {
+1677        out.write(FrameType::ResetStream); // 1 byte
+1678        out.write(self.id); // <= 8 bytes
+1679        out.write(self.error_code); // <= 8 bytes
+1680        out.write(self.final_offset); // <= 8 bytes
+1681    }
+1682}
+1683
+1684#[derive(Debug, Copy, Clone, derive_more::Display)]
+1685#[display("STOP_SENDING id: {id}")]
+1686pub(crate) struct StopSending {
+1687    pub(crate) id: StreamId,
+1688    pub(crate) error_code: VarInt,
+1689}
+1690
+1691impl FrameStruct for StopSending {
+1692    const SIZE_BOUND: usize = 1 + 8 + 8;
+1693}
+1694
+1695impl StopSending {
+1696    const fn get_type(&self) -> FrameType {
+1697        FrameType::StopSending
+1698    }
+1699}
+1700
+1701impl Encodable for StopSending {
+1702    fn encode<W: BufMut>(&self, out: &mut W) {
+1703        out.write(FrameType::StopSending); // 1 byte
+1704        out.write(self.id); // <= 8 bytes
+1705        out.write(self.error_code) // <= 8 bytes
+1706    }
+1707}
+1708
+1709#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+1710#[display("{} {} seq: {sequence} id: {id}", self.get_type(), DisplayOption::new("path_id", path_id.as_ref()))]
+1711pub(crate) struct NewConnectionId {
+1712    pub(crate) path_id: Option<PathId>,
+1713    pub(crate) sequence: u64,
+1714    pub(crate) retire_prior_to: u64,
+1715    pub(crate) id: ConnectionId,
+1716    pub(crate) reset_token: ResetToken,
+1717}
+1718
+1719impl NewConnectionId {
+1720    /// Maximum size of this frame when the frame type is [`FrameType::NewConnectionId`],
+1721    pub(crate) const SIZE_BOUND: usize = {
+1722        let type_len = FrameType::NewConnectionId.size();
+1723        let seq_max_len = 8usize;
+1724        let retire_prior_to_max_len = 8usize;
+1725        let cid_len_len = 1;
+1726        let cid_len = 160;
+1727        let reset_token_len = 16;
+1728        type_len + seq_max_len + retire_prior_to_max_len + cid_len_len + cid_len + reset_token_len
+1729    };
+1730
+1731    /// Maximum size of this frame when the frame type is [`FrameType::PathNewConnectionId`],
+1732    pub(crate) const SIZE_BOUND_MULTIPATH: usize = {
+1733        let type_len = FrameType::PathNewConnectionId.size();
+1734        let path_id_len = VarInt::from_u32(u32::MAX).size();
+1735        let seq_max_len = 8usize;
+1736        let retire_prior_to_max_len = 8usize;
+1737        let cid_len_len = 1;
+1738        let cid_len = 160;
+1739        let reset_token_len = 16;
+1740        type_len
+1741            + path_id_len
+1742            + seq_max_len
+1743            + retire_prior_to_max_len
+1744            + cid_len_len
+1745            + cid_len
+1746            + reset_token_len
+1747    };
+1748
+1749    const fn get_type(&self) -> FrameType {
+1750        if self.path_id.is_some() {
+1751            FrameType::PathNewConnectionId
+1752        } else {
+1753            FrameType::NewConnectionId
+1754        }
+1755    }
+1756
+1757    /// Returns the maximum encoded size on the wire.
+1758    ///
+1759    /// This is a rough upper estimate, does not squeeze every last byte out.
+1760    pub(crate) const fn size_bound(path_new_cid: bool, cid_len: usize) -> usize {
+1761        let upper_bound = match path_new_cid {
+1762            true => Self::SIZE_BOUND_MULTIPATH,
+1763            false => Self::SIZE_BOUND,
+1764        };
+1765        // instead of using the maximum cid len, use the provided one
+1766        upper_bound - 160 + cid_len
+1767    }
+1768
+1769    fn read<R: Buf>(bytes: &mut R, read_path: bool) -> Result<Self, IterErr> {
+1770        let path_id = if read_path { Some(bytes.get()?) } else { None };
+1771        let sequence = bytes.get_var()?;
+1772        let retire_prior_to = bytes.get_var()?;
+1773        if retire_prior_to > sequence {
+1774            return Err(IterErr::Malformed);
+1775        }
+1776        let length = bytes.get::<u8>()? as usize;
+1777        if length > MAX_CID_SIZE || length == 0 {
+1778            return Err(IterErr::Malformed);
+1779        }
+1780        if length > bytes.remaining() {
+1781            return Err(IterErr::UnexpectedEnd);
+1782        }
+1783        let mut stage = [0; MAX_CID_SIZE];
+1784        bytes.copy_to_slice(&mut stage[0..length]);
+1785        let id = ConnectionId::new(&stage[..length]);
+1786        if bytes.remaining() < 16 {
+1787            return Err(IterErr::UnexpectedEnd);
+1788        }
+1789        let mut reset_token = [0; RESET_TOKEN_SIZE];
+1790        bytes.copy_to_slice(&mut reset_token);
+1791        Ok(Self {
+1792            path_id,
+1793            sequence,
+1794            retire_prior_to,
+1795            id,
+1796            reset_token: reset_token.into(),
+1797        })
+1798    }
+1799
+1800    pub(crate) fn issued(&self) -> crate::shared::IssuedCid {
+1801        crate::shared::IssuedCid {
+1802            path_id: self.path_id.unwrap_or_default(),
+1803            sequence: self.sequence,
+1804            id: self.id,
+1805            reset_token: self.reset_token,
+1806        }
+1807    }
+1808}
+1809
+1810impl Encodable for NewConnectionId {
+1811    fn encode<W: BufMut>(&self, out: &mut W) {
+1812        out.write(self.get_type());
+1813        if let Some(id) = self.path_id {
+1814            out.write(id);
+1815        }
+1816        out.write_var(self.sequence);
+1817        out.write_var(self.retire_prior_to);
+1818        out.write(self.id.len() as u8);
+1819        out.put_slice(&self.id);
+1820        out.put_slice(&self.reset_token);
+1821    }
+1822}
+1823
+1824impl FrameStruct for NewConnectionId {
+1825    const SIZE_BOUND: usize = 1 + 8 + 8 + 1 + MAX_CID_SIZE + RESET_TOKEN_SIZE;
+1826}
+1827
+1828/// An unreliable datagram
+1829#[derive(Debug, Clone, derive_more::Display)]
+1830#[display("DATAGRAM len: {}", data.len())]
+1831pub struct Datagram {
+1832    /// Payload
+1833    pub data: Bytes,
+1834}
+1835
+1836impl FrameStruct for Datagram {
+1837    const SIZE_BOUND: usize = 1 + 8;
+1838}
+1839
+1840impl Datagram {
+1841    pub(crate) fn size(&self, length: bool) -> usize {
+1842        1 + if length {
+1843            VarInt::from_u64(self.data.len() as u64).unwrap().size()
+1844        } else {
+1845            0
+1846        } + self.data.len()
+1847    }
+1848
+1849    const fn get_type(&self) -> FrameType {
+1850        FrameType::Datagram(DatagramInfo(*DatagramInfo::VALUES.start() as u8))
+1851    }
+1852}
+1853
+1854impl Encodable for Datagram {
+1855    fn encode<B: BufMut>(&self, out: &mut B) {
+1856        // A datagram is encoded only after this is verified.
+1857        const ENCODE_LEN: bool = true;
+1858        out.write(FrameType::Datagram(DatagramInfo(
+1859            *DatagramInfo::VALUES.start() as u8 | u8::from(ENCODE_LEN),
+1860        ))); // 1 byte
+1861        // Safe to unwrap because we check length sanity before queueing datagrams
+1862        out.write(VarInt::from_u64(self.data.len() as u64).unwrap()); // <= 8 bytes
+1863        out.put_slice(&self.data);
 1864    }
-1865    .size();
+1865}
 1866
-1867    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
-1868        Self { round, ip, port }
-1869    }
-1870
-1871    /// Get the [`FrameType`] for this frame
-1872    pub(crate) const fn get_type(&self) -> FrameType {
-1873        if self.ip.is_ipv6() {
-1874            FrameType::ReachOutAtIpv6
-1875        } else {
-1876            FrameType::ReachOutAtIpv4
-1877        }
-1878    }
-1879
-1880    /// Compute the number of bytes needed to encode the frame
-1881    pub(crate) const fn size(&self) -> usize {
-1882        let type_size = self.get_type().size();
-1883        let round_bytes = self.round.size();
-1884        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
-1885        let port_bytes = 2;
-1886        type_size + round_bytes + ip_bytes + port_bytes
-1887    }
-1888
-1889    /// Read the frame contents from the buffer
-1890    ///
-1891    /// Should only be called when the frame type has been identified as
-1892    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
-1893    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
-1894        let round = bytes.get()?;
-1895        let ip = if is_ipv6 {
-1896            IpAddr::V6(bytes.get()?)
-1897        } else {
-1898            IpAddr::V4(bytes.get()?)
-1899        };
-1900        let port = bytes.get()?;
-1901        Ok(Self { round, ip, port })
-1902    }
-1903
-1904    /// Give the [`SocketAddr`] encoded in the frame
-1905    pub(crate) fn socket_addr(&self) -> SocketAddr {
-1906        (self.ip, self.port).into()
-1907    }
-1908}
-1909
-1910impl Encodable for ReachOut {
-1911    fn encode<W: BufMut>(&self, buf: &mut W) {
-1912        buf.write(self.get_type());
-1913        buf.write(self.round);
-1914        match self.ip {
-1915            IpAddr::V4(ipv4_addr) => {
-1916                buf.write(ipv4_addr);
-1917            }
-1918            IpAddr::V6(ipv6_addr) => {
-1919                buf.write(ipv6_addr);
-1920            }
-1921        }
-1922        buf.write::<u16>(self.port);
+1867#[derive(Debug, Copy, Clone, PartialEq, Eq, derive_more::Display)]
+1868#[display("ACK_FREQUENCY max_ack_delay: {}µs", request_max_ack_delay.0)]
+1869pub(crate) struct AckFrequency {
+1870    pub(crate) sequence: VarInt,
+1871    pub(crate) ack_eliciting_threshold: VarInt,
+1872    pub(crate) request_max_ack_delay: VarInt,
+1873    pub(crate) reordering_threshold: VarInt,
+1874}
+1875
+1876impl AckFrequency {
+1877    const fn get_type(&self) -> FrameType {
+1878        FrameType::AckFrequency
+1879    }
+1880}
+1881
+1882impl Encodable for AckFrequency {
+1883    fn encode<W: BufMut>(&self, buf: &mut W) {
+1884        buf.write(FrameType::AckFrequency);
+1885        buf.write(self.sequence);
+1886        buf.write(self.ack_eliciting_threshold);
+1887        buf.write(self.request_max_ack_delay);
+1888        buf.write(self.reordering_threshold);
+1889    }
+1890}
+1891
+1892/* Address Discovery https://datatracker.ietf.org/doc/draft-seemann-quic-address-discovery/ */
+1893
+1894/// Conjunction of the information contained in the address discovery frames
+1895/// ([`FrameType::ObservedIpv4Addr`], [`FrameType::ObservedIpv6Addr`]).
+1896#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+1897#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+1898pub(crate) struct ObservedAddr {
+1899    /// Monotonically increasing integer within the same connection.
+1900    pub(crate) seq_no: VarInt,
+1901    /// Reported observed address.
+1902    pub(crate) ip: IpAddr,
+1903    /// Reported observed port.
+1904    pub(crate) port: u16,
+1905}
+1906
+1907impl ObservedAddr {
+1908    pub(crate) fn new<N: Into<VarInt>>(remote: std::net::SocketAddr, seq_no: N) -> Self {
+1909        Self {
+1910            ip: remote.ip(),
+1911            port: remote.port(),
+1912            seq_no: seq_no.into(),
+1913        }
+1914    }
+1915
+1916    /// Get the [`FrameType`] for this frame.
+1917    const fn get_type(&self) -> FrameType {
+1918        if self.ip.is_ipv6() {
+1919            FrameType::ObservedIpv6Addr
+1920        } else {
+1921            FrameType::ObservedIpv4Addr
+1922        }
 1923    }
-1924}
-1925
-1926/// Frame signaling an address is no longer being advertised
-1927#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
-1928// TODO(@divma): remove
-1929#[allow(dead_code)]
-1930pub(crate) struct RemoveAddress {
-1931    /// The sequence number of the address advertisement to be removed
-1932    pub(crate) seq_no: VarInt,
-1933}
-1934
-1935// TODO(@divma): remove
-1936#[allow(dead_code)]
-1937impl RemoveAddress {
-1938    /// [`FrameType`] of this frame
-1939    pub(crate) const TYPE: FrameType = FrameType::RemoveAddress;
-1940
-1941    /// Smallest number of bytes this type of frame is guaranteed to fit within
-1942    pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size();
-1943
-1944    pub(crate) const fn new(seq_no: VarInt) -> Self {
-1945        Self { seq_no }
-1946    }
-1947
-1948    /// Compute the number of bytes needed to encode the frame
-1949    pub(crate) const fn size(&self) -> usize {
-1950        let type_size = Self::TYPE.size();
-1951        let seq_no_bytes = self.seq_no.size();
-1952        type_size + seq_no_bytes
-1953    }
+1924
+1925    /// Compute the number of bytes needed to encode the frame.
+1926    pub(crate) fn size(&self) -> usize {
+1927        let type_size = self.get_type().size();
+1928        let req_id_bytes = self.seq_no.size();
+1929        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+1930        let port_bytes = 2;
+1931        type_size + req_id_bytes + ip_bytes + port_bytes
+1932    }
+1933
+1934    /// Reads the frame contents from the buffer.
+1935    ///
+1936    /// Should only be called when the frame type has been identified as
+1937    /// [`FrameType::ObservedIpv4Addr`] or [`FrameType::ObservedIpv6Addr`].
+1938    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+1939        let seq_no = bytes.get()?;
+1940        let ip = if is_ipv6 {
+1941            IpAddr::V6(bytes.get()?)
+1942        } else {
+1943            IpAddr::V4(bytes.get()?)
+1944        };
+1945        let port = bytes.get()?;
+1946        Ok(Self { seq_no, ip, port })
+1947    }
+1948
+1949    /// Gives the [`SocketAddr`] reported in the frame.
+1950    pub(crate) fn socket_addr(&self) -> SocketAddr {
+1951        (self.ip, self.port).into()
+1952    }
+1953}
 1954
-1955    /// Read the frame contents from the buffer
-1956    ///
-1957    /// Should only be called when the frame type has been identified as
-1958    /// [`FrameType::RemoveAddress`].
-1959    pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
-1960        Ok(Self {
-1961            seq_no: bytes.get()?,
-1962        })
-1963    }
-1964}
-1965
-1966impl Encodable for RemoveAddress {
-1967    fn encode<W: BufMut>(&self, buf: &mut W) {
-1968        buf.write(Self::TYPE);
-1969        buf.write(self.seq_no);
-1970    }
-1971}
+1955impl Encodable for ObservedAddr {
+1956    fn encode<W: BufMut>(&self, buf: &mut W) {
+1957        buf.write(self.get_type());
+1958        buf.write(self.seq_no);
+1959        match self.ip {
+1960            IpAddr::V4(ipv4_addr) => {
+1961                buf.write(ipv4_addr);
+1962            }
+1963            IpAddr::V6(ipv6_addr) => {
+1964                buf.write(ipv6_addr);
+1965            }
+1966        }
+1967        buf.write::<u16>(self.port);
+1968    }
+1969}
+1970
+1971/* Multipath <https://datatracker.ietf.org/doc/draft-ietf-quic-multipath/> */
 1972
-1973#[cfg(test)]
-1974mod test {
-1975    use super::*;
-1976    use crate::coding::Encodable;
-1977    use assert_matches::assert_matches;
-1978
-1979    #[test]
-1980    fn frame_type() {
-1981        assert_eq!(
-1982            FrameType::try_from(FrameType::Padding.to_u64()),
-1983            Ok(FrameType::Padding),
-1984        );
-1985
-1986        assert_eq!(
-1987            FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()),
-1988            Ok(FrameType::Datagram(DatagramInfo(0x30))),
-1989        );
-1990
-1991        assert_eq!(
-1992            FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()),
-1993            Ok(FrameType::Stream(StreamInfo(0x08))),
-1994        );
-1995    }
-1996
-1997    #[track_caller]
-1998    fn frames(buf: Vec<u8>) -> Vec<Frame> {
-1999        Iter::new(Bytes::from(buf))
-2000            .unwrap()
-2001            .collect::<Result<Vec<_>, _>>()
-2002            .unwrap()
-2003    }
+1973#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+1974#[display("PATH_ABANDON path_id: {path_id}")]
+1975pub(crate) struct PathAbandon {
+1976    pub(crate) path_id: PathId,
+1977    pub(crate) error_code: TransportErrorCode,
+1978}
+1979
+1980impl PathAbandon {
+1981    pub(crate) const SIZE_BOUND: usize = FrameType::PathAbandon.size() + 8 + 8;
+1982
+1983    const fn get_type(&self) -> FrameType {
+1984        FrameType::PathAbandon
+1985    }
+1986}
+1987
+1988impl Encodable for PathAbandon {
+1989    fn encode<W: BufMut>(&self, buf: &mut W) {
+1990        buf.write(FrameType::PathAbandon);
+1991        buf.write(self.path_id);
+1992        buf.write(self.error_code);
+1993    }
+1994}
+1995
+1996impl Decodable for PathAbandon {
+1997    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+1998        Ok(Self {
+1999            path_id: bytes.get()?,
+2000            error_code: bytes.get()?,
+2001        })
+2002    }
+2003}
 2004
-2005    #[test]
-2006    fn ack_coding() {
-2007        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
-2008        let mut ranges = ArrayRangeSet::new();
-2009        for &packet in PACKETS {
-2010            ranges.insert(packet..packet + 1);
-2011        }
-2012        let mut buf = Vec::new();
-2013        const ECN: EcnCounts = EcnCounts {
-2014            ect0: 42,
-2015            ect1: 24,
-2016            ce: 12,
-2017        };
-2018        Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf);
-2019        let frames = frames(buf);
-2020        assert_eq!(frames.len(), 1);
-2021        match frames[0] {
-2022            Frame::Ack(ref ack) => {
-2023                let mut packets = ack.iter().flatten().collect::<Vec<_>>();
-2024                packets.sort_unstable();
-2025                assert_eq!(&packets[..], PACKETS);
-2026                assert_eq!(ack.ecn, Some(ECN));
-2027            }
-2028            ref x => panic!("incorrect frame {x:?}"),
-2029        }
-2030    }
-2031
-2032    #[test]
-2033    #[allow(clippy::range_plus_one)]
-2034    fn path_ack_coding() {
-2035        const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14];
-2036        let mut ranges = ArrayRangeSet::new();
-2037        for &packet in PACKETS {
-2038            ranges.insert(packet..packet + 1);
-2039        }
-2040        let mut buf = Vec::new();
-2041        const ECN: EcnCounts = EcnCounts {
-2042            ect0: 42,
-2043            ect1: 24,
-2044            ce: 12,
-2045        };
-2046        const PATH_ID: PathId = PathId::MAX;
-2047        PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf);
-2048        let frames = frames(buf);
-2049        assert_eq!(frames.len(), 1);
-2050        match frames[0] {
-2051            Frame::PathAck(ref ack) => {
-2052                assert_eq!(ack.path_id, PATH_ID);
-2053                let mut packets = ack.into_iter().flatten().collect::<Vec<_>>();
-2054                packets.sort_unstable();
-2055                assert_eq!(&packets[..], PACKETS);
-2056                assert_eq!(ack.ecn, Some(ECN));
-2057            }
-2058            ref x => panic!("incorrect frame {x:?}"),
-2059        }
-2060    }
-2061
-2062    #[test]
-2063    fn ack_frequency_coding() {
-2064        let mut buf = Vec::new();
-2065        let original = AckFrequency {
-2066            sequence: VarInt(42),
-2067            ack_eliciting_threshold: VarInt(20),
-2068            request_max_ack_delay: VarInt(50_000),
-2069            reordering_threshold: VarInt(1),
-2070        };
-2071        original.encode(&mut buf);
-2072        let frames = frames(buf);
-2073        assert_eq!(frames.len(), 1);
-2074        match &frames[0] {
-2075            Frame::AckFrequency(decoded) => assert_eq!(decoded, &original),
-2076            x => panic!("incorrect frame {x:?}"),
-2077        }
-2078    }
-2079
-2080    #[test]
-2081    fn immediate_ack_coding() {
-2082        let mut buf = Vec::new();
-2083        FrameType::ImmediateAck.encode(&mut buf);
-2084        let frames = frames(buf);
-2085        assert_eq!(frames.len(), 1);
-2086        assert_matches!(&frames[0], Frame::ImmediateAck);
-2087    }
-2088
-2089    /// Test that encoding and decoding [`ObservedAddr`] produces the same result.
-2090    #[test]
-2091    fn test_observed_addr_roundrip() {
-2092        let observed_addr = ObservedAddr {
-2093            seq_no: VarInt(42),
-2094            ip: std::net::Ipv4Addr::LOCALHOST.into(),
-2095            port: 4242,
-2096        };
-2097        let mut buf = Vec::with_capacity(observed_addr.size());
-2098        observed_addr.encode(&mut buf);
-2099
-2100        assert_eq!(
-2101            observed_addr.size(),
-2102            buf.len(),
-2103            "expected written bytes and actual size differ"
-2104        );
-2105
-2106        let mut decoded = frames(buf);
-2107        assert_eq!(decoded.len(), 1);
-2108        match decoded.pop().expect("non empty") {
-2109            Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr),
-2110            x => panic!("incorrect frame {x:?}"),
-2111        }
-2112    }
-2113
-2114    #[test]
-2115    fn test_path_abandon_roundtrip() {
-2116        let abandon = PathAbandon {
-2117            path_id: PathId(42),
-2118            error_code: TransportErrorCode::NO_ERROR,
-2119        };
-2120        let mut buf = Vec::new();
-2121        abandon.encode(&mut buf);
-2122
-2123        let mut decoded = frames(buf);
-2124        assert_eq!(decoded.len(), 1);
-2125        match decoded.pop().expect("non empty") {
-2126            Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon),
-2127            x => panic!("incorrect frame {x:?}"),
-2128        }
-2129    }
-2130
-2131    #[test]
-2132    fn test_path_status_available_roundtrip() {
-2133        let path_status_available = PathStatusAvailable {
-2134            path_id: PathId(42),
-2135            status_seq_no: VarInt(73),
-2136        };
-2137        let mut buf = Vec::new();
-2138        path_status_available.encode(&mut buf);
-2139
-2140        let mut decoded = frames(buf);
-2141        assert_eq!(decoded.len(), 1);
-2142        match decoded.pop().expect("non empty") {
-2143            Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available),
-2144            x => panic!("incorrect frame {x:?}"),
-2145        }
-2146    }
-2147
-2148    #[test]
-2149    fn test_path_status_backup_roundtrip() {
-2150        let path_status_backup = PathStatusBackup {
-2151            path_id: PathId(42),
-2152            status_seq_no: VarInt(73),
-2153        };
-2154        let mut buf = Vec::new();
-2155        path_status_backup.encode(&mut buf);
-2156
-2157        let mut decoded = frames(buf);
-2158        assert_eq!(decoded.len(), 1);
-2159        match decoded.pop().expect("non empty") {
-2160            Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup),
-2161            x => panic!("incorrect frame {x:?}"),
-2162        }
-2163    }
-2164
-2165    #[test]
-2166    fn test_path_new_connection_id_roundtrip() {
-2167        let cid = NewConnectionId {
-2168            path_id: Some(PathId(22)),
-2169            sequence: 31,
-2170            retire_prior_to: 13,
-2171            id: ConnectionId::new(&[0xAB; 8]),
-2172            reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]),
-2173        };
-2174        let mut buf = Vec::new();
-2175        cid.encode(&mut buf);
-2176
-2177        let mut decoded = frames(buf);
-2178        assert_eq!(decoded.len(), 1);
-2179        match decoded.pop().expect("non empty") {
-2180            Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid),
-2181            x => panic!("incorrect frame {x:?}"),
-2182        }
-2183    }
-2184
-2185    #[test]
-2186    fn test_path_retire_connection_id_roundtrip() {
-2187        let retire_cid = RetireConnectionId {
-2188            path_id: Some(PathId(22)),
-2189            sequence: 31,
-2190        };
-2191        let mut buf = Vec::new();
-2192        retire_cid.encode(&mut buf);
-2193
-2194        let mut decoded = frames(buf);
-2195        assert_eq!(decoded.len(), 1);
-2196        match decoded.pop().expect("non empty") {
-2197            Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid),
-2198            x => panic!("incorrect frame {x:?}"),
-2199        }
-2200    }
-2201
-2202    #[test]
-2203    fn test_paths_blocked_path_cids_blocked_roundtrip() {
-2204        let mut buf = Vec::new();
+2005#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2006#[display("PATH_STATUS_AVAILABLE path_id: {path_id} seq_no: {status_seq_no}")]
+2007pub(crate) struct PathStatusAvailable {
+2008    pub(crate) path_id: PathId,
+2009    pub(crate) status_seq_no: VarInt,
+2010}
+2011
+2012impl PathStatusAvailable {
+2013    const TYPE: FrameType = FrameType::PathStatusAvailable;
+2014    pub(crate) const SIZE_BOUND: usize = FrameType::PathStatusAvailable.size() + 8 + 8;
+2015
+2016    const fn get_type(&self) -> FrameType {
+2017        FrameType::PathStatusAvailable
+2018    }
+2019}
+2020
+2021impl Encodable for PathStatusAvailable {
+2022    fn encode<W: BufMut>(&self, buf: &mut W) {
+2023        buf.write(Self::TYPE);
+2024        buf.write(self.path_id);
+2025        buf.write(self.status_seq_no);
+2026    }
+2027}
+2028
+2029impl Decodable for PathStatusAvailable {
+2030    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2031        Ok(Self {
+2032            path_id: bytes.get()?,
+2033            status_seq_no: bytes.get()?,
+2034        })
+2035    }
+2036}
+2037
+2038#[derive(Debug, PartialEq, Eq, derive_more::Display)]
+2039#[display("PATH_STATUS_BACKUP path_id: {path_id} seq_no: {status_seq_no}")]
+2040pub(crate) struct PathStatusBackup {
+2041    pub(crate) path_id: PathId,
+2042    pub(crate) status_seq_no: VarInt,
+2043}
+2044
+2045impl PathStatusBackup {
+2046    const TYPE: FrameType = FrameType::PathStatusBackup;
+2047
+2048    const fn get_type(&self) -> FrameType {
+2049        FrameType::PathStatusBackup
+2050    }
+2051}
+2052
+2053impl Encodable for PathStatusBackup {
+2054    fn encode<W: BufMut>(&self, buf: &mut W) {
+2055        buf.write(Self::TYPE);
+2056        buf.write(self.path_id);
+2057        buf.write(self.status_seq_no);
+2058    }
+2059}
+2060
+2061impl Decodable for PathStatusBackup {
+2062    fn decode<R: Buf>(bytes: &mut R) -> coding::Result<Self> {
+2063        Ok(Self {
+2064            path_id: bytes.get()?,
+2065            status_seq_no: bytes.get()?,
+2066        })
+2067    }
+2068}
+2069
+2070/* Nat traversal frames */
+2071
+2072/// Conjunction of the information contained in the add address frames
+2073/// ([`FrameType::AddIpv4Address`], [`FrameType::AddIpv6Address`]).
+2074#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2075#[display("{} seq_no: {seq_no} addr: {}", self.get_type(), self.socket_addr())]
+2076pub(crate) struct AddAddress {
+2077    /// Monotonically increasing integer within the same connection
+2078    // TODO(@divma): both assumed, the draft has no mention of this but it's standard
+2079    pub(crate) seq_no: VarInt,
+2080    /// Address to include in the known set
+2081    pub(crate) ip: IpAddr,
+2082    /// Port to use with this address
+2083    pub(crate) port: u16,
+2084}
+2085
+2086// TODO(@divma): remove
+2087#[allow(dead_code)]
+2088impl AddAddress {
+2089    /// Smallest number of bytes this type of frame is guaranteed to fit within.
+2090    pub(crate) const SIZE_BOUND: usize = Self {
+2091        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2092        port: u16::MAX,
+2093        seq_no: VarInt::MAX,
+2094    }
+2095    .size();
+2096
+2097    pub(crate) const fn new((ip, port): (IpAddr, u16), seq_no: VarInt) -> Self {
+2098        Self { ip, port, seq_no }
+2099    }
+2100
+2101    /// Get the [`FrameType`] for this frame.
+2102    const fn get_type(&self) -> FrameType {
+2103        if self.ip.is_ipv6() {
+2104            FrameType::AddIpv6Address
+2105        } else {
+2106            FrameType::AddIpv4Address
+2107        }
+2108    }
+2109
+2110    /// Compute the number of bytes needed to encode the frame
+2111    pub(crate) const fn size(&self) -> usize {
+2112        let type_size = self.get_type().size();
+2113        let seq_no_bytes = self.seq_no.size();
+2114        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2115        let port_bytes = 2;
+2116        type_size + seq_no_bytes + ip_bytes + port_bytes
+2117    }
+2118
+2119    /// Read the frame contents from the buffer
+2120    ///
+2121    /// Should only be called when the frame type has been identified as
+2122    /// [`FrameType::AddIpv4Address`] or [`FrameType::AddIpv6Address`].
+2123    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2124        let seq_no = bytes.get()?;
+2125        let ip = if is_ipv6 {
+2126            IpAddr::V6(bytes.get()?)
+2127        } else {
+2128            IpAddr::V4(bytes.get()?)
+2129        };
+2130        let port = bytes.get()?;
+2131        Ok(Self { seq_no, ip, port })
+2132    }
+2133
+2134    /// Give the [`SocketAddr`] encoded in the frame
+2135    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2136        self.ip_port().into()
+2137    }
+2138
+2139    pub(crate) fn ip_port(&self) -> (IpAddr, u16) {
+2140        (self.ip, self.port)
+2141    }
+2142}
+2143
+2144impl Encodable for AddAddress {
+2145    fn encode<W: BufMut>(&self, buf: &mut W) {
+2146        buf.write(self.get_type());
+2147        buf.write(self.seq_no);
+2148        match self.ip {
+2149            IpAddr::V4(ipv4_addr) => {
+2150                buf.write(ipv4_addr);
+2151            }
+2152            IpAddr::V6(ipv6_addr) => {
+2153                buf.write(ipv6_addr);
+2154            }
+2155        }
+2156        buf.write::<u16>(self.port);
+2157    }
+2158}
+2159
+2160/// Conjunction of the information contained in the reach out frames
+2161/// ([`FrameType::ReachOutAtIpv4`], [`FrameType::ReachOutAtIpv6`])
+2162#[derive(Debug, PartialEq, Eq, Clone, derive_more::Display)]
+2163#[display("REACH_OUT round: {round} local_addr: {}", self.socket_addr())]
+2164pub(crate) struct ReachOut {
+2165    /// The sequence number of the NAT Traversal attempts
+2166    pub(crate) round: VarInt,
+2167    /// Address to use
+2168    pub(crate) ip: IpAddr,
+2169    /// Port to use with this address
+2170    pub(crate) port: u16,
+2171}
+2172
+2173// TODO(@divma): remove
+2174#[allow(dead_code)]
+2175impl ReachOut {
+2176    /// Smallest number of bytes this type of frame is guaranteed to fit within
+2177    pub(crate) const SIZE_BOUND: usize = Self {
+2178        round: VarInt::MAX,
+2179        ip: IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
+2180        port: u16::MAX,
+2181    }
+2182    .size();
+2183
+2184    pub(crate) const fn new(round: VarInt, (ip, port): (IpAddr, u16)) -> Self {
+2185        Self { round, ip, port }
+2186    }
+2187
+2188    /// Get the [`FrameType`] for this frame
+2189    pub(crate) const fn get_type(&self) -> FrameType {
+2190        if self.ip.is_ipv6() {
+2191            FrameType::ReachOutAtIpv6
+2192        } else {
+2193            FrameType::ReachOutAtIpv4
+2194        }
+2195    }
+2196
+2197    /// Compute the number of bytes needed to encode the frame
+2198    pub(crate) const fn size(&self) -> usize {
+2199        let type_size = self.get_type().size();
+2200        let round_bytes = self.round.size();
+2201        let ip_bytes = if self.ip.is_ipv6() { 16 } else { 4 };
+2202        let port_bytes = 2;
+2203        type_size + round_bytes + ip_bytes + port_bytes
+2204    }
 2205
-2206        let frame0 = PathsBlocked(PathId(22));
-2207        frame0.encode(&mut buf);
-2208        let frame1 = PathCidsBlocked {
-2209            path_id: PathId(23),
-2210            next_seq: VarInt(32),
-2211        };
-2212        frame1.encode(&mut buf);
-2213
-2214        let mut decoded = frames(buf);
-2215        assert_eq!(decoded.len(), 2);
-2216        match decoded.pop().expect("non empty") {
-2217            Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1),
-2218            x => panic!("incorrect frame {x:?}"),
-2219        }
-2220        match decoded.pop().expect("non empty") {
-2221            Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0),
-2222            x => panic!("incorrect frame {x:?}"),
-2223        }
+2206    /// Read the frame contents from the buffer
+2207    ///
+2208    /// Should only be called when the frame type has been identified as
+2209    /// [`FrameType::ReachOutAtIpv4`] or [`FrameType::ReachOutAtIpv6`].
+2210    pub(crate) fn read<R: Buf>(bytes: &mut R, is_ipv6: bool) -> coding::Result<Self> {
+2211        let round = bytes.get()?;
+2212        let ip = if is_ipv6 {
+2213            IpAddr::V6(bytes.get()?)
+2214        } else {
+2215            IpAddr::V4(bytes.get()?)
+2216        };
+2217        let port = bytes.get()?;
+2218        Ok(Self { round, ip, port })
+2219    }
+2220
+2221    /// Give the [`SocketAddr`] encoded in the frame
+2222    pub(crate) fn socket_addr(&self) -> SocketAddr {
+2223        (self.ip, self.port).into()
 2224    }
-2225
-2226    /// Test that encoding and decoding [`AddAddress`] produces the same result
-2227    #[test]
-2228    fn test_add_address_roundrip() {
-2229        let add_address = AddAddress {
-2230            seq_no: VarInt(42),
-2231            ip: std::net::Ipv4Addr::LOCALHOST.into(),
-2232            port: 4242,
-2233        };
-2234        let mut buf = Vec::with_capacity(add_address.size());
-2235        add_address.encode(&mut buf);
-2236
-2237        assert_eq!(
-2238            add_address.size(),
-2239            buf.len(),
-2240            "expected written bytes and actual size differ"
-2241        );
+2225}
+2226
+2227impl Encodable for ReachOut {
+2228    fn encode<W: BufMut>(&self, buf: &mut W) {
+2229        buf.write(self.get_type());
+2230        buf.write(self.round);
+2231        match self.ip {
+2232            IpAddr::V4(ipv4_addr) => {
+2233                buf.write(ipv4_addr);
+2234            }
+2235            IpAddr::V6(ipv6_addr) => {
+2236                buf.write(ipv6_addr);
+2237            }
+2238        }
+2239        buf.write::<u16>(self.port);
+2240    }
+2241}
 2242
-2243        let mut decoded = frames(buf);
-2244        assert_eq!(decoded.len(), 1);
-2245        match decoded.pop().expect("non empty") {
-2246            Frame::AddAddress(decoded) => assert_eq!(decoded, add_address),
-2247            x => panic!("incorrect frame {x:?}"),
-2248        }
-2249    }
+2243/// Frame signaling an address is no longer being advertised
+2244#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord, derive_more::Display)]
+2245#[display("REMOVE_ADDRESS seq_no: {seq_no}")]
+2246pub(crate) struct RemoveAddress {
+2247    /// The sequence number of the address advertisement to be removed
+2248    pub(crate) seq_no: VarInt,
+2249}
 2250
-2251    /// Test that encoding and decoding [`AddAddress`] produces the same result
-2252    #[test]
-2253    fn test_reach_out_roundrip() {
-2254        let reach_out = ReachOut {
-2255            round: VarInt(42),
-2256            ip: std::net::Ipv6Addr::LOCALHOST.into(),
-2257            port: 4242,
-2258        };
-2259        let mut buf = Vec::with_capacity(reach_out.size());
-2260        reach_out.encode(&mut buf);
-2261
-2262        assert_eq!(
-2263            reach_out.size(),
-2264            buf.len(),
-2265            "expected written bytes and actual size differ"
-2266        );
-2267
-2268        let mut decoded = frames(buf);
-2269        assert_eq!(decoded.len(), 1);
-2270        match decoded.pop().expect("non empty") {
-2271            Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out),
-2272            x => panic!("incorrect frame {x:?}"),
-2273        }
-2274    }
-2275
-2276    /// Test that encoding and decoding [`RemoveAddress`] produces the same result
-2277    #[test]
-2278    fn test_remove_address_roundrip() {
-2279        let remove_addr = RemoveAddress::new(VarInt(10));
-2280        let mut buf = Vec::with_capacity(remove_addr.size());
-2281        remove_addr.encode(&mut buf);
-2282
-2283        assert_eq!(
-2284            remove_addr.size(),
-2285            buf.len(),
-2286            "expected written bytes and actual size differ"
-2287        );
-2288
-2289        let mut decoded = frames(buf);
-2290        assert_eq!(decoded.len(), 1);
-2291        match decoded.pop().expect("non empty") {
-2292            Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr),
-2293            x => panic!("incorrect frame {x:?}"),
-2294        }
-2295    }
-2296}
\ No newline at end of file +2251// TODO(@divma): remove +2252#[allow(dead_code)] +2253impl RemoveAddress { +2254 /// [`FrameType`] of this frame +2255 pub(crate) const TYPE: FrameType = FrameType::RemoveAddress; +2256 +2257 /// Smallest number of bytes this type of frame is guaranteed to fit within +2258 pub(crate) const SIZE_BOUND: usize = Self::new(VarInt::MAX).size(); +2259 +2260 pub(crate) const fn new(seq_no: VarInt) -> Self { +2261 Self { seq_no } +2262 } +2263 +2264 /// Compute the number of bytes needed to encode the frame +2265 pub(crate) const fn size(&self) -> usize { +2266 let type_size = Self::TYPE.size(); +2267 let seq_no_bytes = self.seq_no.size(); +2268 type_size + seq_no_bytes +2269 } +2270 +2271 /// Read the frame contents from the buffer +2272 /// +2273 /// Should only be called when the frame type has been identified as +2274 /// [`FrameType::RemoveAddress`]. +2275 pub(crate) fn read<R: Buf>(bytes: &mut R) -> coding::Result<Self> { +2276 Ok(Self { +2277 seq_no: bytes.get()?, +2278 }) +2279 } +2280 +2281 const fn get_type(&self) -> FrameType { +2282 FrameType::RemoveAddress +2283 } +2284} +2285 +2286impl Encodable for RemoveAddress { +2287 fn encode<W: BufMut>(&self, buf: &mut W) { +2288 buf.write(Self::TYPE); +2289 buf.write(self.seq_no); +2290 } +2291} +2292 +2293/// Helper struct for display implementations. +2294// NOTE: Due to lifetimes in fmt::Arguments it's not possible to make this a simple function that +2295// avoids allocations. +2296struct DisplayOption<T: Display> { +2297 field_name: &'static str, +2298 op: Option<T>, +2299} +2300 +2301impl<T: Display> DisplayOption<T> { +2302 fn new(field_name: &'static str, op: Option<T>) -> Self { +2303 Self { field_name, op } +2304 } +2305} +2306 +2307impl<T: Display> Display for DisplayOption<T> { +2308 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +2309 if let Some(x) = self.op.as_ref() { +2310 write!(f, "{}: {x}", self.field_name) +2311 } else { +2312 fmt::Result::Ok(()) +2313 } +2314 } +2315} +2316 +2317#[cfg(test)] +2318mod test { +2319 use super::*; +2320 use crate::coding::Encodable; +2321 use assert_matches::assert_matches; +2322 +2323 #[test] +2324 fn frame_type() { +2325 assert_eq!( +2326 FrameType::try_from(FrameType::Padding.to_u64()), +2327 Ok(FrameType::Padding), +2328 ); +2329 +2330 assert_eq!( +2331 FrameType::try_from(FrameType::Datagram(DatagramInfo(0x30)).to_u64()), +2332 Ok(FrameType::Datagram(DatagramInfo(0x30))), +2333 ); +2334 +2335 assert_eq!( +2336 FrameType::try_from(FrameType::Stream(StreamInfo(0x08)).to_u64()), +2337 Ok(FrameType::Stream(StreamInfo(0x08))), +2338 ); +2339 } +2340 +2341 #[track_caller] +2342 fn frames(buf: Vec<u8>) -> Vec<Frame> { +2343 Iter::new(Bytes::from(buf)) +2344 .unwrap() +2345 .collect::<Result<Vec<_>, _>>() +2346 .unwrap() +2347 } +2348 +2349 #[test] +2350 fn ack_coding() { +2351 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; +2352 let mut ranges = ArrayRangeSet::new(); +2353 for &packet in PACKETS { +2354 ranges.insert(packet..packet + 1); +2355 } +2356 let mut buf = Vec::new(); +2357 const ECN: EcnCounts = EcnCounts { +2358 ect0: 42, +2359 ect1: 24, +2360 ce: 12, +2361 }; +2362 Ack::encoder(42, &ranges, Some(&ECN)).encode(&mut buf); +2363 let frames = frames(buf); +2364 assert_eq!(frames.len(), 1); +2365 match frames[0] { +2366 Frame::Ack(ref ack) => { +2367 let mut packets = ack.iter().flatten().collect::<Vec<_>>(); +2368 packets.sort_unstable(); +2369 assert_eq!(&packets[..], PACKETS); +2370 assert_eq!(ack.ecn, Some(ECN)); +2371 } +2372 ref x => panic!("incorrect frame {x:?}"), +2373 } +2374 } +2375 +2376 #[test] +2377 #[allow(clippy::range_plus_one)] +2378 fn path_ack_coding() { +2379 const PACKETS: &[u64] = &[1, 2, 3, 5, 10, 11, 14]; +2380 let mut ranges = ArrayRangeSet::new(); +2381 for &packet in PACKETS { +2382 ranges.insert(packet..packet + 1); +2383 } +2384 let mut buf = Vec::new(); +2385 const ECN: EcnCounts = EcnCounts { +2386 ect0: 42, +2387 ect1: 24, +2388 ce: 12, +2389 }; +2390 const PATH_ID: PathId = PathId::MAX; +2391 PathAck::encoder(PATH_ID, 42, &ranges, Some(&ECN)).encode(&mut buf); +2392 let frames = frames(buf); +2393 assert_eq!(frames.len(), 1); +2394 match frames[0] { +2395 Frame::PathAck(ref ack) => { +2396 assert_eq!(ack.path_id, PATH_ID); +2397 let mut packets = ack.into_iter().flatten().collect::<Vec<_>>(); +2398 packets.sort_unstable(); +2399 assert_eq!(&packets[..], PACKETS); +2400 assert_eq!(ack.ecn, Some(ECN)); +2401 } +2402 ref x => panic!("incorrect frame {x:?}"), +2403 } +2404 } +2405 +2406 #[test] +2407 fn ack_frequency_coding() { +2408 let mut buf = Vec::new(); +2409 let original = AckFrequency { +2410 sequence: VarInt(42), +2411 ack_eliciting_threshold: VarInt(20), +2412 request_max_ack_delay: VarInt(50_000), +2413 reordering_threshold: VarInt(1), +2414 }; +2415 original.encode(&mut buf); +2416 let frames = frames(buf); +2417 assert_eq!(frames.len(), 1); +2418 match &frames[0] { +2419 Frame::AckFrequency(decoded) => assert_eq!(decoded, &original), +2420 x => panic!("incorrect frame {x:?}"), +2421 } +2422 } +2423 +2424 #[test] +2425 fn immediate_ack_coding() { +2426 let mut buf = Vec::new(); +2427 FrameType::ImmediateAck.encode(&mut buf); +2428 let frames = frames(buf); +2429 assert_eq!(frames.len(), 1); +2430 assert_matches!(&frames[0], Frame::ImmediateAck); +2431 } +2432 +2433 /// Test that encoding and decoding [`ObservedAddr`] produces the same result. +2434 #[test] +2435 fn test_observed_addr_roundrip() { +2436 let observed_addr = ObservedAddr { +2437 seq_no: VarInt(42), +2438 ip: std::net::Ipv4Addr::LOCALHOST.into(), +2439 port: 4242, +2440 }; +2441 let mut buf = Vec::with_capacity(observed_addr.size()); +2442 observed_addr.encode(&mut buf); +2443 +2444 assert_eq!( +2445 observed_addr.size(), +2446 buf.len(), +2447 "expected written bytes and actual size differ" +2448 ); +2449 +2450 let mut decoded = frames(buf); +2451 assert_eq!(decoded.len(), 1); +2452 match decoded.pop().expect("non empty") { +2453 Frame::ObservedAddr(decoded) => assert_eq!(decoded, observed_addr), +2454 x => panic!("incorrect frame {x:?}"), +2455 } +2456 } +2457 +2458 #[test] +2459 fn test_path_abandon_roundtrip() { +2460 let abandon = PathAbandon { +2461 path_id: PathId(42), +2462 error_code: TransportErrorCode::NO_ERROR, +2463 }; +2464 let mut buf = Vec::new(); +2465 abandon.encode(&mut buf); +2466 +2467 let mut decoded = frames(buf); +2468 assert_eq!(decoded.len(), 1); +2469 match decoded.pop().expect("non empty") { +2470 Frame::PathAbandon(decoded) => assert_eq!(decoded, abandon), +2471 x => panic!("incorrect frame {x:?}"), +2472 } +2473 } +2474 +2475 #[test] +2476 fn test_path_status_available_roundtrip() { +2477 let path_status_available = PathStatusAvailable { +2478 path_id: PathId(42), +2479 status_seq_no: VarInt(73), +2480 }; +2481 let mut buf = Vec::new(); +2482 path_status_available.encode(&mut buf); +2483 +2484 let mut decoded = frames(buf); +2485 assert_eq!(decoded.len(), 1); +2486 match decoded.pop().expect("non empty") { +2487 Frame::PathStatusAvailable(decoded) => assert_eq!(decoded, path_status_available), +2488 x => panic!("incorrect frame {x:?}"), +2489 } +2490 } +2491 +2492 #[test] +2493 fn test_path_status_backup_roundtrip() { +2494 let path_status_backup = PathStatusBackup { +2495 path_id: PathId(42), +2496 status_seq_no: VarInt(73), +2497 }; +2498 let mut buf = Vec::new(); +2499 path_status_backup.encode(&mut buf); +2500 +2501 let mut decoded = frames(buf); +2502 assert_eq!(decoded.len(), 1); +2503 match decoded.pop().expect("non empty") { +2504 Frame::PathStatusBackup(decoded) => assert_eq!(decoded, path_status_backup), +2505 x => panic!("incorrect frame {x:?}"), +2506 } +2507 } +2508 +2509 #[test] +2510 fn test_path_new_connection_id_roundtrip() { +2511 let cid = NewConnectionId { +2512 path_id: Some(PathId(22)), +2513 sequence: 31, +2514 retire_prior_to: 13, +2515 id: ConnectionId::new(&[0xAB; 8]), +2516 reset_token: ResetToken::from([0xCD; crate::RESET_TOKEN_SIZE]), +2517 }; +2518 let mut buf = Vec::new(); +2519 cid.encode(&mut buf); +2520 +2521 let mut decoded = frames(buf); +2522 assert_eq!(decoded.len(), 1); +2523 match decoded.pop().expect("non empty") { +2524 Frame::NewConnectionId(decoded) => assert_eq!(decoded, cid), +2525 x => panic!("incorrect frame {x:?}"), +2526 } +2527 } +2528 +2529 #[test] +2530 fn test_path_retire_connection_id_roundtrip() { +2531 let retire_cid = RetireConnectionId { +2532 path_id: Some(PathId(22)), +2533 sequence: 31, +2534 }; +2535 let mut buf = Vec::new(); +2536 retire_cid.encode(&mut buf); +2537 +2538 let mut decoded = frames(buf); +2539 assert_eq!(decoded.len(), 1); +2540 match decoded.pop().expect("non empty") { +2541 Frame::RetireConnectionId(decoded) => assert_eq!(decoded, retire_cid), +2542 x => panic!("incorrect frame {x:?}"), +2543 } +2544 } +2545 +2546 #[test] +2547 fn test_paths_blocked_path_cids_blocked_roundtrip() { +2548 let mut buf = Vec::new(); +2549 +2550 let frame0 = PathsBlocked(PathId(22)); +2551 frame0.encode(&mut buf); +2552 let frame1 = PathCidsBlocked { +2553 path_id: PathId(23), +2554 next_seq: VarInt(32), +2555 }; +2556 frame1.encode(&mut buf); +2557 +2558 let mut decoded = frames(buf); +2559 assert_eq!(decoded.len(), 2); +2560 match decoded.pop().expect("non empty") { +2561 Frame::PathCidsBlocked(decoded) => assert_eq!(decoded, frame1), +2562 x => panic!("incorrect frame {x:?}"), +2563 } +2564 match decoded.pop().expect("non empty") { +2565 Frame::PathsBlocked(decoded) => assert_eq!(decoded, frame0), +2566 x => panic!("incorrect frame {x:?}"), +2567 } +2568 } +2569 +2570 /// Test that encoding and decoding [`AddAddress`] produces the same result +2571 #[test] +2572 fn test_add_address_roundrip() { +2573 let add_address = AddAddress { +2574 seq_no: VarInt(42), +2575 ip: std::net::Ipv4Addr::LOCALHOST.into(), +2576 port: 4242, +2577 }; +2578 let mut buf = Vec::with_capacity(add_address.size()); +2579 add_address.encode(&mut buf); +2580 +2581 assert_eq!( +2582 add_address.size(), +2583 buf.len(), +2584 "expected written bytes and actual size differ" +2585 ); +2586 +2587 let mut decoded = frames(buf); +2588 assert_eq!(decoded.len(), 1); +2589 match decoded.pop().expect("non empty") { +2590 Frame::AddAddress(decoded) => assert_eq!(decoded, add_address), +2591 x => panic!("incorrect frame {x:?}"), +2592 } +2593 } +2594 +2595 /// Test that encoding and decoding [`AddAddress`] produces the same result +2596 #[test] +2597 fn test_reach_out_roundrip() { +2598 let reach_out = ReachOut { +2599 round: VarInt(42), +2600 ip: std::net::Ipv6Addr::LOCALHOST.into(), +2601 port: 4242, +2602 }; +2603 let mut buf = Vec::with_capacity(reach_out.size()); +2604 reach_out.encode(&mut buf); +2605 +2606 assert_eq!( +2607 reach_out.size(), +2608 buf.len(), +2609 "expected written bytes and actual size differ" +2610 ); +2611 +2612 let mut decoded = frames(buf); +2613 assert_eq!(decoded.len(), 1); +2614 match decoded.pop().expect("non empty") { +2615 Frame::ReachOut(decoded) => assert_eq!(decoded, reach_out), +2616 x => panic!("incorrect frame {x:?}"), +2617 } +2618 } +2619 +2620 /// Test that encoding and decoding [`RemoveAddress`] produces the same result +2621 #[test] +2622 fn test_remove_address_roundrip() { +2623 let remove_addr = RemoveAddress::new(VarInt(10)); +2624 let mut buf = Vec::with_capacity(remove_addr.size()); +2625 remove_addr.encode(&mut buf); +2626 +2627 assert_eq!( +2628 remove_addr.size(), +2629 buf.len(), +2630 "expected written bytes and actual size differ" +2631 ); +2632 +2633 let mut decoded = frames(buf); +2634 assert_eq!(decoded.len(), 1); +2635 match decoded.pop().expect("non empty") { +2636 Frame::RemoveAddress(decoded) => assert_eq!(decoded, remove_addr), +2637 x => panic!("incorrect frame {x:?}"), +2638 } +2639 } +2640} \ No newline at end of file diff --git a/pr/323/docs/src/iroh_quinn_proto/packet.rs.html b/pr/323/docs/src/iroh_quinn_proto/packet.rs.html index e34dde3aa..ae16c6e2e 100644 --- a/pr/323/docs/src/iroh_quinn_proto/packet.rs.html +++ b/pr/323/docs/src/iroh_quinn_proto/packet.rs.html @@ -527,527 +527,536 @@ 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, +530 +531 #[cfg(test)] +532 pub(crate) fn dummy() -> Self { +533 Self { +534 start: 0, +535 header_len: 2, +536 pn: Some((1, true)), +537 } +538 } +539} +540 +541/// Plain packet header +542#[derive(Clone, Debug)] +543pub enum ProtectedHeader { +544 /// An Initial packet header +545 Initial(ProtectedInitialHeader), +546 /// A Long packet header, as used during the handshake +547 Long { +548 /// Type of the Long header packet +549 ty: LongType, +550 /// Destination Connection ID +551 dst_cid: ConnectionId, +552 /// Source Connection ID +553 src_cid: ConnectionId, +554 /// Length of the packet payload +555 len: u64, 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, +559 /// A Retry packet header +560 Retry { +561 /// Destination Connection ID +562 dst_cid: ConnectionId, +563 /// Source Connection ID +564 src_cid: ConnectionId, +565 /// QUIC version +566 version: u32, +567 }, +568 /// A short packet header, as used during the data phase +569 Short { +570 /// Spin bit +571 spin: bool, +572 /// Destination Connection ID +573 dst_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::*; +575 /// A Version Negotiation packet header +576 VersionNegotiate { +577 /// Random value +578 random: u8, +579 /// Destination Connection ID +580 dst_cid: ConnectionId, +581 /// Source Connection ID +582 src_cid: ConnectionId, +583 }, +584} +585 +586impl ProtectedHeader { +587 fn as_initial(&self) -> Option<&ProtectedInitialHeader> { 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); +589 Self::Initial(x) => Some(x), +590 _ => None, +591 } +592 } +593 +594 /// The destination Connection ID of the packet +595 pub fn dst_cid(&self) -> ConnectionId { +596 use ProtectedHeader::*; +597 match self { +598 Initial(header) => header.dst_cid, +599 &Long { dst_cid, .. } => dst_cid, +600 &Retry { dst_cid, .. } => dst_cid, +601 &Short { dst_cid, .. } => dst_cid, +602 &VersionNegotiate { dst_cid, .. } => dst_cid, +603 } +604 } +605 +606 fn payload_len(&self) -> Option<u64> { +607 use ProtectedHeader::*; +608 match self { +609 Initial(ProtectedInitialHeader { len, .. }) | Long { len, .. } => Some(*len), +610 _ => None, +611 } +612 } +613 +614 /// Decode a plain header from given buffer, with given [`ConnectionIdParser`]. +615 pub fn decode( +616 buf: &mut io::Cursor<BytesMut>, +617 cid_parser: &(impl ConnectionIdParser + ?Sized), +618 supported_versions: &[u32], +619 grease_quic_bit: bool, +620 ) -> Result<Self, PacketDecodeError> { +621 let first = buf.get::<u8>()?; +622 if !grease_quic_bit && first & FIXED_BIT == 0 { +623 return Err(PacketDecodeError::InvalidHeader("fixed bit unset")); +624 } +625 if first & LONG_HEADER_FORM == 0 { +626 let spin = first & SPIN_BIT != 0; +627 +628 Ok(Self::Short { +629 spin, +630 dst_cid: cid_parser.parse(buf)?, +631 }) +632 } else { +633 let version = buf.get::<u32>()?; +634 +635 let dst_cid = ConnectionId::decode_long(buf) +636 .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?; +637 let src_cid = ConnectionId::decode_long(buf) +638 .ok_or(PacketDecodeError::InvalidHeader("malformed cid"))?; +639 +640 // TODO: Support long CIDs for compatibility with future QUIC versions +641 if version == 0 { +642 let random = first & !LONG_HEADER_FORM; +643 return Ok(Self::VersionNegotiate { +644 random, +645 dst_cid, +646 src_cid, +647 }); +648 } +649 +650 if !supported_versions.contains(&version) { +651 return Err(PacketDecodeError::UnsupportedVersion { +652 src_cid, +653 dst_cid, +654 version, +655 }); +656 } 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, +658 match LongHeaderType::from_byte(first)? { +659 LongHeaderType::Initial => { +660 let token_len = buf.get_var()? as usize; +661 let token_start = buf.position() as usize; +662 if token_len > buf.remaining() { +663 return Err(PacketDecodeError::InvalidHeader("token out of bounds")); +664 } +665 buf.advance(token_len); +666 +667 let len = buf.get_var()?; +668 Ok(Self::Initial(ProtectedInitialHeader { +669 dst_cid, +670 src_cid, +671 token_pos: token_start..token_start + token_len, +672 len, +673 version, +674 })) +675 } +676 LongHeaderType::Retry => Ok(Self::Retry { +677 dst_cid, +678 src_cid, +679 version, +680 }), +681 LongHeaderType::Standard(ty) => Ok(Self::Long { +682 ty, +683 dst_cid, +684 src_cid, +685 len: buf.get_var()?, +686 version, +687 }), +688 } +689 } +690 } +691} +692 +693/// Header of an Initial packet, before decryption +694#[derive(Clone, Debug)] +695pub struct ProtectedInitialHeader { +696 /// Destination Connection ID +697 pub dst_cid: ConnectionId, +698 /// Source Connection ID +699 pub src_cid: ConnectionId, +700 /// The position of a token in the packet buffer +701 pub token_pos: Range<usize>, +702 /// Length of the packet payload +703 pub len: u64, +704 /// QUIC version +705 pub 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), +708#[derive(Clone, Debug)] +709pub(crate) struct InitialHeader { +710 pub(crate) dst_cid: ConnectionId, +711 pub(crate) src_cid: ConnectionId, +712 pub(crate) token: Bytes, +713 pub(crate) number: PacketNumber, +714 pub(crate) version: 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, +717// An encoded packet number +718#[derive(Debug, Copy, Clone, Eq, PartialEq)] +719pub(crate) enum PacketNumber { +720 U8(u8), +721 U16(u16), +722 U24(u32), +723 U32(u32), +724} +725 +726impl PacketNumber { +727 pub(crate) fn new(n: u64, largest_acked: u64) -> Self { +728 let range = (n - largest_acked) * 2; +729 if range < 1 << 8 { +730 Self::U8(n as u8) +731 } else if range < 1 << 16 { +732 Self::U16(n as u16) +733 } else if range < 1 << 24 { +734 Self::U24(n as u32) +735 } else if range < 1 << 32 { +736 Self::U32(n as u32) +737 } else { +738 panic!("packet number too large to encode") +739 } +740 } +741 +742 pub(crate) fn len(self) -> usize { +743 use PacketNumber::*; +744 match self { +745 U8(_) => 1, +746 U16(_) => 2, +747 U24(_) => 3, +748 U32(_) => 4, +749 } +750 } +751 +752 pub(crate) fn encode<W: BufMut>(self, w: &mut W) { +753 use PacketNumber::*; +754 match self { +755 U8(x) => w.write(x), +756 U16(x) => w.write(x), +757 U24(x) => w.put_uint(u64::from(x), 3), +758 U32(x) => w.write(x), +759 } +760 } +761 +762 pub(crate) fn decode<R: Buf>(len: usize, r: &mut R) -> Result<Self, PacketDecodeError> { +763 use PacketNumber::*; +764 let pn = match len { +765 1 => U8(r.get()?), +766 2 => U16(r.get()?), +767 3 => U24(r.get_uint(3) as u32), +768 4 => U32(r.get()?), +769 _ => unreachable!(), +770 }; +771 Ok(pn) +772 } +773 +774 pub(crate) fn decode_len(tag: u8) -> usize { +775 1 + (tag & 0x03) as usize +776 } +777 +778 fn tag(self) -> u8 { +779 use PacketNumber::*; +780 match self { +781 U8(_) => 0b00, +782 U16(_) => 0b01, +783 U24(_) => 0b10, +784 U32(_) => 0b11, +785 } +786 } +787 +788 pub(crate) fn expand(self, expected: u64) -> u64 { +789 // From Appendix A +790 use PacketNumber::*; +791 let truncated = match self { +792 U8(x) => u64::from(x), +793 U16(x) => u64::from(x), +794 U24(x) => u64::from(x), +795 U32(x) => u64::from(x), +796 }; +797 let nbits = self.len() * 8; +798 let win = 1 << nbits; +799 let hwin = win / 2; +800 let mask = win - 1; +801 // The incoming packet number should be greater than expected - hwin and less than or equal +802 // to expected + hwin +803 // +804 // This means we can't just strip the trailing bits from expected and add the truncated +805 // because that might yield a value outside the window. +806 // +807 // The following code calculates a candidate value and makes sure it's within the packet +808 // number window. +809 let candidate = (expected & !mask) | truncated; +810 if expected.checked_sub(hwin).is_some_and(|x| candidate <= x) { +811 candidate + win +812 } else if candidate > expected + hwin && candidate > win { +813 candidate - win +814 } else { +815 candidate +816 } +817 } +818} +819 +820/// A [`ConnectionIdParser`] implementation that assumes the connection ID is of fixed length +821pub struct FixedLengthConnectionIdParser { +822 expected_len: usize, +823} +824 +825impl FixedLengthConnectionIdParser { +826 /// Create a new instance of `FixedLengthConnectionIdParser` +827 pub fn new(expected_len: usize) -> Self { +828 Self { expected_len } +829 } +830} +831 +832impl ConnectionIdParser for FixedLengthConnectionIdParser { +833 fn parse(&self, buffer: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError> { +834 (buffer.remaining() >= self.expected_len) +835 .then(|| ConnectionId::from_buf(buffer, self.expected_len)) +836 .ok_or(PacketDecodeError::InvalidHeader("packet too small")) +837 } +838} +839 +840/// Parse connection id in short header packet +841pub trait ConnectionIdParser { +842 /// Parse a connection id from given buffer +843 fn parse(&self, buf: &mut dyn Buf) -> Result<ConnectionId, PacketDecodeError>; +844} +845 +846/// Long packet type including non-uniform cases +847#[derive(Clone, Copy, Debug, Eq, PartialEq)] +848pub(crate) enum LongHeaderType { +849 Initial, +850 Retry, +851 Standard(LongType), +852} +853 +854impl LongHeaderType { +855 fn from_byte(b: u8) -> Result<Self, PacketDecodeError> { +856 use {LongHeaderType::*, LongType::*}; +857 debug_assert!(b & LONG_HEADER_FORM != 0, "not a long packet"); +858 Ok(match (b & 0x30) >> 4 { +859 0x0 => Initial, +860 0x1 => Standard(ZeroRtt), +861 0x2 => Standard(Handshake), +862 0x3 => Retry, +863 _ => unreachable!(), +864 }) +865 } +866} +867 +868impl From<LongHeaderType> for u8 { +869 fn from(ty: LongHeaderType) -> Self { +870 use {LongHeaderType::*, LongType::*}; +871 match ty { +872 Initial => LONG_HEADER_FORM | FIXED_BIT, +873 Standard(ZeroRtt) => LONG_HEADER_FORM | FIXED_BIT | (0x1 << 4), +874 Standard(Handshake) => LONG_HEADER_FORM | FIXED_BIT | (0x2 << 4), +875 Retry => LONG_HEADER_FORM | FIXED_BIT | (0x3 << 4), +876 } +877 } 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(crate) 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(crate) 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(crate) 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")); +880/// Long packet types with uniform header structure +881#[derive(Clone, Copy, Debug, Eq, PartialEq)] +882pub enum LongType { +883 /// Handshake packet +884 Handshake, +885 /// 0-RTT packet +886 ZeroRtt, +887} +888 +889/// Packet decode error +890#[derive(Debug, Error, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +891pub enum PacketDecodeError { +892 /// Packet uses a QUIC version that is not supported +893 #[error("unsupported version {version:x}")] +894 UnsupportedVersion { +895 /// Source Connection ID +896 src_cid: ConnectionId, +897 /// Destination Connection ID +898 dst_cid: ConnectionId, +899 /// The version that was unsupported +900 version: u32, +901 }, +902 /// The packet header is invalid +903 #[error("invalid header: {0}")] +904 InvalidHeader(&'static str), +905} +906 +907impl From<coding::UnexpectedEnd> for PacketDecodeError { +908 fn from(_: coding::UnexpectedEnd) -> Self { +909 Self::InvalidHeader("unexpected end of packet") +910 } +911} +912 +913pub(crate) const LONG_HEADER_FORM: u8 = 0x80; +914pub(crate) const FIXED_BIT: u8 = 0x40; +915pub(crate) const SPIN_BIT: u8 = 0x20; +916const SHORT_RESERVED_BITS: u8 = 0x18; +917const LONG_RESERVED_BITS: u8 = 0x0c; +918const KEY_PHASE_BIT: u8 = 0x04; +919 +920/// Packet number space identifiers +921#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +922pub(crate) enum SpaceId { +923 /// Unprotected packets, used to bootstrap the handshake +924 Initial = 0, +925 Handshake = 1, +926 /// Application data space, used for 0-RTT and post-handshake/1-RTT packets +927 Data = 2, +928} +929 +930impl SpaceId { +931 pub(crate) fn iter() -> impl Iterator<Item = Self> { +932 [Self::Initial, Self::Handshake, Self::Data].iter().cloned() +933 } +934 +935 /// Returns the next higher packet space. +936 /// +937 /// Keeps returning [`SpaceId::Data`] as the highest space. +938 pub(crate) fn next(&self) -> Self { +939 match self { +940 Self::Initial => Self::Handshake, +941 Self::Handshake => Self::Data, +942 Self::Data => Self::Data, +943 } +944 } +945} +946 +947#[cfg(test)] +948mod tests { +949 use super::*; +950 use hex_literal::hex; +951 use std::io; +952 +953 fn check_pn(typed: PacketNumber, encoded: &[u8]) { +954 let mut buf = Vec::new(); +955 typed.encode(&mut buf); +956 assert_eq!(&buf[..], encoded); +957 let decoded = PacketNumber::decode(typed.len(), &mut io::Cursor::new(&buf)).unwrap(); +958 assert_eq!(typed, decoded); 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 } +962 fn roundtrip_packet_numbers() { +963 check_pn(PacketNumber::U8(0x7f), &hex!("7f")); +964 check_pn(PacketNumber::U16(0x80), &hex!("0080")); +965 check_pn(PacketNumber::U16(0x3fff), &hex!("3fff")); +966 check_pn(PacketNumber::U32(0x0000_4000), &hex!("0000 4000")); +967 check_pn(PacketNumber::U32(0xffff_ffff), &hex!("ffff ffff")); +968 } +969 +970 #[test] +971 fn pn_encode() { +972 check_pn(PacketNumber::new(0x10, 0), &hex!("10")); +973 check_pn(PacketNumber::new(0x100, 0), &hex!("0100")); +974 check_pn(PacketNumber::new(0x10000, 0), &hex!("010000")); 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 +977 #[test] +978 fn pn_expand_roundtrip() { +979 for expected in 0..1024 { +980 for actual in expected..1024 { +981 assert_eq!(actual, PacketNumber::new(actual, expected).expand(expected)); +982 } +983 } +984 } +985 +986 #[cfg(any(feature = "rustls-aws-lc-rs", feature = "rustls-ring"))] +987 #[test] +988 fn header_encoding() { +989 use crate::Side; +990 use crate::crypto::rustls::{initial_keys, initial_suite_from_provider}; +991 #[cfg(all(feature = "rustls-aws-lc-rs", not(feature = "rustls-ring")))] +992 use rustls::crypto::aws_lc_rs::default_provider; +993 #[cfg(feature = "rustls-ring")] +994 use rustls::crypto::ring::default_provider; +995 use rustls::quic::Version; +996 +997 let dcid = ConnectionId::new(&hex!("06b858ec6f80452b")); +998 let provider = default_provider(); +999 +1000 let suite = initial_suite_from_provider(&std::sync::Arc::new(provider)).unwrap(); +1001 let client = initial_keys(Version::V1, dcid, Side::Client, &suite); +1002 let mut buf = Vec::new(); +1003 let header = Header::Initial(InitialHeader { +1004 number: PacketNumber::U8(0), +1005 src_cid: ConnectionId::new(&[]), +1006 dst_cid: dcid, +1007 token: Bytes::new(), +1008 version: crate::DEFAULT_SUPPORTED_VERSIONS[0], +1009 }); +1010 let encode = header.encode(&mut buf); +1011 let header_len = buf.len(); +1012 buf.resize(header_len + 16 + client.packet.local.tag_len(), 0); +1013 encode.finish( +1014 &mut buf, +1015 &*client.header.local, +1016 Some((0, PathId::ZERO, &*client.packet.local)), +1017 ); +1018 +1019 for byte in &buf { +1020 print!("{byte:02x}"); +1021 } +1022 println!(); +1023 assert_eq!( +1024 buf[..], +1025 hex!( +1026 "c8000000010806b858ec6f80452b00004021be +1027 3ef50807b84191a196f760a6dad1e9d1c430c48952cba0148250c21c0a6a70e1" +1028 )[..] +1029 ); +1030 +1031 let server = initial_keys(Version::V1, dcid, Side::Server, &suite); +1032 let supported_versions = crate::DEFAULT_SUPPORTED_VERSIONS.to_vec(); +1033 let decode = PartialDecode::new( +1034 buf.as_slice().into(), +1035 &FixedLengthConnectionIdParser::new(0), +1036 &supported_versions, +1037 false, +1038 ) +1039 .unwrap() +1040 .0; +1041 let mut packet = decode.finish(Some(&*server.header.remote)).unwrap(); +1042 assert_eq!( +1043 packet.header_data[..], +1044 hex!("c0000000010806b858ec6f80452b0000402100")[..] +1045 ); +1046 server +1047 .packet +1048 .remote +1049 .decrypt(PathId::ZERO, 0, &packet.header_data, &mut packet.payload) +1050 .unwrap(); +1051 assert_eq!(packet.payload[..], [0; 16]); +1052 match packet.header { +1053 Header::Initial(InitialHeader { +1054 number: PacketNumber::U8(0), +1055 .. +1056 }) => {} +1057 _ => { +1058 panic!("unexpected header {:?}", packet.header); +1059 } +1060 } +1061 } +1062} \ No newline at end of file diff --git a/pr/323/docs/trait.impl/core/fmt/trait.Display.js b/pr/323/docs/trait.impl/core/fmt/trait.Display.js index 93a331159..2beb612ac 100644 --- a/pr/323/docs/trait.impl/core/fmt/trait.Display.js +++ b/pr/323/docs/trait.impl/core/fmt/trait.Display.js @@ -1,9 +1,9 @@ (function() { - var implementors = Object.fromEntries([["iroh_quinn",[["impl Display for ReadError"],["impl Display for ReadExactError"],["impl Display for ReadToEndError"],["impl Display for ResetError"],["impl Display for SendDatagramError"],["impl Display for StoppedError"],["impl Display for WriteError"],["impl Display for RetryError"]]],["iroh_quinn_proto",[["impl Display for ClosePathError"],["impl Display for ConfigError"],["impl Display for ConnectError"],["impl Display for ConnectionError"],["impl Display for Dir"],["impl Display for FinishError"],["impl Display for FrameType"],["impl Display for MaybeFrame"],["impl Display for PacketDecodeError"],["impl Display for PathError"],["impl Display for ReadError"],["impl Display for ReadableError"],["impl Display for SendDatagramError"],["impl Display for SetPathStatusError"],["impl Display for WriteError"],["impl Display for Error"],["impl Display for Error"],["impl Display for UnexpectedEnd"],["impl Display for NoInitialCipherSuite"],["impl Display for ApplicationClose"],["impl Display for ClosedPath"],["impl Display for ClosedStream"],["impl Display for ConnectionClose"],["impl Display for ConnectionId"],["impl Display for DatagramInfo"],["impl Display for FourTuple"],["impl Display for MultipathNotNegotiated"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["impl Display for StreamInfo"],["impl Display for Error"],["impl Display for Code"],["impl Display for VarInt"],["impl Display for VarIntBoundsExceeded"]]]]); + var implementors = Object.fromEntries([["iroh_quinn",[["impl Display for ReadError"],["impl Display for ReadExactError"],["impl Display for ReadToEndError"],["impl Display for ResetError"],["impl Display for SendDatagramError"],["impl Display for StoppedError"],["impl Display for WriteError"],["impl Display for RetryError"]]],["iroh_quinn_proto",[["impl Display for ClosePathError"],["impl Display for ConfigError"],["impl Display for ConnectError"],["impl Display for ConnectionError"],["impl Display for Dir"],["impl Display for FinishError"],["impl Display for FrameType"],["impl Display for MaybeFrame"],["impl Display for PacketDecodeError"],["impl Display for PathError"],["impl Display for ReadError"],["impl Display for ReadableError"],["impl Display for SendDatagramError"],["impl Display for SetPathStatusError"],["impl Display for WriteError"],["impl Display for Error"],["impl Display for Error"],["impl Display for UnexpectedEnd"],["impl Display for NoInitialCipherSuite"],["impl Display for ApplicationClose"],["impl Display for ClosedPath"],["impl Display for ClosedStream"],["impl Display for ConnectionClose"],["impl Display for ConnectionId"],["impl Display for Datagram"],["impl Display for DatagramInfo"],["impl Display for FourTuple"],["impl Display for MultipathNotNegotiated"],["impl Display for PathId"],["impl Display for RetryError"],["impl Display for StreamId"],["impl Display for StreamInfo"],["impl Display for Error"],["impl Display for Code"],["impl Display for VarInt"],["impl Display for VarIntBoundsExceeded"]]]]); if (window.register_implementors) { window.register_implementors(implementors); } else { window.pending_implementors = implementors; } })() -//{"start":57,"fragment_lengths":[2166,10016]} \ No newline at end of file +//{"start":57,"fragment_lengths":[2166,10290]} \ No newline at end of file